blob: e7a57c8d2fc6a3a6d899a9e49eae51c2619db801 [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),
Mingming Yinae3530f2014-07-03 16:50:18 -070077 STRING_TO_ENUM(AUDIO_FORMAT_AMR_NB),
78 STRING_TO_ENUM(AUDIO_FORMAT_AMR_WB),
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -070079 STRING_TO_ENUM(AUDIO_FORMAT_AC3),
Mingming Yinae3530f2014-07-03 16:50:18 -070080 STRING_TO_ENUM(AUDIO_FORMAT_E_AC3),
81#ifdef FORMATS_ENABLED
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -070082 STRING_TO_ENUM(AUDIO_FORMAT_DTS),
83 STRING_TO_ENUM(AUDIO_FORMAT_DTS_LBR),
84 STRING_TO_ENUM(AUDIO_FORMAT_WMA),
85 STRING_TO_ENUM(AUDIO_FORMAT_WMA_PRO),
86 STRING_TO_ENUM(AUDIO_FORMAT_AAC_ADIF),
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -070087 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));
Haynes Mathew Georgeb51ceb12014-06-30 13:56:18 -0700144 if (sf_info == NULL)
145 break; /* return whatever was parsed */
146
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700147 sf_info->format = format;
148 list_add_tail(&so_info->format_list, &sf_info->list);
149 }
150 str = strtok(NULL, "|");
151 }
152}
153
154static int parse_sample_rate_names(char *name)
155{
156 int sample_rate = 48000;
157 char *str = strtok(name, "|");
158
159 if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
160 sample_rate = (int)strtol(str, (char **)NULL, 10);
161
162 ALOGV("%s: sample_rate - %d", __func__, sample_rate);
163 return sample_rate;
164}
165
166static int parse_bit_width_names(char *name)
167{
168 int bit_width = 16;
169 char *str = strtok(name, "|");
170
171 if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
172 bit_width = (int)strtol(str, (char **)NULL, 10);
173
174 ALOGV("%s: bit_width - %d", __func__, bit_width);
175 return bit_width;
176}
177
178static int parse_app_type_names(void *platform, char *name)
179{
Subhash Chandra Bose Naripeddy54274672014-03-10 14:51:02 -0700180 int app_type = platform_get_default_app_type(platform);
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700181 char *str = strtok(name, "|");
182
183 if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG))
184 app_type = (int)strtol(str, (char **)NULL, 10);
185
186 ALOGV("%s: app_type - %d", __func__, app_type);
187 return app_type;
188}
189
190static void update_streams_output_cfg_list(cnode *root, void *platform,
191 struct listnode *streams_output_cfg_list)
192{
193 cnode *node = root->first_child;
194 struct streams_output_cfg *so_info;
195
196 ALOGV("%s", __func__);
197 so_info = (struct streams_output_cfg *)calloc(1, sizeof(struct streams_output_cfg));
Haynes Mathew Georgeb51ceb12014-06-30 13:56:18 -0700198
199 if (!so_info) {
200 ALOGE("failed to allocate mem for so_info list element");
201 return;
202 }
203
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700204 while (node) {
205 if (strcmp(node->name, FLAGS_TAG) == 0) {
206 so_info->flags = parse_flag_names((char *)node->value);
207 } else if (strcmp(node->name, FORMATS_TAG) == 0) {
208 parse_format_names((char *)node->value, so_info);
209 } else if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
210 so_info->app_type_cfg.sample_rate = parse_sample_rate_names((char *)node->value);
211 } else if (strcmp(node->name, BIT_WIDTH_TAG) == 0) {
212 so_info->app_type_cfg.bit_width = parse_bit_width_names((char *)node->value);
213 } else if (strcmp(node->name, APP_TYPE_TAG) == 0) {
214 so_info->app_type_cfg.app_type = parse_app_type_names(platform, (char *)node->value);
215 }
216 node = node->next;
217 }
218 list_add_tail(streams_output_cfg_list, &so_info->list);
219}
220
221static void load_output(cnode *root, void *platform,
222 struct listnode *streams_output_cfg_list)
223{
224 cnode *node = config_find(root, OUTPUTS_TAG);
225 if (node == NULL) {
226 ALOGE("%s: could not load output, node is NULL", __func__);
227 return;
228 }
229
230 node = node->first_child;
231 while (node) {
232 ALOGV("%s: loading output %s", __func__, node->name);
233 update_streams_output_cfg_list(node, platform, streams_output_cfg_list);
234 node = node->next;
235 }
236}
237
238static void send_app_type_cfg(void *platform, struct mixer *mixer,
239 struct listnode *streams_output_cfg_list)
240{
241 int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT] = {-1};
242 int length = 0, i, num_app_types = 0;
243 struct listnode *node;
244 bool update;
245 struct mixer_ctl *ctl = NULL;
246 const char *mixer_ctl_name = "App Type Config";
247 struct streams_output_cfg *so_info;
248
249 if (!mixer) {
250 ALOGE("%s: mixer is null",__func__);
251 return;
252 }
253 ctl = mixer_get_ctl_by_name(mixer, mixer_ctl_name);
254 if (!ctl) {
255 ALOGE("%s: Could not get ctl for mixer cmd - %s",__func__, mixer_ctl_name);
256 return;
257 }
258 if (streams_output_cfg_list == NULL) {
259 app_type_cfg[length++] = 1;
Subhash Chandra Bose Naripeddy54274672014-03-10 14:51:02 -0700260 app_type_cfg[length++] = platform_get_default_app_type(platform);
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700261 app_type_cfg[length++] = 48000;
262 app_type_cfg[length++] = 16;
263 mixer_ctl_set_array(ctl, app_type_cfg, length);
264 return;
265 }
266
267 app_type_cfg[length++] = num_app_types;
268 list_for_each(node, streams_output_cfg_list) {
269 so_info = node_to_item(node, struct streams_output_cfg, list);
270 update = true;
271 for (i=0; i<length; i=i+3) {
272 if (app_type_cfg[i+1] == -1)
273 break;
274 else if (app_type_cfg[i+1] == so_info->app_type_cfg.app_type) {
275 update = false;
276 break;
277 }
278 }
279 if (update && ((length + 3) <= MAX_LENGTH_MIXER_CONTROL_IN_INT)) {
280 num_app_types += 1 ;
281 app_type_cfg[length++] = so_info->app_type_cfg.app_type;
282 app_type_cfg[length++] = so_info->app_type_cfg.sample_rate;
283 app_type_cfg[length++] = so_info->app_type_cfg.bit_width;
284 }
285 }
286 ALOGV("%s: num_app_types: %d", __func__, num_app_types);
287 if (num_app_types) {
288 app_type_cfg[0] = num_app_types;
289 mixer_ctl_set_array(ctl, app_type_cfg, length);
290 }
291}
292
293void audio_extn_utils_update_streams_output_cfg_list(void *platform,
294 struct mixer *mixer,
295 struct listnode *streams_output_cfg_list)
296{
297 cnode *root;
298 char *data;
299
300 ALOGV("%s", __func__);
301 list_init(streams_output_cfg_list);
302 data = (char *)load_file(AUDIO_OUTPUT_POLICY_VENDOR_CONFIG_FILE, NULL);
303 if (data == NULL) {
304 send_app_type_cfg(platform, mixer, NULL);
305 ALOGE("%s: could not load output policy config file", __func__);
306 return;
307 }
308
309 root = config_node("", "");
Haynes Mathew Georgeb51ceb12014-06-30 13:56:18 -0700310 if (root == NULL) {
311 ALOGE("cfg_list, NULL config root");
312 return;
313 }
314
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700315 config_load(root, data);
316 load_output(root, platform, streams_output_cfg_list);
317
318 send_app_type_cfg(platform, mixer, streams_output_cfg_list);
319}
320
321void audio_extn_utils_dump_streams_output_cfg_list(
322 struct listnode *streams_output_cfg_list)
323{
324 int i=0;
325 struct listnode *node_i, *node_j;
326 struct streams_output_cfg *so_info;
327 struct stream_format *sf_info;
328 ALOGV("%s", __func__);
329 list_for_each(node_i, streams_output_cfg_list) {
330 so_info = node_to_item(node_i, struct streams_output_cfg, list);
331 ALOGV("%d: flags-%d, output_sample_rate-%d, output_bit_width-%d, app_type-%d",
332 i++, so_info->flags, so_info->app_type_cfg.sample_rate,
333 so_info->app_type_cfg.bit_width, so_info->app_type_cfg.app_type);
334 list_for_each(node_j, &so_info->format_list) {
335 sf_info = node_to_item(node_j, struct stream_format, list);
336 ALOGV("format-%x", sf_info->format);
337 }
338 }
339}
340
341void audio_extn_utils_release_streams_output_cfg_list(
342 struct listnode *streams_output_cfg_list)
343{
344 struct listnode *node_i, *node_j;
345 struct streams_output_cfg *so_info;
346 struct stream_format *sf_info;
347
348 ALOGV("%s", __func__);
349 while (!list_empty(streams_output_cfg_list)) {
350 node_i = list_head(streams_output_cfg_list);
351 so_info = node_to_item(node_i, struct streams_output_cfg, list);
352 while (!list_empty(&so_info->format_list)) {
353 node_j = list_head(&so_info->format_list);
354 list_remove(node_j);
355 free(node_to_item(node_j, struct stream_format, list));
356 }
357 list_remove(node_i);
358 free(node_to_item(node_i, struct streams_output_cfg, list));
359 }
360}
361
362void audio_extn_utils_update_stream_app_type_cfg(void *platform,
363 struct listnode *streams_output_cfg_list,
364 audio_output_flags_t flags,
365 audio_format_t format,
366 struct stream_app_type_cfg *app_type_cfg)
367{
368 struct listnode *node_i, *node_j;
369 struct streams_output_cfg *so_info;
370 struct stream_format *sf_info;
371
372 ALOGV("%s: flags: %x, format: %x", __func__, flags, format);
373 list_for_each(node_i, streams_output_cfg_list) {
374 so_info = node_to_item(node_i, struct streams_output_cfg, list);
375 if (so_info->flags == flags) {
376 list_for_each(node_j, &so_info->format_list) {
377 sf_info = node_to_item(node_j, struct stream_format, list);
378 if (sf_info->format == format) {
379 ALOGV("App type: %d", so_info->app_type_cfg.app_type);
380 app_type_cfg->app_type = so_info->app_type_cfg.app_type;
381 app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate;
382 app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
383 return;
384 }
385 }
386 }
387 }
388 list_for_each(node_i, streams_output_cfg_list) {
389 so_info = node_to_item(node_i, struct streams_output_cfg, list);
390 if (so_info->flags == AUDIO_OUTPUT_FLAG_PRIMARY) {
391 ALOGV("Compatible output profile not found.");
392 ALOGV("App type default to primary output: %d", so_info->app_type_cfg.app_type);
393 app_type_cfg->app_type = so_info->app_type_cfg.app_type;
394 app_type_cfg->sample_rate = so_info->app_type_cfg.sample_rate;
395 app_type_cfg->bit_width = so_info->app_type_cfg.bit_width;
396 return;
397 }
398 }
399 ALOGW("%s: App type could not be selected. Falling back to default", __func__);
Subhash Chandra Bose Naripeddy54274672014-03-10 14:51:02 -0700400 app_type_cfg->app_type = platform_get_default_app_type(platform);
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700401 app_type_cfg->sample_rate = 48000;
402 app_type_cfg->bit_width = 16;
403}
404
405int audio_extn_utils_send_app_type_cfg(struct audio_usecase *usecase)
406{
407 char mixer_ctl_name[MAX_LENGTH_MIXER_CONTROL_IN_INT];
408 int app_type_cfg[MAX_LENGTH_MIXER_CONTROL_IN_INT], len = 0, rc;
Anish Kumarc8599f22014-07-20 09:36:07 -0700409 struct stream_out *out;
410 struct audio_device *adev;
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700411 struct mixer_ctl *ctl;
412 int pcm_device_id, acdb_dev_id, snd_device = usecase->out_snd_device;
413
414 ALOGV("%s", __func__);
415
416 if (usecase->type != PCM_PLAYBACK) {
417 ALOGV("%s: not a playback path, no need to cfg app type", __func__);
418 rc = 0;
419 goto exit_send_app_type_cfg;
420 }
421 if ((usecase->id != USECASE_AUDIO_PLAYBACK_DEEP_BUFFER) &&
422 (usecase->id != USECASE_AUDIO_PLAYBACK_LOW_LATENCY) &&
423 (usecase->id != USECASE_AUDIO_PLAYBACK_MULTI_CH) &&
424 (usecase->id != USECASE_AUDIO_PLAYBACK_OFFLOAD)) {
425 ALOGV("%s: a playback path where app type cfg is not required", __func__);
426 rc = 0;
427 goto exit_send_app_type_cfg;
428 }
Anish Kumarc8599f22014-07-20 09:36:07 -0700429 out = usecase->stream.out;
430 adev = out->dev;
Subhash Chandra Bose Naripeddy19dc03b2014-03-10 14:43:05 -0700431
432 snd_device = usecase->out_snd_device;
433
434 pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
435
436 snprintf(mixer_ctl_name, sizeof(mixer_ctl_name),
437 "Audio Stream %d App Type Cfg", pcm_device_id);
438
439 ctl = mixer_get_ctl_by_name(adev->mixer, mixer_ctl_name);
440 if (!ctl) {
441 ALOGE("%s: Could not get ctl for mixer cmd - %s", __func__,
442 mixer_ctl_name);
443 rc = -EINVAL;
444 goto exit_send_app_type_cfg;
445 }
446 snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
447 audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
448 acdb_dev_id = platform_get_snd_device_acdb_id(snd_device);
449 if (acdb_dev_id < 0) {
450 ALOGE("%s: Couldn't get the acdb dev id", __func__);
451 rc = -EINVAL;
452 goto exit_send_app_type_cfg;
453 }
454 app_type_cfg[len++] = out->app_type_cfg.app_type;
455 app_type_cfg[len++] = acdb_dev_id;
456
457 mixer_ctl_set_array(ctl, app_type_cfg, len);
458
459 rc = 0;
460exit_send_app_type_cfg:
461 return rc;
462}
Subhash Chandra Bose Naripeddy54274672014-03-10 14:51:02 -0700463
464void audio_extn_utils_send_audio_calibration(struct audio_device *adev,
465 struct audio_usecase *usecase)
466{
467 int type = usecase->type;
468
469 if (type == PCM_PLAYBACK) {
470 struct stream_out *out = usecase->stream.out;
471 int snd_device = usecase->out_snd_device;
472 snd_device = (snd_device == SND_DEVICE_OUT_SPEAKER) ?
473 audio_extn_get_spkr_prot_snd_device(snd_device) : snd_device;
474 platform_send_audio_calibration(adev->platform, usecase->out_snd_device,
475 out->app_type_cfg.app_type,
476 out->app_type_cfg.sample_rate);
477 }
478 if ((type == PCM_HFP_CALL) || (type == PCM_CAPTURE)) {
479 /* when app type is default. the sample rate is not used to send cal */
480 platform_send_audio_calibration(adev->platform, usecase->in_snd_device,
481 platform_get_default_app_type(adev->platform),
482 48000);
483 }
484}
485