blob: db0230d73467d0c811bb33d3e39b6e62e6605785 [file] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Google Whitechapel AoC ALSA Driver on AoC Audio Control
*
* Copyright (c) 2019 Google LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "aoc_alsa.h"
#include "aoc_alsa_drv.h"
#include "aoc_alsa_path.h"
#include <linux/modem_notifier.h>
#ifndef ALSA_AOC_CMD_LOG_DISABLE
static int cmd_count;
#endif
#define DEFAULT_TELEPHONY_MIC PORT_INCALL_TX
#define AOC_CHIRP_BLOCK 9
extern struct be_path_cache port_array[PORT_MAX];
/*
* TODO: TDM/I2S will be removed from port naming and will be replaced
* by sink-associated devices such as spker, headphone, bt, usb, mode
*/
static aoc_audio_sink[] = {
[PORT_I2S_0_RX] = SINK_HEADPHONE, [PORT_I2S_0_TX] = -1,
[PORT_I2S_1_RX] = SINK_BT, [PORT_I2S_1_TX] = -1,
[PORT_I2S_2_RX] = SINK_USB, [PORT_I2S_2_TX] = -1,
[PORT_TDM_0_RX] = SINK_SPEAKER, [PORT_TDM_0_TX] = -1,
[PORT_TDM_1_RX] = SINK_MODEM, [PORT_TDM_1_TX] = -1,
[PORT_USB_RX] = SINK_USB, [PORT_USB_TX] = -1,
[PORT_BT_RX] = SINK_BT, [PORT_BT_TX] = -1,
[PORT_INCALL_RX] = -1, [PORT_INCALL_TX] = -1,
[PORT_INTERNAL_MIC] = -1, [PORT_HAPTIC_RX] = SINK_SPEAKER,
[PORT_INTERNAL_MIC_US] = -1,
};
static int hw_id_to_sink(int hw_idx)
{
if (hw_idx < 0 || hw_idx >= ARRAY_SIZE(aoc_audio_sink))
return -1;
return aoc_audio_sink[hw_idx];
}
static int ep_id_to_source(int ep_idx)
{
/* TODO: refactor needed. Haptics pcm dev id: 7, its entrypoint is 10(HAPTICS) */
switch (ep_idx) {
case IDX_EP8:
return HAPTICS;
case IDX_HIFI:
return USB_HIFI;
case IDX_US:
return SPEAKER_US;
case IDX_IMSV:
return IMMERSIVE;
default:
return ep_idx;
}
}
static bool ap_filter_capture_stream(struct aoc_alsa_stream *alsa_stream)
{
switch (alsa_stream->idx) {
case UC_AUDIO_RECORD:
case UC_MMAP_RECORD:
case UC_LOW_LATENCY_AUDIO_RECORD:
return true;
default:
return false;
}
}
static struct aoc_alsa_stream *find_alsa_stream_by_ep_id(struct aoc_chip *chip, int ep_id)
{
int i;
struct snd_soc_pcm_runtime *rtd;
for (i = 0; i < ARRAY_SIZE(chip->alsa_stream); i++) {
if (!chip->alsa_stream[i])
continue;
if (!chip->alsa_stream[i]->substream)
continue;
rtd = chip->alsa_stream[i]->substream->private_data;
if (rtd && rtd->dai_link->id == ep_id)
return chip->alsa_stream[i];
}
return NULL;
}
static struct aoc_alsa_stream *find_alsa_stream_by_device_idx(struct aoc_chip *chip, int idx)
{
int i;
for (i = 0; i < ARRAY_SIZE(chip->alsa_stream); i++) {
if (!chip->alsa_stream[i])
continue;
if (chip->alsa_stream[i]->idx == idx)
return chip->alsa_stream[i];
}
return NULL;
}
static struct aoc_alsa_stream *find_alsa_stream_by_capture_stream(struct aoc_chip *chip)
{
int i;
if (aoc_audio_capture_active_stream_num(chip) == 0)
return NULL;
for (i = 0; i < ARRAY_SIZE(chip->alsa_stream); i++) {
struct aoc_alsa_stream *alsa_stream = chip->alsa_stream[i];
if (!alsa_stream)
continue;
if (!alsa_stream->substream)
continue;
if (alsa_stream->substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return chip->alsa_stream[i];
}
return NULL;
}
static int hw_id_to_phone_mic_source(int hw_id)
{
int mic_input_source;
/* Use user-specified mic for voice call independently of the playback sink */
switch (hw_id) {
case PORT_USB_TX:
mic_input_source = MODEM_USB_INPUT_INDEX;
break;
case PORT_BT_TX:
mic_input_source = MODEM_BT_INPUT_INDEX;
break;
case PORT_INCALL_TX:
mic_input_source = MODEM_INCALL_INPUT_INDEX;
break;
default:
pr_err("ERR in mic input source for voice call, mic source=%d\n",
hw_id);
case PORT_INTERNAL_MIC:
mic_input_source = MODEM_MIC_INPUT_INDEX;
break;
}
return mic_input_source;
}
/* temp usage */
static aoc_audio_stream_type[] = {
[0] = MMAPED, [1] = NORMAL, [2] = NORMAL, [3] = NORMAL, [4] = NORMAL,
[5] = NORMAL, [6] = COMPRESS, [7] = NORMAL, [8] = NORMAL, [9] = MMAPED,
[10] = RAW, [11] = NORMAL, [12] = NORMAL, [13] = NORMAL, [14] = NORMAL,
[15] = NORMAL, [16] = NORMAL, [17] = NORMAL, [18] = INCALL, [19] = INCALL,
[20] = INCALL, [21] = INCALL, [22] = INCALL, [23] = MMAPED, [24] = NORMAL,
[25] = HIFI, [26] = HIFI, [27] = ANDROID_AEC, [28] = MMAPED, [29] = INCALL,
[30] = NORMAL, [31] = CAP_INJ, [32] = HOTWORD_TAP,
};
int aoc_pcm_device_to_stream_type(int device)
{
if (device < 0 || device >= ARRAY_SIZE(aoc_audio_stream_type))
return -1;
return aoc_audio_stream_type[device];
}
static bool aoc_pcm_is_mmap_raw(struct aoc_alsa_stream *alsa_stream)
{
return (alsa_stream->stream_type == MMAPED || alsa_stream->stream_type == RAW);
}
/*
* Sending commands to AoC for setting parameters and start/stop the streams
*/
static int aoc_audio_control(const char *cmd_channel, const uint8_t *cmd,
size_t cmd_size, uint8_t *response,
struct aoc_chip *chip)
{
struct aoc_service_dev *dev;
uint8_t *buffer;
int buffer_size = 1024;
struct timespec64 tv0, tv1;
int err, count;
unsigned long time_expired;
if (!cmd_channel || !cmd)
return -EINVAL;
if (mutex_lock_interruptible(&chip->audio_cmd_chan_mutex))
return -EINTR;
/* Get the aoc audio control channel at runtime */
err = alloc_aoc_audio_service(cmd_channel, &dev, NULL, NULL);
if (err < 0) {
mutex_unlock(&chip->audio_cmd_chan_mutex);
return err;
}
#ifndef ALSA_AOC_CMD_LOG_DISABLE
cmd_count++;
pr_notice_ratelimited(ALSA_AOC_CMD
" cmd [%s] id %#06x, size %zu, cntr %d\n",
CMD_CHANNEL(dev), ((struct CMD_HDR *)cmd)->id,
cmd_size, cmd_count);
#endif
buffer = kmalloc_array(buffer_size, sizeof(*buffer), GFP_ATOMIC);
if (!buffer) {
err = -ENOMEM;
pr_err("ERR: no memory!\n");
goto exit;
}
/*
* TODO: assuming only one user for the audio control channel.
* clear all the response messages from previous commands
*/
count = 0;
while (((err = aoc_service_read(dev, buffer, buffer_size,
NONBLOCKING)) >= 1)) {
count++;
}
if (count > 0)
pr_debug("%d messages read for previous commands\n", count);
/* Sending cmd to AoC */
if ((aoc_service_write(dev, cmd, cmd_size, NONBLOCKING)) != cmd_size) {
pr_err(ALSA_AOC_CMD " ERR: ring full - cmd id %#06x\n",
((struct CMD_HDR *)cmd)->id);
err = -EAGAIN;
goto exit;
}
#ifdef AOC_CMD_DEBUG_ENABLE
ktime_get_real_ts64(&tv0);
#endif /* AOC_CMD_DEBUG_ENABLE */
/* Getting responses from Aoc for the command just sent */
count = 0;
time_expired = jiffies + msecs_to_jiffies(WAITING_TIME_MS);
while (((err = aoc_service_read(dev, buffer, buffer_size,
NONBLOCKING)) < 1) &&
time_is_after_jiffies(time_expired)) {
count++;
}
#ifdef AOC_CMD_DEBUG_ENABLE
ktime_get_real_ts64(&tv1);
pr_debug("Elapsed: %lld (usecs)\n",
(tv1.tv_sec - tv0.tv_sec) * USEC_PER_SEC +
(tv1.tv_nsec - tv0.tv_nsec) / NSEC_PER_USEC);
if (count > 0)
pr_debug("%d times tried for response\n", count);
#endif /* AOC_CMD_DEBUG_ENABLE */
if (err < 1) {
uint16_t cmd_id = ((struct CMD_HDR *)cmd)->id;
char reset_reason[40];
scnprintf(reset_reason, sizeof(reset_reason), "ALSA command timeout %#06x",
cmd_id);
pr_err(ALSA_AOC_CMD " ERR:timeout - cmd [%s] id %#06x\n",
CMD_CHANNEL(dev), cmd_id);
print_hex_dump(KERN_ERR, ALSA_AOC_CMD " :mem ",
DUMP_PREFIX_OFFSET, 16, 1, cmd, cmd_size, false);
aoc_trigger_watchdog(reset_reason);
} else if (err == 4) {
pr_err(ALSA_AOC_CMD " ERR:%#x - cmd [%s] id %#06x\n",
*(uint32_t *)buffer, CMD_CHANNEL(dev),
((struct CMD_HDR *)cmd)->id);
print_hex_dump(KERN_ERR, ALSA_AOC_CMD " :mem ",
DUMP_PREFIX_OFFSET, 16, 1, cmd, cmd_size, false);
} else {
pr_debug(ALSA_AOC_CMD
" cmd [%s] id %#06x, reply mesg size %d\n",
CMD_CHANNEL(dev), ((struct CMD_HDR *)cmd)->id, err);
}
if (response != NULL)
memcpy(response, buffer, cmd_size);
exit:
kfree(buffer);
free_aoc_audio_service(cmd_channel, dev);
mutex_unlock(&chip->audio_cmd_chan_mutex);
return err < 1 ? -EAGAIN : 0;
}
static int aoc_audio_control_simple_cmd(const char *cmd_channel, int cmd_id, struct aoc_chip *chip)
{
int err = 0;
struct CMD_HDR cmd;
AocCmdHdrSet(&cmd, cmd_id, sizeof(cmd));
err = aoc_audio_control(cmd_channel, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in audio control simple cmd:%d sent\n", err, cmd_id);
return err;
}
return 0;
}
int aoc_audio_volume_set(struct aoc_chip *chip, uint32_t volume, int src,
int dst)
{
int err;
struct CMD_AUDIO_OUTPUT_SET_PARAMETER cmd;
/* No volume control for capturing */
/* Haptics in AoC does not have adjustable volume */
if (src == HAPTICS)
return 0;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_PARAMETER_ID,
sizeof(cmd));
/* Sink 0-4 with block ID from 16-20 */
cmd.block = dst + AOC_AUDIO_SINK_BLOCK_ID_BASE;
cmd.component = 0;
cmd.key = src;
cmd.val = volume;
pr_debug("volume changed to: %d\n", volume);
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), NULL, chip);
if (err < 0)
pr_err("ERR:%d in volume set\n", err);
return err;
}
static int aoc_audio_capture_mic_input(struct aoc_chip *chip,
int input_cmd, int mic_input_source)
{
int err;
struct CMD_AUDIO_INPUT_AP_INPUT_START cmd1;
if (input_cmd == START) {
AocCmdHdrSet(&(cmd1.parent), CMD_AUDIO_INPUT_AP_INPUT_START_ID,
sizeof(cmd1));
cmd1.mic_input_source = mic_input_source;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd1, sizeof(cmd1),
NULL, chip);
if (err < 0)
pr_err("ERR:%d audio capture mic input start fail!\n", err);
} else {
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL,
CMD_AUDIO_INPUT_AP_INPUT_STOP_ID, chip);
if (err < 0)
pr_err("ERR:%d audio capture mic input stop fail!\n", err);
}
return err;
}
static int aoc_audio_us_capture_mic_input(struct aoc_chip *chip, int input_cmd)
{
int err;
if (input_cmd == START) {
err = aoc_audio_control_simple_cmd(
CMD_INPUT_CHANNEL, CMD_AUDIO_INPUT_ULTRASONIC_CAPTURE_START_ID,
chip);
if (err < 0)
pr_err("ERR:%d audio us capture mic input start fail!\n", err);
} else {
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL,
CMD_AUDIO_INPUT_ULTRASONIC_CAPTURE_STOP_ID, chip);
if (err < 0)
pr_err("ERR:%d audio us capture mic input stop fail!\n", err);
}
return err;
}
int aoc_audio_capture_mic_prepare(struct aoc_chip *chip)
{
int err = 0;
int mic_input_source;
switch (chip->audio_capture_mic_source) {
case BUILTIN_MIC:
mic_input_source = AP_INPUT_PROCESSOR_MIC_INPUT_INDEX;
break;
case USB_MIC:
mic_input_source = AP_INPUT_PROCESSOR_USB_INPUT_INDEX;
break;
case BT_MIC:
mic_input_source = AP_INPUT_PROCESSOR_BT_INPUT_INDEX;
break;
case ERASER:
mic_input_source = AP_INPUT_PROCESSOR_ERASER_INPUT_INDEX;
break;
default:
pr_err("ERR in mic input source for audio capture mic source=%d\n",
chip->audio_capture_mic_source);
err = EINVAL;
goto exit;
}
/* Update mask before start capture */
if (mic_input_source == AP_INPUT_PROCESSOR_MIC_INPUT_INDEX)
aoc_audio_mic_mask_set(chip, false);
// CMD_AUDIO_INPUT_AP_INPUT_START_ID with mic_input_source
pr_info("mic_input_source = %d\n", mic_input_source);
err = aoc_audio_capture_mic_input(chip, START, mic_input_source);
if (err < 0)
pr_err("ERR:%d in audio capture mic input setup start\n", err);
exit:
return err;
}
int aoc_audio_capture_mic_close(struct aoc_chip *chip)
{
int err = 0;
err = aoc_audio_capture_mic_input(chip, STOP, 0);
if (err < 0) {
pr_err("ERR:%d in audio capture mic input stop\n", err);
return err;
}
return 0;
}
static int aoc_audio_us_capture_mic_prepare(struct aoc_chip *chip)
{
int err = 0;
err = aoc_audio_us_capture_mic_input(chip, START);
if (err < 0)
pr_err("ERR:%d in audio us capture mic input setup start\n", err);
return err;
}
static int aoc_audio_us_capture_mic_close(struct aoc_chip *chip)
{
int err = 0;
err = aoc_audio_us_capture_mic_input(chip, STOP);
if (err < 0) {
pr_err("ERR:%d in audio us capture mic input stop\n", err);
return err;
}
return 0;
}
int aoc_audio_capture_active_stream_num(struct aoc_chip *chip)
{
return (int)hweight64((uint64_t)(chip->opened & AOC_AUDIO_CAPUTRE_DEVICE_MASK));
}
static int aoc_record_param_configured_num(struct aoc_chip *chip)
{
return (int)hweight64((uint64_t)(chip->capture_param_set & AOC_AUDIO_CAPUTRE_DEVICE_MASK));
}
static int aoc_us_record_param_configured_num(struct aoc_chip *chip)
{
return (int)hweight64((uint64_t)(chip->capture_param_set & AOC_ULTRASONIC_CAPUTRE_DEVICE_MASK));
}
int aoc_set_builtin_mic_power_state(struct aoc_chip *chip, int iMic, int state)
{
int err;
struct CMD_AUDIO_INPUT_MIC_POWER_ON cmd_on;
struct CMD_AUDIO_INPUT_MIC_POWER_OFF cmd_off;
if (state == 1) {
AocCmdHdrSet(&(cmd_on.parent), CMD_AUDIO_INPUT_MIC_POWER_ON_ID,
sizeof(cmd_on));
cmd_on.mic_index = iMic;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd_on,
sizeof(cmd_on), NULL, chip);
} else {
AocCmdHdrSet(&(cmd_off.parent),
CMD_AUDIO_INPUT_MIC_POWER_OFF_ID, sizeof(cmd_off));
cmd_off.mic_index = iMic;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd_off,
sizeof(cmd_off), NULL, chip);
}
if (err < 0)
pr_err("ERR:%d in set mic state\n", err);
return err < 0 ? err : 0;
}
int aoc_get_builtin_mic_power_state(struct aoc_chip *chip, int iMic)
{
int err;
struct CMD_AUDIO_INPUT_MIC_GET_POWER_STATE cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_MIC_GET_POWER_STATE_ID,
sizeof(cmd));
cmd.mic_index = iMic;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
(uint8_t *)&cmd, chip);
if (err < 0)
pr_err("ERR:%d in get mic state\n", err);
return err < 0 ? err : cmd.power_state;
}
int aoc_mic_clock_rate_get(struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_INPUT_GET_MIC_CLOCK_FREQUENCY cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_GET_MIC_CLOCK_FREQUENCY_ID,
sizeof(cmd));
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
(uint8_t *)&cmd, chip);
if (err < 0) {
pr_err("ERR:%d in get mic clock frequency\n", err);
return err;
}
return cmd.mic_clock_frequency_hz;
}
int aoc_mic_hw_gain_get(struct aoc_chip *chip, int state)
{
int err;
/* TODO: the cmd structures for 3 states differ only in names */
struct CMD_AUDIO_INPUT_GET_MIC_CURRENT_HW_GAIN cmd;
int cmd_id;
switch (state) {
case MIC_LOW_POWER_GAIN:
cmd_id = CMD_AUDIO_INPUT_GET_MIC_LOW_POWER_HW_GAIN_ID;
break;
case MIC_HIGH_POWER_GAIN:
cmd_id = CMD_AUDIO_INPUT_GET_MIC_HIGH_POWER_HW_GAIN_ID;
break;
default:
cmd_id = CMD_AUDIO_INPUT_GET_MIC_CURRENT_HW_GAIN_ID;
}
AocCmdHdrSet(&(cmd.parent), cmd_id, sizeof(cmd));
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
(uint8_t *)&cmd, chip);
if (err < 0) {
pr_err("ERR:%d in get current mic hw gain\n", err);
return err;
}
return cmd.mic_hw_gain_cb;
}
int aoc_mic_hw_gain_set(struct aoc_chip *chip, int state, int gain)
{
int err;
/* TODO: the cmd structures for 3 states differ only in names */
struct CMD_AUDIO_INPUT_SET_MIC_LOW_POWER_HW_GAIN cmd;
int cmd_id;
switch (state) {
case MIC_LOW_POWER_GAIN:
cmd_id = CMD_AUDIO_INPUT_SET_MIC_LOW_POWER_HW_GAIN_ID;
break;
case MIC_HIGH_POWER_GAIN:
cmd_id = CMD_AUDIO_INPUT_SET_MIC_HIGH_POWER_HW_GAIN_ID;
break;
default:
cmd_id = CMD_AUDIO_INPUT_SET_MIC_LOW_POWER_HW_GAIN_ID;
}
AocCmdHdrSet(&(cmd.parent), cmd_id, sizeof(cmd));
cmd.mic_hw_gain_cb = gain;
pr_debug("power state =%d, gain = %d\n", state, cmd.mic_hw_gain_cb);
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
(uint8_t *)&cmd, chip);
if (err < 0) {
pr_err("ERR:%d in set mic hw gain\n", err);
return err;
}
return 0;
}
int aoc_mic_dc_blocker_get(struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_INPUT_GET_MIC_DC_BLOCKER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_GET_MIC_DC_BLOCKER_ID,
sizeof(cmd));
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
(uint8_t *)&cmd, chip);
if (err < 0) {
pr_err("ERR:%d in get mic dc blocker state\n", err);
return err;
}
return cmd.dc_blocker_enabled;
}
int aoc_mic_dc_blocker_set(struct aoc_chip *chip, int enable)
{
int err;
struct CMD_AUDIO_INPUT_SET_MIC_DC_BLOCKER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_SET_MIC_DC_BLOCKER_ID,
sizeof(cmd));
cmd.dc_blocker_enabled = enable;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
NULL, chip);
if (err < 0)
pr_err("ERR:%d in set mic dc blocker state as %d\n", err,
enable);
return err;
}
int aoc_sidetone_cfg_get(struct aoc_chip *chip, int param, long *val)
{
int err = 0;
struct CMD_AUDIO_OUTPUT_GET_SIDETONE cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_SIDETONE_ID, sizeof(cmd));
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in get mic dc blocker state\n", err);
return err;
}
pr_info("%s, sidetone- vol:%d, Eq num stage: %d, mic:%d\n", __func__, cmd.volume,
cmd.eq_num_stages, cmd.mic_select);
chip->sidetone_cfg = cmd;
if (val) {
switch (param) {
case SIDETONE_CFG_VOL:
*val = cmd.volume;
break;
case SIDETONE_CFG_STAGE_NUM:
*val = cmd.eq_num_stages;
break;
case SIDETONE_CFG_MIC_ID:
*val = cmd.mic_select;
break;
default:
err = -EINVAL;
pr_err("ERR:%d invalid param for sidetone cfg\n", err);
}
}
return err;
}
int aoc_sidetone_cfg_set(struct aoc_chip *chip, int param, long val)
{
int err = 0;
struct CMD_AUDIO_OUTPUT_SET_SIDETONE cmd;
aoc_sidetone_cfg_get(chip, 0, NULL); //get the updated data from AoC before setting
cmd.eq_num_stages = chip->sidetone_cfg.eq_num_stages;
cmd.mic_select = chip->sidetone_cfg.mic_select;
cmd.volume = chip->sidetone_cfg.volume;
switch (param) {
case SIDETONE_CFG_VOL:
cmd.volume = val;
break;
case SIDETONE_CFG_STAGE_NUM:
cmd.eq_num_stages = val;
break;
case SIDETONE_CFG_MIC_ID:
cmd.mic_select = val;
break;
default:
return -EINVAL;
}
pr_info("%s, side tone- vol:%d, Eq num stage: %d, mic:%d\n", __func__, cmd.volume,
cmd.eq_num_stages, cmd.mic_select);
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_SIDETONE_ID, sizeof(cmd));
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in set sidetone params\n", err);
return err;
}
return err;
}
int aoc_sidetone_eq_get(struct aoc_chip *chip, int biquad_idx, long *val)
{
int i, err = 0;
struct CMD_AUDIO_OUTPUT_GET_SIDETONE_EQ cmd;
int n_params = ARRAY_SIZE(cmd.coeffs);
pr_info("%s: sidetone eq %d - %d params\n", __func__, biquad_idx, n_params);
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_SIDETONE_EQ_ID, sizeof(cmd));
cmd.stage_num = biquad_idx;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in get sidetone eq param\n", err);
return err;
}
if (val) {
for (i = 0; i < n_params; i++)
val[i] = *(uint32_t *)&cmd.coeffs[i];
}
return 0;
}
int aoc_sidetone_eq_set(struct aoc_chip *chip, int biquad_idx, long *val)
{
int i, err = 0;
uint32_t tmp;
struct CMD_AUDIO_OUTPUT_SET_SIDETONE_EQ cmd;
int n_params = ARRAY_SIZE(cmd.coeffs);
pr_info("%s: sidetone eq %d - %d params\n", __func__, biquad_idx, n_params);
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_SIDETONE_EQ_ID, sizeof(cmd));
cmd.stage_num = biquad_idx;
for (i = 0; i < n_params; i++) {
tmp = (uint32_t)val[i];
cmd.coeffs[i] = *(float *)(&tmp);
}
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in set sidetone eq param\n", err);
return err;
}
return 0;
}
int aoc_incall_capture_enable_get(struct aoc_chip *chip, int stream, long *val)
{
int err;
struct CMD_AUDIO_OUTPUT_TELE_CAPT cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_TELE_CAPT_ID, sizeof(cmd));
cmd.ring = stream;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in get voice capture stream %d state\n", err, stream);
return err;
}
if (val)
*val = cmd.mode;
chip->incall_capture_state[stream] = cmd.mode;
pr_info("%s: get voice call capture stream %d state %d\n", __func__, stream, cmd.mode);
return 0;
}
int aoc_incall_capture_enable_set(struct aoc_chip *chip, int stream, long val)
{
int err;
struct CMD_AUDIO_OUTPUT_TELE_CAPT cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_TELE_CAPT_ID, sizeof(cmd));
cmd.ring = stream;
cmd.mode = val;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in set incall capture stream %d state as %d\n", err, stream,
cmd.mode);
return err;
}
chip->incall_capture_state[stream] = val;
pr_info("%s: set incall capture stream %d state as %d\n", __func__, stream, cmd.mode);
return 0;
}
int aoc_incall_playback_enable_get(struct aoc_chip *chip, int stream, long *val)
{
int err;
struct CMD_AUDIO_OUTPUT_INCALL_MUSIC cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_INCALL_MUSIC_ID, sizeof(cmd));
cmd.ring = stream;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in get voice playback stream %d state\n", err, stream);
return err;
}
if (val)
*val = cmd.enable;
pr_info("%s: get incall playback stream %d state %d\n", __func__, stream, cmd.enable);
return 0;
}
int aoc_incall_playback_enable_set(struct aoc_chip *chip, int stream, long val)
{
int err;
struct CMD_AUDIO_OUTPUT_INCALL_MUSIC cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_INCALL_MUSIC_ID, sizeof(cmd));
cmd.ring = stream;
cmd.enable = val;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in set incall playback stream %d state as %d\n", err, stream,
cmd.enable);
return err;
}
pr_info("%s: set incall playback stream %d state as %d\n", __func__, stream, cmd.enable);
return 0;
}
int aoc_incall_playback_mic_channel_get(struct aoc_chip *chip, int stream, long *val)
{
int err;
struct CMD_AUDIO_OUTPUT_INCALL_MUSIC_CHAN cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_INCALL_MUSIC_CHAN_ID, sizeof(cmd));
cmd.ring = stream;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in get incall music stream %d mic channel\n", err, stream);
return err;
}
if (val)
*val = cmd.channel;
pr_info("incall playback stream %d: mic channel get :%d\n", stream, cmd.channel);
return 0;
}
int aoc_incall_playback_mic_channel_set(struct aoc_chip *chip, int stream, long val)
{
int err;
struct CMD_AUDIO_OUTPUT_INCALL_MUSIC_CHAN cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_INCALL_MUSIC_CHAN_ID, sizeof(cmd));
cmd.ring = stream;
cmd.channel = val;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in incall playback stream %d: mic channel set as %d\n", err, stream,
cmd.channel);
return err;
}
pr_info("incall playback stream %d: mic channel :%d set\n", stream, cmd.channel);
return 0;
}
/* TODO: temporary solution for mic muting, has to be revised using DSP modules instead of mixer */
int aoc_voice_call_mic_mute(struct aoc_chip *chip, int mute)
{
int err;
int gain = (mute == 1) ? -700 : chip->default_mic_hw_gain;
pr_debug("voice call mic mute: %d\n", mute);
if ((err = aoc_mic_hw_gain_set(chip, MIC_HIGH_POWER_GAIN, gain))) {
pr_err("ERR: fail in muting mic in voice call\n");
return err;
}
return 0;
}
int aoc_get_builtin_mic_process_mode(struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_INPUT_GET_AP_MIC_INDEX cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_GET_AP_MIC_INDEX_ID,
sizeof(cmd));
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0)
pr_err("Error in get builtin_mic_process_mode!\n");
return err < 0 ? err : cmd.mic_process_index;
}
int aoc_set_builtin_mic_process_mode(struct aoc_chip *chip, long mode)
{
int err;
struct CMD_AUDIO_INPUT_GET_AP_MIC_INDEX cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_SET_AP_MIC_INDEX_ID, sizeof(cmd));
switch (mode) {
case 0:
cmd.mic_process_index = AP_MIC_PROCESS_RAW;
break;
case 1:
cmd.mic_process_index = AP_MIC_PROCESS_SPATIAL;
break;
default:
cmd.mic_process_index = AP_MIC_PROCESS_RAW;
}
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR: %d in set builtin_mic_process_mode!\n", err);
return err;
}
return 0;
}
int aoc_get_dsp_state(struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_OUTPUT_GET_DSP_STATE cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_DSP_STATE_ID,
sizeof(cmd));
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0)
pr_err("Error in get aoc dsp state !\n");
return err < 0 ? err : cmd.mode;
}
int aoc_get_asp_mode(struct aoc_chip *chip, int block, int component, int key)
{
int err;
struct CMD_AUDIO_OUTPUT_GET_PARAMETER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_PARAMETER_ID,
sizeof(cmd));
/* Sink 0-4 with block ID from 16-20 */
cmd.block = block;
cmd.component = component;
cmd.key = key;
pr_info("get asp mode: block=%d, component=%d, key=%d\n", block, component, key);
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0) {
pr_err("ERR:%d in getting dsp mode, block=%d, component=%d, key=%d\n",
err, block, component, key);
return err;
}
return cmd.val;
}
int aoc_set_asp_mode(struct aoc_chip *chip, int block, int component, int key,
int val)
{
int err;
struct CMD_AUDIO_OUTPUT_SET_PARAMETER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_PARAMETER_ID,
sizeof(cmd));
/* Sink 0-4 with block ID from 16-20 */
cmd.block = block;
cmd.component = component;
cmd.key = key;
cmd.val = val;
pr_info("set asp mode: block=%d, component=%d, key=%d, val=%d\n", block,
component, key, val);
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), NULL, chip);
if (err < 0)
pr_err("ERR:%d in dsp mode, block=%d, component=%d, key=%d, val=%d\n",
err, block, component, key, val);
return err;
}
int aoc_get_audio_dsp_mode(struct aoc_chip *chip, long *val)
{
int err;
struct CMD_AUDIO_OUTPUT_DSP_MODE cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_DSP_MODE_GET_ID, sizeof(cmd));
pr_debug("Get audio asp mode\n");
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in getting audio dsp mode", err);
return err;
}
if (val)
*val = cmd.mode;
return 0;
}
int aoc_set_audio_dsp_mode(struct aoc_chip *chip, long val)
{
int err;
struct CMD_AUDIO_OUTPUT_DSP_MODE cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_DSP_MODE_SET_ID, sizeof(cmd));
cmd.mode = val;
pr_info("Set audio dsp mode: %ld\n", val);
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in audio dsp mode set: val=%ld\n", err, val);
return err;
}
return 0;
}
int aoc_get_sink_channel_bitmap(struct aoc_chip *chip, int sink)
{
int err;
struct CMD_AUDIO_OUTPUT_GET_SINKS_BITMAPS cmd;
if (sink >= AUDIO_OUTPUT_SINKS) {
pr_err("Err: sink id %d not exists!\n", sink);
return -EINVAL;
}
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_SINKS_BITMAPS_ID, sizeof(cmd));
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("Err:%d in get aoc sink %d channel bitmap!\n", err, sink);
return err;
}
return cmd.bitmap[sink];
}
int aoc_get_sink_mode(struct aoc_chip *chip, int sink)
{
return chip->sink_mode[sink];
}
int aoc_set_sink_mode(struct aoc_chip *chip, int sink, int mode)
{
int err;
struct CMD_AUDIO_OUTPUT_SINK2 cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SINK2_ID, sizeof(cmd));
cmd.sink = sink;
cmd.mode = mode;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0) {
pr_err("Error in get aoc sink processing state !\n");
return err;
}
chip->sink_mode[sink] = mode;
pr_info("sink state set :%d - %d\n", sink, cmd.mode);
return 0;
}
int aoc_get_sink_state(struct aoc_chip *chip, int sink)
{
int err;
struct CMD_AUDIO_OUTPUT_GET_SINK_PROCESSING_STATE cmd;
AocCmdHdrSet(&(cmd.parent),
CMD_AUDIO_OUTPUT_GET_SINK_PROCESSING_STATE_ID,
sizeof(cmd));
cmd.sink = sink;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0)
pr_err("Error in get aoc sink processing state !\n");
pr_info("sink_state:%d - %d\n", sink, cmd.mode);
return err < 0 ? err : cmd.mode;
}
/* TODO: usb cfg may be devided into three commands, dev/ep-ids, rx, tx */
int aoc_set_usb_config(struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_OUTPUT_USB_CONFIG cmd = chip->usb_sink_cfg;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_USB_CONFIG_ID, sizeof(cmd));
cmd.rx_enable = true;
cmd.tx_enable = true;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0)
pr_err("Err:%d in aoc set usb config!\n", err);
return err;
}
int aoc_set_usb_config_v2(struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_OUTPUT_USB_CONFIG_V2 cmd = chip->usb_sink_cfg_v2;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_USB_CONFIG_V2_ID, sizeof(cmd));
cmd.rx_enable = true;
cmd.tx_enable = true;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0)
pr_err("Err:%d in aoc set usb config v2!\n", err);
return err;
}
static int
aoc_audio_playback_trigger_source(struct aoc_alsa_stream *alsa_stream, int cmd,
int src)
{
int err;
struct CMD_AUDIO_OUTPUT_SOURCE source;
AocCmdHdrSet(&(source.parent), CMD_AUDIO_OUTPUT_SOURCE_ID,
sizeof(source));
/* source on/off */
source.source = src;
switch (cmd) {
case START:
source.on = 1;
break;
case STOP:
source.on = 0;
break;
default:
pr_err("Invalid source operation (only On/Off allowed)\n");
return -EINVAL;
}
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&source,
sizeof(source), NULL, alsa_stream->chip);
pr_debug("Source %d %s !\n", alsa_stream->idx,
cmd == START ? "on" : "off");
return err;
}
static int aoc_audio_playback_source_on(struct aoc_chip *chip, int cmd, int src)
{
int err;
struct CMD_AUDIO_OUTPUT_SOURCE source;
AocCmdHdrSet(&(source.parent), CMD_AUDIO_OUTPUT_SOURCE_ID, sizeof(source));
/* source on/off */
source.source = src;
switch (cmd) {
case START:
source.on = 1;
break;
case STOP:
source.on = 0;
break;
default:
pr_err("Invalid source operation (only On/Off allowed)\n");
return -EINVAL;
}
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&source, sizeof(source), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in playback source %s\n", err, (cmd == START) ? "on" : "off");
return err;
}
pr_debug("Source %d %s !\n", src, cmd == START ? "on" : "off");
return 0;
}
/* Bind/unbind the source and dest */
static int aoc_audio_path_bind(int src, int dst, int cmd, struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_OUTPUT_BIND bind;
if (dst < 0)
return 0;
pr_info("%s: src:%d - sink:%d!\n", cmd == START ? "bind" : "unbind", src, dst);
AocCmdHdrSet(&(bind.parent), CMD_AUDIO_OUTPUT_BIND2_ID, sizeof(bind));
bind.bind = (cmd == START) ? 1 : 0;
bind.src = src;
bind.dst = dst;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&bind, sizeof(bind), NULL, chip);
if (err < 0)
pr_err("ERR:%d %s: src:%d - sink:%d!\n", err, (cmd == START) ? "bind" : "unbind",
src, dst);
return err;
}
static int aoc_mmap_capture_trigger(struct aoc_alsa_stream *alsa_stream, int record_cmd)
{
int err = 0;
int cmd_id;
struct CMD_AUDIO_INPUT_MMAP_ENABLE_2 cmd;
struct aoc_chip *chip = alsa_stream->chip;
if (alsa_stream->channels > 2) {
pr_err("ERR: mmap capture channel number %d (only mono or stereo allowed)\n",
alsa_stream->channels);
return -EINVAL;
}
cmd_id = (record_cmd == START) ? CMD_AUDIO_INPUT_MMAP_ENABLE_2_ID :
CMD_AUDIO_INPUT_MIC_MMAP_DISABLE_ID;
if (record_cmd == START) {
AocCmdHdrSet(&(cmd.parent), cmd_id, sizeof(cmd));
cmd.chan = alsa_stream->channels;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
} else
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in audio mmap capture %s\n", err,
(record_cmd == START) ? "start" : "stop");
return err;
}
return 0;
}
static int aoc_raw_capture_trigger(struct aoc_alsa_stream *alsa_stream, int record_cmd)
{
int err = 0;
int cmd_id;
struct CMD_AUDIO_INPUT_RAW_ENABLE_2 cmd;
struct aoc_chip *chip = alsa_stream->chip;
if (alsa_stream->channels > 2) {
pr_err("ERR: raw capture channel number %d (only mono or stereo allowed)\n",
alsa_stream->channels);
return -EINVAL;
}
cmd_id = (record_cmd == START) ? CMD_AUDIO_INPUT_RAW_ENABLE_2_ID :
CMD_AUDIO_INPUT_MIC_RAW_DISABLE_ID;
if (record_cmd == START) {
AocCmdHdrSet(&(cmd.parent), cmd_id, sizeof(cmd));
cmd.chan = alsa_stream->channels;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
} else
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in audio raw capture %s\n", err,
(record_cmd == START) ? "start" : "stop");
return err;
}
return 0;
}
static int aoc_audio_capture_mic_process_mode(struct aoc_chip *chip,
struct aoc_alsa_stream *alsa_stream)
{
int err;
struct CMD_AUDIO_INPUT_GET_AP_MIC_INDEX cmd;
if (alsa_stream->idx == UC_ULTRASONIC_RECORD)
return 0;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_SET_AP_MIC_INDEX_ID, sizeof(cmd));
if (chip->mic_spatial_module_enable)
cmd.mic_process_index = AP_MIC_PROCESS_SPATIAL;
else
cmd.mic_process_index = AP_MIC_PROCESS_RAW;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR: %d in set builtin_mic_process_mode!\n", err);
return err;
}
return 0;
}
static int aoc_audio_capture_runtime_control(struct aoc_alsa_stream *alsa_stream, int dst, bool on,
bool be_on)
{
int err;
struct aoc_chip *chip = alsa_stream->chip;
if (!be_on)
return 0;
if (on) {
err = aoc_audio_capture_mic_process_mode(chip, alsa_stream);
if (err < 0)
return err;
err = ap_data_control_trigger(chip, alsa_stream, START);
if (err < 0)
return err;
} else {
err = ap_data_control_trigger(chip, alsa_stream, STOP);
if (err < 0)
return err;
}
return 0;
}
static int aoc_raw_capture_runtime_control(struct aoc_alsa_stream *alsa_stream,
int dst, bool on, bool be_on)
{
int err;
if (on) {
if (!be_on)
return 0;
err = aoc_raw_capture_trigger(alsa_stream, START);
if (err < 0)
return err;
} else {
err = aoc_raw_capture_trigger(alsa_stream, STOP);
if (err < 0)
return err;
}
return 0;
}
static int aoc_mmap_capture_runtime_control(struct aoc_alsa_stream *alsa_stream,
int dst, bool on, bool be_on)
{
int err;
if (on) {
if (!be_on)
return 0;
err = aoc_mmap_capture_trigger(alsa_stream, START);
if (err < 0)
return err;
} else {
err = aoc_mmap_capture_trigger(alsa_stream, STOP);
if (err < 0)
return err;
}
return 0;
}
int ap_data_control_trigger(struct aoc_chip *chip, struct aoc_alsa_stream *alsa_stream,
int record_cmd)
{
int err = 0;
struct CMD_HDR cmd;
int cmd_id;
/* We don't support the stop_data yet */
if (record_cmd != START)
return 0;
if (alsa_stream->idx == UC_ULTRASONIC_RECORD)
cmd_id = CMD_AUDIO_INPUT_ULTRASONIC_AP_START_ID;
else
cmd_id = CMD_AUDIO_INPUT_MIC_RECORD_AP_START_DATA_ID;
AocCmdHdrSet(&cmd, cmd_id, sizeof(cmd));
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in audio input mic record start/stop\n", err);
}
return err;
}
int ap_record_stop(struct aoc_chip *chip, struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
int cmd_id;
if (alsa_stream->idx == UC_ULTRASONIC_RECORD)
cmd_id = CMD_AUDIO_INPUT_ULTRASONIC_CAPTURE_STOP_ID;
else
cmd_id = CMD_AUDIO_INPUT_AP_INPUT_STOP_ID;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in audio input mic record start/stop\n", err);
}
return err;
}
static int aoc_record_filter_runtime_control(struct aoc_chip *chip, uint32_t port_id, bool on)
{
int err;
if (aoc_record_param_configured_num(chip) == 0)
return 0;
pr_info("%s: port 0x%x runtime %d", __func__, port_id, on);
if (on) {
err = aoc_audio_capture_mic_prepare(chip);
} else {
err = aoc_audio_capture_mic_close(chip);
}
return err;
}
static int aoc_us_record_filter_runtime_control(struct aoc_chip *chip, uint32_t port_id, bool on)
{
int err;
if (aoc_us_record_param_configured_num(chip) == 0)
return 0;
pr_info("%s: port 0x%x runtime %d", __func__, port_id, on);
if (on) {
err = aoc_audio_us_capture_mic_prepare(chip);
} else {
err = aoc_audio_us_capture_mic_close(chip);
}
return err;
}
int aoc_capture_filter_runtime_control(struct aoc_chip *chip, uint32_t port_id, bool on)
{
switch (port_id) {
case INTERNAL_MIC_US_TX:
return aoc_us_record_filter_runtime_control(chip, port_id, on);
default:
return aoc_record_filter_runtime_control(chip, port_id, on);
}
}
int aoc_audio_capture_runtime_trigger(struct aoc_chip *chip, int ep_id, int dst, bool on)
{
struct aoc_alsa_stream *alsa_stream;
alsa_stream = find_alsa_stream_by_ep_id(chip, ep_id);
if (!alsa_stream)
return 0;
if (!alsa_stream->running)
return 0;
pr_info("%s: on %d idx %d", __func__, on, alsa_stream->idx);
switch (alsa_stream->idx) {
case UC_ULTRASONIC_RECORD:
case UC_AUDIO_RECORD:
return aoc_audio_capture_runtime_control(alsa_stream, dst, on, true);
case UC_MMAP_RECORD:
return aoc_mmap_capture_runtime_control(alsa_stream, dst, on, true);
case UC_LOW_LATENCY_AUDIO_RECORD:
return aoc_raw_capture_runtime_control(alsa_stream, dst, on, true);
default:
return 0;
}
}
static int aoc_audio_capture_path_bind(struct aoc_chip *chip, int ep_id,
int dst, bool on, bool be_on)
{
struct aoc_alsa_stream *alsa_stream;
alsa_stream = find_alsa_stream_by_ep_id(chip, ep_id);
if (!alsa_stream)
return 0;
if (!alsa_stream->running)
return 0;
switch (alsa_stream->idx) {
case UC_ULTRASONIC_RECORD:
case UC_AUDIO_RECORD:
return aoc_audio_capture_runtime_control(alsa_stream, dst, on, be_on);
case UC_MMAP_RECORD:
return aoc_mmap_capture_runtime_control(alsa_stream, dst, on, be_on);
case UC_LOW_LATENCY_AUDIO_RECORD:
return aoc_raw_capture_runtime_control(alsa_stream, dst, on, be_on);
default:
return 0;
}
}
int aoc_audio_path_open(struct aoc_chip *chip, int src, int dest, bool be_on)
{
uint32_t src_idx, dest_idx;
bool src_for_capture;
/* ignore nohost */
if (src & AOC_NOHOST)
return 0;
src_for_capture = src & AOC_TX;
src_idx = AOC_ID_TO_INDEX(src);
dest_idx = AOC_ID_TO_INDEX(dest);
/* voice call capture or playback */
if ((src_idx == 3 && src_for_capture) || (src_idx == 4 && !src_for_capture))
return aoc_phonecall_path_open(chip, src_idx, dest_idx, dest & AOC_TX);
if (src_idx == IDX_VOIP)
return aoc_voipcall_path_open(chip, src_idx, dest_idx, dest & AOC_TX);
if (src_for_capture)
return aoc_audio_capture_path_bind(chip, src, dest, true, be_on);
else
return aoc_audio_path_bind(ep_id_to_source(src_idx),
hw_id_to_sink(dest_idx), START, chip);
}
int aoc_audio_path_close(struct aoc_chip *chip, int src, int dest, bool be_on)
{
uint32_t src_idx, dest_idx;
bool src_for_capture;
/* ignore nohost */
if (src & AOC_NOHOST)
return 0;
src_for_capture = src & AOC_TX;
src_idx = AOC_ID_TO_INDEX(src);
dest_idx = AOC_ID_TO_INDEX(dest);
/* voice call capture or playback */
if ((src_idx == 3 && src_for_capture) || (src_idx == 4 && !src_for_capture))
return aoc_phonecall_path_close(chip, src_idx, dest_idx, dest & AOC_TX);
if (src_idx == IDX_VOIP)
return aoc_voipcall_path_close(chip, src_idx, dest_idx, dest & AOC_TX);
if (src_for_capture)
return aoc_audio_capture_path_bind(chip, src, dest, false, be_on);
else
return aoc_audio_path_bind(ep_id_to_source(src_idx),
hw_id_to_sink(dest_idx), STOP, chip);
}
static int aoc_audio_playback_set_params(struct aoc_alsa_stream *alsa_stream,
uint32_t channels, uint32_t samplerate,
uint32_t bps, bool pcm_float_fmt,
int source_mode)
{
int err;
struct CMD_AUDIO_OUTPUT_EP_SETUP cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_EP_SETUP_ID, sizeof(cmd));
cmd.d.channel = alsa_stream->entry_point_idx;
cmd.d.watermark = PLAYBACK_WATERMARK_DEFAULT;
cmd.d.length = 0;
cmd.d.address = 0;
cmd.d.wraparound = true;
cmd.d.metadata.offset = 0;
switch (bps) {
case 32:
cmd.d.metadata.bits = WIDTH_32_BIT;
break;
case 24:
cmd.d.metadata.bits = WIDTH_24_BIT;
break;
case 16:
cmd.d.metadata.bits = WIDTH_16_BIT;
break;
case 8:
cmd.d.metadata.bits = WIDTH_8_BIT;
break;
default:
cmd.d.metadata.bits = WIDTH_32_BIT;
}
cmd.d.metadata.format =
(pcm_float_fmt) ? FRMT_FLOATING_POINT : FRMT_FIXED_POINT;
cmd.d.metadata.chan = channels;
switch (samplerate) {
case 48000:
cmd.d.metadata.sr = SR_48KHZ;
break;
case 44100:
cmd.d.metadata.sr = SR_44K1HZ;
break;
case 16000:
cmd.d.metadata.sr = SR_16KHZ;
break;
case 8000:
cmd.d.metadata.sr = SR_8KHZ;
break;
default:
cmd.d.metadata.sr = SR_48KHZ;
}
pr_debug("chan =%d, sr=%d, bits=%d\n", cmd.d.metadata.chan,
cmd.d.metadata.sr, cmd.d.metadata.bits);
switch (source_mode) {
case PLAYBACK_MODE:
cmd.mode = ENTRYPOINT_MODE_PLAYBACK;
break;
case HAPTICS_MODE:
cmd.mode = ENTRYPOINT_MODE_HAPTICS;
break;
case OFFLOAD_MODE:
cmd.mode = ENTRYPOINT_MODE_DECODE_OFFLOAD;
break;
default:
cmd.mode = ENTRYPOINT_MODE_PLAYBACK;
}
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), NULL, alsa_stream->chip);
if (err < 0)
pr_err("ERR:%d in playback set parameters\n", err);
return err;
}
static int aoc_audio_capture_set_params(struct aoc_alsa_stream *alsa_stream, uint32_t channels,
uint32_t samplerate, uint32_t bps, bool pcm_float_fmt)
{
int err = 0;
struct CMD_AUDIO_INPUT_MIC_RECORD_AP_SET_PARAMS cmd;
int cmd_id;
struct aoc_chip *chip = alsa_stream->chip;
int i, mic_id;
if (alsa_stream->idx < 0 || alsa_stream->idx >= sizeof(chip->capture_param_set) * 8) {
pr_err("ERR: idx %d over capture_param_set maximum\n", alsa_stream->idx);
return -EINVAL;
}
/* Regular audio capture should be the primary setting of the single ap filter */
if ((alsa_stream->idx != UC_ULTRASONIC_RECORD) &&
(chip->capture_param_set & (1 << UC_AUDIO_RECORD))) {
pr_info("%s: ignore capture set param 0x%llu", __func__, chip->capture_param_set);
chip->capture_param_set |= (1 << alsa_stream->idx);
if (!aoc_ring_flush_read_data(alsa_stream->dev->service, AOC_UP, 0)) {
pr_err("ERR: capture ring buffer flush fail\n");
err = -EINVAL;
}
return err;
}
if (alsa_stream->idx == UC_ULTRASONIC_RECORD)
cmd_id = CMD_AUDIO_INPUT_ULTRASONIC_AP_SET_PARAMS_ID;
else
cmd_id = CMD_AUDIO_INPUT_MIC_RECORD_AP_SET_PARAMS_ID;
AocCmdHdrSet(&(cmd.parent), cmd_id, sizeof(cmd));
/* TODO: the output of spatial module is stereo */
if (channels < 1 || channels > NUM_OF_BUILTIN_MIC) {
pr_err("ERR: wrong channel number %u for capture\n", channels);
err = -EINVAL;
goto exit;
}
/* TODO: update this after DSP supports the runtime configuration */
cmd.pdm_mask = 0;
if (alsa_stream->idx == UC_ULTRASONIC_RECORD) {
for (i = 0; i < ARRAY_SIZE(chip->buildin_us_mic_id_list); i++) {
cmd.interleaving[i] = 0xff;
mic_id = chip->buildin_us_mic_id_list[i];
if (mic_id != -1) {
cmd.interleaving[i] = mic_id;
cmd.pdm_mask = cmd.pdm_mask | (1 << mic_id);
}
}
} else {
for (i = 0; i < ARRAY_SIZE(chip->buildin_mic_id_list); i++) {
cmd.interleaving[i] = 0xff;
mic_id = chip->buildin_mic_id_list[i];
if (mic_id != -1) {
cmd.interleaving[i] = mic_id;
cmd.pdm_mask = cmd.pdm_mask | (1 << mic_id);
}
}
}
cmd.period_ms = 10; /*TODO: how to make it configuratable*/
cmd.num_periods = 4; /*TODO: how to make it configuratable*/
switch (samplerate) {
case 96000:
cmd.sample_rate = SR_96KHZ;
break;
case 48000:
cmd.sample_rate = SR_48KHZ;
break;
case 44100:
cmd.sample_rate = SR_44K1HZ;
break;
case 16000:
cmd.sample_rate = SR_16KHZ;
break;
case 8000:
cmd.sample_rate = SR_8KHZ;
break;
default:
cmd.sample_rate = SR_48KHZ;
}
switch (bps) {
case 32:
cmd.requested_format.bits = WIDTH_32_BIT;
break;
case 24:
/* TODO: tinycap limitation */
cmd.requested_format.bits = WIDTH_32_BIT;
break;
case 16:
cmd.requested_format.bits = WIDTH_16_BIT;
break;
case 8:
cmd.requested_format.bits = WIDTH_8_BIT;
break;
default:
cmd.requested_format.bits = WIDTH_32_BIT;
}
cmd.requested_format.sr = cmd.sample_rate; /* TODO: double check format*/
cmd.requested_format.format = (pcm_float_fmt) ? FRMT_FLOATING_POINT : FRMT_FIXED_POINT;
cmd.requested_format.chan = channels;
if (chip->mic_spatial_module_enable && !aoc_pcm_is_mmap_raw(alsa_stream))
cmd.mic_process_index = AP_MIC_PROCESS_SPATIAL;
else
cmd.mic_process_index = AP_MIC_PROCESS_RAW;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in capture parameter setup\n", err);
goto exit;
}
chip->capture_param_set |= (1 << alsa_stream->idx);
if (alsa_stream->idx == UC_ULTRASONIC_RECORD) {
/* Start the ultrasound mic */
err = aoc_audio_us_capture_mic_prepare(chip);
if (err < 0)
pr_err("ERR:%d in audio us capture mic prepare\n", err);
} else {
/* Start the pdm/usb/bt mic */
err = aoc_audio_capture_mic_prepare(chip);
if (err < 0)
pr_err("ERR:%d in audio capture mic prepare\n", err);
}
pr_debug("Flush aoc ring buffer\n");
if (!aoc_ring_flush_read_data(alsa_stream->dev->service, AOC_UP, 0)) {
pr_err("ERR: capture ring buffer flush fail\n");
err = -EINVAL;
}
/* Resume the mmap and raw */
if (alsa_stream->idx == UC_AUDIO_RECORD) {
struct aoc_alsa_stream *target_alsa_stream;
if (chip->capture_param_set & (1 << UC_MMAP_RECORD)) {
target_alsa_stream = find_alsa_stream_by_device_idx(chip, UC_MMAP_RECORD);
if (target_alsa_stream && target_alsa_stream->running) {
aoc_mmap_capture_trigger(alsa_stream, START);
}
}
if (chip->capture_param_set & (1 << UC_LOW_LATENCY_AUDIO_RECORD)) {
target_alsa_stream =
find_alsa_stream_by_device_idx(chip, UC_LOW_LATENCY_AUDIO_RECORD);
if (target_alsa_stream && target_alsa_stream->running) {
aoc_raw_capture_trigger(alsa_stream, START);
}
}
}
exit:
return err;
}
static int aoc_audio_capture_spatial_module_trigger(struct aoc_chip *chip,
int record_cmd)
{
int err = 0;
struct CMD_HDR cmd;
pr_info("%s: %d", __func__, record_cmd);
AocCmdHdrSet(&cmd,
(record_cmd == START) ? CMD_AUDIO_INPUT_SPATIAL_START_ID :
CMD_AUDIO_INPUT_SPATIAL_STOP_ID,
sizeof(cmd));
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
NULL, chip);
if (err < 0)
pr_err("ERR:%d in spatial module trigger\n", err);
return err;
}
int aoc_spatial_module_start(struct aoc_chip *chip)
{
return aoc_audio_capture_spatial_module_trigger(chip, START);
}
int aoc_spatial_module_stop(struct aoc_chip *chip)
{
return aoc_audio_capture_spatial_module_trigger(chip, STOP);
}
/* Start or stop the stream */
static int aoc_audio_capture_trigger(struct aoc_alsa_stream *alsa_stream, int record_cmd)
{
int err = 0;
struct aoc_chip *chip = alsa_stream->chip;
pr_info("%s: %d", __func__, record_cmd);
if (alsa_stream->stream_type == NORMAL) {
err = ap_data_control_trigger(chip, alsa_stream, record_cmd);
if (err < 0)
goto exit;
}
/* For mmap capture */
if (alsa_stream->stream_type == MMAPED) {
err = aoc_mmap_capture_trigger(alsa_stream, record_cmd);
if (err < 0)
pr_err("ERR:%d in aoc mmap capture start/stop\n", err);
}
/* For raw capture */
if (alsa_stream->stream_type == RAW) {
err = aoc_raw_capture_trigger(alsa_stream, record_cmd);
if (err < 0)
pr_err("ERR:%d in aoc raw capture start/stop\n", err);
}
exit:
if (err < 0)
pr_err("ERR:%d in capture trigger\n", err);
return err;
}
static int aoc_audio_get_parameters(int cmd_id, int block, int component, int key, int *val,
struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_INPUT_GET_PARAMETER cmd0;
struct CMD_AUDIO_OUTPUT_GET_PARAMETER cmd1;
if (cmd_id == CMD_AUDIO_INPUT_GET_PARAMETER_ID) /* Input channel */
{
AocCmdHdrSet(&(cmd0.parent), CMD_AUDIO_INPUT_GET_PARAMETER_ID, sizeof(cmd0));
cmd0.block = block;
cmd0.component = component;
cmd0.key = key;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd0, sizeof(cmd0),
(uint8_t *)&cmd0, chip);
if (!err && val)
*val = cmd0.val;
} else /*Output channel */
{
AocCmdHdrSet(&(cmd1.parent), CMD_AUDIO_OUTPUT_GET_PARAMETER_ID, sizeof(cmd1));
cmd1.block = block;
cmd1.component = component;
cmd1.key = key;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd1, sizeof(cmd1),
(uint8_t *)&cmd1, chip);
if (!err && val)
*val = cmd1.val;
}
if (err < 0) {
pr_err("ERR:%d in audio parameter get- block:%d,component:%d,key:%d\n", err, block,
component, key);
return err;
}
return 0;
}
static int aoc_audio_set_parameters(int cmd_id, int block, int component, int key, int val,
struct aoc_chip *chip)
{
int err;
struct CMD_AUDIO_INPUT_SET_PARAMETER cmd0;
struct CMD_AUDIO_OUTPUT_SET_PARAMETER cmd1;
if (cmd_id == CMD_AUDIO_INPUT_SET_PARAMETER_ID) /* Input channel */
{
AocCmdHdrSet(&(cmd0.parent), CMD_AUDIO_INPUT_SET_PARAMETER_ID, sizeof(cmd0));
cmd0.block = block;
cmd0.component = component;
cmd0.key = key;
cmd0.val = val;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd0, sizeof(cmd0), NULL,
chip);
} else /*Output channel */
{
AocCmdHdrSet(&(cmd1.parent), CMD_AUDIO_OUTPUT_SET_PARAMETER_ID, sizeof(cmd1));
cmd1.block = block;
cmd1.component = component;
cmd1.key = key;
cmd1.val = val;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd1, sizeof(cmd1), NULL,
chip);
}
if (err < 0) {
pr_err("ERR:%d in audio parameter set- block:%d,component:%d,key:%d,val:%d\n", err,
block, component, key, val);
return err;
}
return 0;
}
int aoc_incall_mic_sink_mute_get(struct aoc_chip *chip, int param, long *mute)
{
int err;
int cmd_id, block, component, key, value;
if (param == 0) /* Up link (mic) */
{
cmd_id = CMD_AUDIO_OUTPUT_GET_PARAMETER_ID;
block = 19;
component = 0;
key = 6;
} else /* Download link (sink) */
{
cmd_id = CMD_AUDIO_OUTPUT_GET_PARAMETER_ID;
block = 19;
component = 30;
key = 6;
}
/* Send cmd to AOC */
err = aoc_audio_get_parameters(cmd_id, block, component, key, &value, chip);
if (err < 0) {
pr_err("ERR:%d in incall mute get\n", err);
return err;
}
if (mute)
*mute = (value == FLOAT_ZERO) ? 1 : 0;
return 0;
}
int aoc_incall_mic_sink_mute_set(struct aoc_chip *chip, int param, long mute)
{
int err;
int cmd_id, block, component, key, value;
if (param == 0) /* Up link (mic) */
{
cmd_id = CMD_AUDIO_OUTPUT_SET_PARAMETER_ID;
block = 19;
component = 0;
key = 6;
} else /* Download link (sink) */
{
cmd_id = CMD_AUDIO_OUTPUT_SET_PARAMETER_ID;
block = 19;
component = 30;
key = 6;
}
value = mute ? FLOAT_ZERO : FLOAT_ONE;
/* Send cmd to AOC */
err = aoc_audio_set_parameters(cmd_id, block, component, key, value, chip);
if (err < 0) {
pr_err("ERR:%d in incall mute set\n", err);
return err;
}
return 0;
}
/* can we get dB gain directly */
int aoc_mic_record_gain_get(struct aoc_chip *chip, long *val)
{
int err;
int cmd_id, block, component, key, value;
cmd_id = CMD_AUDIO_INPUT_GET_PARAMETER_ID;
block = 136;
component = 30;
key = 16; /* for dB */
/* Send cmd to AOC */
err = aoc_audio_get_parameters(cmd_id, block, component, key, &value, chip);
if (err < 0) {
pr_err("ERR:%d in in mic record gain get\n", err);
return err;
}
if (val)
*val = value;
return 0;
}
int aoc_mic_record_gain_set(struct aoc_chip *chip, long val)
{
int err;
int cmd_id, block, component, key, value;
cmd_id = CMD_AUDIO_INPUT_SET_PARAMETER_ID;
block = 136;
component = 30;
key = 16; /* for dB */
value = val; /* TODO: db value in float? */
/* Send cmd to AOC */
err = aoc_audio_set_parameters(cmd_id, block, component, key, value, chip);
if (err < 0) {
pr_err("ERR:%d in mic record gain set\n", err);
return err;
}
return 0;
}
int aoc_mmap_record_gain_get(struct aoc_chip *chip, long *val)
{
int err;
int cmd_id, block, component, key, value;
cmd_id = CMD_AUDIO_INPUT_GET_PARAMETER_ID;
block = 136;
component = 0;
key = 16; /* for dB */
/* Send cmd to AOC */
err = aoc_audio_get_parameters(cmd_id, block, component, key, &value, chip);
if (err < 0) {
pr_err("ERR:%d mmap mic record gain get\n", err);
return err;
}
if (val)
*val = value;
return 0;
}
int aoc_mmap_record_gain_set(struct aoc_chip *chip, long val)
{
int err;
int cmd_id, block, component, key, value;
cmd_id = CMD_AUDIO_INPUT_SET_PARAMETER_ID;
block = 136;
component = 0;
key = 16; /* for dB */
value = val;
/* Send cmd to AOC */
err = aoc_audio_set_parameters(cmd_id, block, component, key, value, chip);
if (err < 0) {
pr_err("ERR:%d mmap record gain set\n", err);
return err;
}
return 0;
}
int aoc_audio_capture_eraser_enable(struct aoc_chip *chip, long enable)
{
int cmd_id, err = 0;
cmd_id = (enable == 1) ? CMD_AUDIO_INPUT_MIC_RECORD_AP_ENABLE_AEC_ID :
CMD_AUDIO_INPUT_MIC_RECORD_AP_DISABLE_AEC_ID;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in audio capture eraser %s\n", err, (enable) ? "enable" : "disable");
return err;
}
return 0;
}
int aoc_hotword_tap_enable(struct aoc_chip *chip, long enable)
{
if (chip->hotword_supported) {
int cmd_id, err = 0;
cmd_id = (enable == 1) ? CMD_AUDIO_INPUT_HOTWORD_ENABLE_HOTWORD_TAP_ID :
CMD_AUDIO_INPUT_HOTWORD_DISABLE_HOTWORD_TAP_ID;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in hotword tap %s\n", err, (enable) ? "enable" : "disable");
return err;
}
return 0;
} else {
pr_err("WARN:hotword is not supported on this device\n");
return 0;
}
}
int aoc_load_cca_module(struct aoc_chip *chip, long load)
{
int cmd_id, err = 0;
cmd_id = (load == 1) ? CMD_AUDIO_OUTPUT_VOICE_CCA_START_ID :
CMD_AUDIO_OUTPUT_VOICE_CCA_STOP_ID;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL, cmd_id, chip);
return err;
}
int aoc_lvm_enable_get(struct aoc_chip *chip, long *enable)
{
int err;
struct CMD_AUDIO_OUTPUT_GET_PARAMETER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_PARAMETER_ID, sizeof(cmd));
cmd.block = 14; /* ABLOCK_DCDOFF */
cmd.component = 0; /* LVM */
cmd.key = 16; /* ASP_LVM_PARAM_ENABLE */
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in lvm get\n", err);
return err;
}
pr_debug("lvm: %s\n", cmd.val ? "enabled" : "disabled");
if (enable)
*enable = cmd.val;
return 0;
}
int aoc_lvm_enable_set(struct aoc_chip *chip, long enable)
{
int err;
struct CMD_AUDIO_OUTPUT_SET_PARAMETER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_PARAMETER_ID, sizeof(cmd));
cmd.block = 14; /* ABLOCK_DCDOFF */
cmd.component = 0;
cmd.key = 16;
cmd.val = enable;
pr_debug("lvm: %s\n", enable ? "enabled" : "disabled");
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in lvm set\n", err);
return err;
}
return 0;
}
int aoc_decoder_ref_enable_get(struct aoc_chip *chip, long *enable)
{
int err;
struct CMD_AUDIO_OUTPUT_GET_PARAMETER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_PARAMETER_ID, sizeof(cmd));
cmd.block = 14;
cmd.component = 1;
cmd.key = 0;
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in decoder ref get\n", err);
return err;
}
pr_debug("decoder ref: %s\n", cmd.val ? "enabled" : "disabled");
if (enable)
*enable = cmd.val;
return 0;
}
int aoc_decoder_ref_enable_set(struct aoc_chip *chip, long enable)
{
int err;
struct CMD_AUDIO_OUTPUT_SET_PARAMETER cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_SET_PARAMETER_ID, sizeof(cmd));
cmd.block = 14;
cmd.component = 1;
cmd.key = 0;
cmd.val = enable;
pr_debug("decoder ref: %s\n", enable ? "enabled" : "disabled");
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in decoder ref set\n", err);
return err;
}
return 0;
}
int aoc_compr_offload_linear_gain_get(struct aoc_chip *chip, long *val)
{
int err;
struct CMD_AUDIO_OUTPUT_DEC_CH_GAIN cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_DEC_CH_GAIN_GET_ID, sizeof(cmd));
cmd.ch_bit_mask = 0x03;
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0) {
pr_err("ERR:%d in decoder ref get\n", err);
return err;
}
if (val) {
val[0] = cmd.ch_gains[0];
val[1] = cmd.ch_gains[1];
}
return 0;
}
int aoc_compr_offload_linear_gain_set(struct aoc_chip *chip, long *val)
{
int err;
struct CMD_AUDIO_OUTPUT_DEC_CH_GAIN cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_DEC_CH_GAIN_SET_ID, sizeof(cmd));
cmd.ch_bit_mask = 0x03;
cmd.ch_gains[0] = val[0];
cmd.ch_gains[1] = val[1];
if (val[0] == 0 && val[1] == 0) {
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL,
CMD_AUDIO_OUTPUT_DEC_RESET_CURRENT_GAIN_ID,
chip);
if (err < 0) {
pr_err("ERR:%d in aoc compr reset gain\n", err);
return err;
}
/* To allow the reset finished in aoc */
msleep(100);
}
/* Send cmd to AOC */
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0) {
pr_err("ERR:%d in compr offload linear gain set\n", err);
return err;
}
return 0;
}
int aoc_sidetone_enable(struct aoc_chip *chip, int enable)
{
int err = 0;
int cmd = (enable) ? START : STOP;
int src, dest;
src = SIDETONE;
dest = SINK_SPEAKER;
pr_info("sidetone: %s \n", (enable) ? "Enabled" : "Disabled");
/* Source on/off */
err = aoc_audio_playback_source_on(chip, cmd, src);
if (err < 0) {
pr_err("ERR=%d, sidetone %s fail in source(%d) on/off\n", err,
(enable) ? "START" : "STOP", src);
goto exit;
}
/* Bind/ubind - source 11 and sink 0 */
err = aoc_audio_path_bind(src, dest, cmd, chip);
if (err < 0)
pr_err("ERR=%d, sidestone %s fail in binding src:%d - dest:%d\n", err,
(enable) ? "START" : "STOP", src, dest);
exit:
return err;
}
int aoc_mic_loopback(struct aoc_chip *chip, int enable)
{
int err;
struct CMD_AUDIO_INPUT_ENABLE_MIC_LOOPBACK cmd;
AocCmdHdrSet(&(cmd.parent),
((enable == 1) ? CMD_AUDIO_INPUT_MIC_LOOPBACK_START_ID :
CMD_AUDIO_INPUT_MIC_LOOPBACK_STOP_ID),
sizeof(cmd));
cmd.sample_rate = SR_48KHZ;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd),
NULL, chip);
if (err < 0)
pr_err("ERR:%d in mic loopback\n", err);
return err;
}
/*
* For capture: start recording
* for playback: source on and bind source/sinks
*
* TODO: Capturing from four mics on board differ from other sources
* (BT, USB,... I2S interface from headphone)
*/
int aoc_audio_start(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
int src;
/* TODO: compress offload may not use START for source on, CHECK! */
if (alsa_stream->cstream ||
(alsa_stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) {
src = alsa_stream->entry_point_idx;
err = aoc_audio_playback_trigger_source(alsa_stream, START,
src);
if (err < 0)
pr_err("ERR:%d in source on\n", err);
} else {
err = aoc_audio_capture_trigger(alsa_stream, START);
if (err < 0)
pr_err("ERR:%d in capture start\n", err);
}
return err;
}
/*
* For capture: stop recording
* For playback: source off and unbind source/sinks
*/
int aoc_audio_stop(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
int src;
if (alsa_stream->cstream ||
(alsa_stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) {
src = alsa_stream->entry_point_idx;
err = aoc_audio_playback_trigger_source(alsa_stream, STOP, src);
if (err < 0)
pr_err("ERR:%d in source off\n", err);
} else {
err = aoc_audio_capture_trigger(alsa_stream, STOP);
if (err < 0)
pr_err("ERR:%d in capture stop\n", err);
}
return err;
}
static int aoc_audio_hifi_start(struct aoc_alsa_stream *alsa_stream)
{
int cmd_id, err = 0;
struct aoc_chip *chip = alsa_stream->chip;
if (alsa_stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cmd_id = CMD_AUDIO_OUTPUT_USB_HIFI_PLAYBACK_START_ID;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL, cmd_id, chip);
if (err < 0)
pr_err("ERR:%d in usb hifi playback start on\n", err);
} else {
cmd_id = CMD_AUDIO_INPUT_USB_HIFI_CAPTURE_START_ID;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0)
pr_err("ERR:%d in usb hifi capture start on\n", err);
}
return err;
}
static int aoc_audio_hifi_stop(struct aoc_alsa_stream *alsa_stream)
{
int cmd_id, err = 0;
struct aoc_chip *chip = alsa_stream->chip;
if (alsa_stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
cmd_id = CMD_AUDIO_OUTPUT_USB_HIFI_PLAYBACK_STOP_ID;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL, cmd_id, chip);
if (err < 0)
pr_err("ERR:%d in usb hifi playback stop on\n", err);
} else {
cmd_id = CMD_AUDIO_INPUT_USB_HIFI_CAPTURE_STOP_ID;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0)
pr_err("ERR:%d in usb hifi capture stop on\n", err);
}
return err;
}
static int aoc_audio_android_aec_start(struct aoc_alsa_stream *alsa_stream)
{
int cmd_id, err = 0;
struct aoc_chip *chip = alsa_stream->chip;
cmd_id = CMD_AUDIO_OUTPUT_SPKR_ANDROID_AEC_START_ID;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in android aec capture start\n", err);
return err;
}
return 0;
}
static int aoc_audio_android_aec_stop(struct aoc_alsa_stream *alsa_stream)
{
int cmd_id, err = 0;
struct aoc_chip *chip = alsa_stream->chip;
cmd_id = CMD_AUDIO_OUTPUT_SPKR_ANDROID_AEC_STOP_ID;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in android aec capture stop\n", err);
return err;
}
return 0;
}
static int aoc_audio_capture_inject_params_check(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
int active_mic = 0;
int i;
struct aoc_alsa_stream *active_capture_stream;
if (alsa_stream->stream_type == CAP_INJ) {
active_capture_stream = find_alsa_stream_by_capture_stream(alsa_stream->chip);
if (active_capture_stream == NULL) {
pr_err("No valid capture stream to inject\n");
return -EINVAL;
}
/* Capture injection is replacing the PDM mic data NOT the recording data,
* So it need to compare with the PDM mic format */
/* checking if format is match */
/*
if (alsa_stream->params_rate != active_capture_stream->params_rate) {
pr_err("[CAP_INJ] sample rate mismatch %d vs %d\n",
alsa_stream->params_rate, active_capture_stream->params_rate);
err = -EINVAL;
}
if (alsa_stream->pcm_format_width != active_capture_stream->pcm_format_width) {
pr_err("[CAP_INJ] width mismatch %d vs %d\n",
alsa_stream->pcm_format_width,
active_capture_stream->pcm_format_width);
err = -EINVAL;
}
*/
for (i = 0; i < ARRAY_SIZE(alsa_stream->chip->buildin_mic_id_list); i++) {
if (alsa_stream->chip->buildin_mic_id_list[i] != -1)
active_mic ++;
}
if (alsa_stream->channels != active_mic) {
pr_err("[CAP_INJ] channels and active mic mismatch %d vs %d\n",
alsa_stream->channels, active_mic);
err = -EINVAL;
}
} else {
pr_err("[CAP_INJ] incorrect stream type %d\n", alsa_stream->stream_type);
err = -EINVAL;
}
return err;
}
static int aoc_audio_capture_inject_start(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
struct aoc_chip *chip = alsa_stream->chip;
err = aoc_audio_capture_inject_params_check(alsa_stream);
if (err < 0)
return err;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL,
CMD_AUDIO_INPUT_CAPTURE_INJECTION_START_ID, chip);
if (err < 0) {
pr_err("ERR:%d in audio capture inject start\n", err);
return err;
}
return 0;
}
static int aoc_audio_capture_inject_stop(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
struct aoc_chip *chip = alsa_stream->chip;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL,
CMD_AUDIO_INPUT_CAPTURE_INJECTION_STOP_ID, chip);
if (err < 0) {
pr_err("ERR:%d in audio capture inject stop\n", err);
return err;
}
return 0;
}
int aoc_audio_incall_start(struct aoc_alsa_stream *alsa_stream)
{
int stream, err = 0;
struct aoc_chip *chip = alsa_stream->chip;
if (alsa_stream->stream_type == CAP_INJ)
return aoc_audio_capture_inject_start(alsa_stream);
if (alsa_stream->stream_type == HIFI)
return aoc_audio_hifi_start(alsa_stream);
if (alsa_stream->stream_type == ANDROID_AEC)
return aoc_audio_android_aec_start(alsa_stream);
if (alsa_stream->stream_type == HOTWORD_TAP)
return 0;
/* TODO: stream number inferred by pcm device idx, pb_0:18, cap_0:20, better way needed */
if (alsa_stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
stream = alsa_stream->entry_point_idx - 18;
stream = min(stream, 2); /* stream 2 for pb_2 has device id 29 */
err = aoc_incall_playback_enable_set(chip, stream, 1);
if (err < 0)
pr_err("ERR:%d in incall playback start on\n", err);
} else {
stream = alsa_stream->entry_point_idx - 20;
err = aoc_incall_capture_enable_set(chip, stream,
chip->incall_capture_state[stream]);
if (err < 0)
pr_err("ERR:%d in incall capture start on\n", err);
}
return err;
}
int aoc_audio_incall_stop(struct aoc_alsa_stream *alsa_stream)
{
int stream, err = 0;
struct aoc_chip *chip = alsa_stream->chip;
if (alsa_stream->stream_type == CAP_INJ)
return aoc_audio_capture_inject_stop(alsa_stream);
if (alsa_stream->stream_type == HIFI)
return aoc_audio_hifi_stop(alsa_stream);
if (alsa_stream->stream_type == ANDROID_AEC)
return aoc_audio_android_aec_stop(alsa_stream);
if (alsa_stream->stream_type == HOTWORD_TAP)
return 0;
/* TODO: stream number inferred by pcm device idx, pb_0:18, cap_0:20, better way needed */
if (alsa_stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
stream = alsa_stream->entry_point_idx - 18;
err = aoc_incall_playback_enable_set(chip, stream, 0);
if (err < 0)
pr_err("ERR:%d in incall playback start on\n", err);
} else {
stream = alsa_stream->entry_point_idx - 20;
err = aoc_incall_capture_enable_set(chip, stream, 0);
if (err < 0)
pr_err("ERR:%d in incall capture start on\n", err);
}
return err;
}
int aoc_audio_voip_start(struct aoc_alsa_stream *alsa_stream)
{
return 0;
}
int aoc_audio_voip_stop(struct aoc_alsa_stream *alsa_stream)
{
return 0;
}
/* TODO: this function is modified to deal with the issue where ALSA appl_ptr
* and the reader pointer in AoC ringer buffer are out-of-sync due to overflow
*/
int aoc_audio_read(struct aoc_alsa_stream *alsa_stream, void *dest,
uint32_t count)
{
int err = 0;
void *tmp;
struct aoc_service_dev *dev = alsa_stream->dev;
uint32_t avail;
tmp = (void *)(alsa_stream->substream->runtime->dma_area);
memset(tmp, 0, count);
avail = aoc_ring_bytes_available_to_read(dev->service, AOC_UP);
if (unlikely(avail < count)) {
pr_err("ERR: overrun in audio capture. avail = %d, toread = %d\n",
avail, count);
}
/* Only read bytes available in the ring buffer */
avail = min(avail, count);
if (avail) {
err = aoc_service_read(dev, (void *)tmp, avail, NONBLOCKING);
if (unlikely(err != avail)) {
pr_err("ERR: %d bytes not read from ring buffer\n",
count - err);
err = -EFAULT;
goto out;
}
}
/* If AoC is not ready, force read data to zero */
if (!aoc_online_state(dev))
memset(tmp, 0, count);
err = copy_to_user(dest, tmp, count);
if (err != 0) {
pr_err("ERR: %d bytes not copied to user space\n", err);
err = -EFAULT;
}
out:
return err < 0 ? err : 0;
}
int aoc_audio_write(struct aoc_alsa_stream *alsa_stream, void *src,
uint32_t count)
{
int err = 0;
struct aoc_service_dev *dev = alsa_stream->dev;
void *tmp;
int avail;
uint32_t block_size;
avail = aoc_ring_bytes_available_to_write(dev->service, AOC_DOWN);
if (unlikely(avail < count)) {
pr_err("ERR: inconsistent write/read pointers, avail = %d, towrite = %u\n",
avail, count);
err = -EFAULT;
goto out;
}
if (alsa_stream->substream) {
tmp = alsa_stream->substream->runtime->dma_area;
block_size = count;
} else {
tmp = alsa_stream->cstream->runtime->buffer;
block_size = alsa_stream->offload_temp_data_buf_size;
}
while (count > 0) {
if (count < block_size)
block_size = count;
if (alsa_stream->cstream)
pr_debug("compr offload, count: %d, blocksize: %d\n", count, block_size);
err = copy_from_user(tmp, src, block_size);
if (err != 0) {
pr_err("ERR: %d bytes not read from user space\n", err);
err = -EFAULT;
goto out;
}
err = aoc_service_write(dev, tmp, block_size, NONBLOCKING);
if (err != block_size) {
pr_err("ERR: unwritten data - %d bytes\n", block_size - err);
err = -EFAULT;
}
count -= block_size;
src += block_size;
}
out:
return err < 0 ? err : 0;
}
/* PCM channel setup ??? */
static int aoc_audio_set_ctls_chan(struct aoc_alsa_stream *alsa_stream,
struct aoc_chip *chip)
{
int err = 0;
int src, dst, i;
pr_debug(" Setting ALSA volume(%d)\n", chip->volume);
if (alsa_stream->substream &&
alsa_stream->substream->stream == SNDRV_PCM_STREAM_CAPTURE)
return 0;
src = alsa_stream->entry_point_idx;
for (i = 0; i < MAX_NUM_OF_SINKS_PER_STREAM; i++) {
dst = alsa_stream->chip->sink_id_list[i];
if (dst == -1)
continue;
err = aoc_audio_volume_set(chip, chip->volume, src, dst);
if (err < 0) {
pr_err("ERR:%d in volume setting\n", err);
goto out;
}
}
out:
return err;
}
int aoc_audio_set_ctls(struct aoc_chip *chip)
{
int i;
int err = 0;
/* change ctls for all substreams */
for (i = 0; i < MAX_NUM_OF_SUBSTREAMS; i++) {
if (chip->avail_substreams & (1 << i)) {
pr_debug(" Setting %llu stream i =%d\n",
chip->avail_substreams, i);
if (!chip->alsa_stream[i]) {
pr_debug(
" No ALSA stream available?! %i:%p (%llu)\n",
i, chip->alsa_stream[i],
chip->avail_substreams);
err = 0;
} else if (aoc_audio_set_ctls_chan(chip->alsa_stream[i],
chip) != 0) {
pr_err("ERR: couldn't set controls for stream %d\n",
i);
err = -EINVAL;
} else
pr_debug("controls set for stream %d\n", i);
}
}
return err;
}
int aoc_audio_set_params(struct aoc_alsa_stream *alsa_stream, uint32_t channels,
uint32_t samplerate, uint32_t bps, bool pcm_float_fmt, int source_mode)
{
int err = 0;
pr_debug("setting channels(%u), samplerate(%u), bits-per-sample(%u)\n", channels,
samplerate, bps);
if (alsa_stream->cstream || alsa_stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
err = aoc_audio_playback_set_params(alsa_stream, channels, samplerate, bps,
pcm_float_fmt, source_mode);
if (err < 0)
pr_err("ERR:%d playback audio param set fails\n", err);
} else {
err = aoc_audio_capture_set_params(alsa_stream, channels, samplerate, bps,
pcm_float_fmt);
if (err < 0) {
pr_err("ERR:%d capture audio param set fails\n", err);
goto exit;
}
}
exit:
return err;
}
int aoc_eraser_aec_reference_set(struct aoc_chip *chip, long aec_input_source)
{
int err;
struct CMD_AUDIO_INPUT_FEEDBACK_SRC_SELECT_REF cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_INPUT_HOTWORD_SELECT_AEC_REF_ID, sizeof(cmd));
cmd.aec_ref_index = 0;
switch (aec_input_source) {
case DEFAULT_PLAYBACK:
case SPEAKER_PLAYBACK:
cmd.aec_ref_index = FEEDBACK_SRC_AEC_SPKR_INPUT_INDEX;
break;
case USB_PLAYBACK:
cmd.aec_ref_index = FEEDBACK_SRC_AEC_USB_INPUT_INDEX;
break;
case BT_PLAYBACK:
cmd.aec_ref_index = FEEDBACK_SRC_AEC_BT_INPUT_INDEX;
break;
default:
pr_err("ERR: Eraser AEC ref source wrong %ld, fall back to default!",
aec_input_source);
cmd.aec_ref_index = FEEDBACK_SRC_AEC_SPKR_INPUT_INDEX;
}
pr_notice("Eraser AEC ref source set as %ld\n", aec_input_source);
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL, chip);
if (err < 0)
pr_err("ERR:%d in eraser aec ref source set!\n", err);
return err;
}
static int aoc_audio_modem_mic_input(struct aoc_chip *chip, int input_cmd, int mic_input_source)
{
int err;
struct CMD_HDR cmd0; /* For modem mic input STOP */
struct CMD_AUDIO_INPUT_MODEM_INPUT_START_2 cmd1;
if (input_cmd == START) {
AocCmdHdrSet(&(cmd1.parent), CMD_AUDIO_INPUT_MODEM_INPUT_START_2_ID, sizeof(cmd1));
cmd1.mic_input_source = mic_input_source;
switch (chip->ft_aec_ref_source) {
case DEFAULT_PLAYBACK:
cmd1.ref_input_source = mic_input_source;
break;
case SPEAKER_PLAYBACK:
cmd1.ref_input_source = MODEM_MIC_INPUT_INDEX;
break;
case USB_PLAYBACK:
cmd1.ref_input_source = MODEM_USB_INPUT_INDEX;
break;
case BT_PLAYBACK:
cmd1.ref_input_source = MODEM_BT_INPUT_INDEX;
break;
default:
pr_err("ERR: AEC ref source wrong %d!", chip->ft_aec_ref_source);
cmd1.ref_input_source = mic_input_source;
}
pr_notice("Modem mic input source: %d, aec ref source: %d\n", cmd1.mic_input_source,
cmd1.ref_input_source);
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd1, sizeof(cmd1), NULL,
chip);
if (err < 0)
pr_err("ERR:%d modem input start fail!\n", err);
} else {
AocCmdHdrSet(&cmd0, CMD_AUDIO_INPUT_MODEM_INPUT_STOP_ID, sizeof(cmd0));
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd0, sizeof(cmd0), NULL,
chip);
if (err < 0)
pr_err("ERR:%d modem input stop fail!\n", err);
}
return err;
}
static int aoc_telephony_mic_close(struct aoc_chip *chip, int mic)
{
int err;
int mic_input_source;
if (chip->telephony_expect_mic != mic) {
pr_info("%s: expect_mic %d and mic %d is not matched\n", __func__,
chip->telephony_expect_mic, mic);
return 0;
}
chip->telephony_expect_mic = NULL_PATH;
if (chip->telephony_curr_mic != NULL_PATH) {
pr_info("close telephony mic - %d\n", mic);
/* Audio capture disabled for modem input */
err = aoc_audio_modem_mic_input(chip, STOP, 0);
if (err < 0)
pr_err("ERR:%d modem input stop fail\n", err);
chip->telephony_curr_mic = NULL_PATH;
}
/* Since the telehpony sink is still active, use the default mic source */
if (chip->telephony_curr_sink != NULL_PATH) {
pr_info("%s: telephony_curr_sink - %d\n", __func__, chip->telephony_curr_sink);
mic_input_source = hw_id_to_phone_mic_source(DEFAULT_TELEPHONY_MIC);
err = aoc_audio_modem_mic_input(chip, START, mic_input_source);
if (err < 0)
pr_err("ERR:%d modem input start fail\n", err);
chip->telephony_curr_mic = DEFAULT_TELEPHONY_MIC;
}
return err;
}
static int aoc_telephony_mic_open(struct aoc_chip *chip, int mic)
{
int err;
int mic_input_source;
chip->telephony_expect_mic = mic;
/* The same mic source, ignore update */
if (chip->telephony_curr_mic == mic) {
pr_info("%s: same mic source %d\n", __func__, mic);
return 0;
}
/* Different mic source is active, tear down the old one */
if (chip->telephony_curr_mic != NULL_PATH) {
pr_info("%s: disable mic - %d\n", __func__, chip->telephony_curr_mic);
err = aoc_audio_modem_mic_input(chip, STOP, 0);
if (err < 0)
pr_err("ERR:%d modem input stop fail\n", err);
chip->telephony_curr_mic = NULL_PATH;
}
mic_input_source = hw_id_to_phone_mic_source(mic);
/* Update mask before start capture */
if (mic_input_source == MODEM_MIC_INPUT_INDEX ||
mic_input_source == MODEM_INCALL_INPUT_INDEX)
aoc_audio_mic_mask_set(chip, true);
pr_info("open telephony mic: %d - %d\n", mic_input_source, mic);
if (mic_input_source != NULL_PATH) {
err = aoc_audio_modem_mic_input(chip, START, mic_input_source);
if (err < 0)
pr_err("ERR:%d modem input start fail\n", err);
}
chip->telephony_curr_mic = mic;
return err;
}
static int aoc_telephony_sink_close(struct aoc_chip *chip, int sink)
{
int err;
int sink_id;
if (chip->telephony_expect_sink != sink) {
pr_info("%s: expect_sink %d and sink %d is not matched\n", __func__,
chip->telephony_expect_sink, sink);
return 0;
}
chip->telephony_expect_sink = NULL_PATH;
if (chip->telephony_curr_sink != NULL_PATH) {
sink_id = hw_id_to_sink(sink);
pr_info("close telephony sink: %d - %d\n", sink_id, sink);
if (sink_id != NULL_PATH) {
/* Audio playback disabled for modem output */
err = aoc_audio_path_bind(8, sink_id, STOP, chip);
if (err < 0)
pr_err("ERR:%d Telephony Downlink unbind fail\n", err);
}
chip->telephony_curr_sink = NULL_PATH;
}
/* No mic active requirement, disable the default mic source */
if (chip->telephony_curr_mic != NULL_PATH &&
chip->telephony_expect_mic == NULL_PATH) {
pr_info("%s: disable mic - %d\n", __func__, chip->telephony_curr_mic);
err = aoc_audio_modem_mic_input(chip, STOP, 0);
if (err < 0)
pr_err("ERR:%d modem input stop fail\n", err);
chip->telephony_curr_mic = NULL_PATH;
}
return err;
}
static int aoc_telephony_sink_open(struct aoc_chip *chip, int sink)
{
int err;
int sink_id;
chip->telephony_expect_sink = sink;
/* If there's sink active already on then, disable it first */
if (chip->telephony_curr_sink != NULL_PATH) {
sink_id = hw_id_to_sink(chip->telephony_curr_sink);
pr_info("%s: reset previous sink id %d", __func__, sink_id);
if (sink_id != NULL_PATH) {
/* Audio playback disabled for modem output */
err = aoc_audio_path_bind(8, sink_id, STOP, chip);
if (err < 0)
pr_err("ERR:%d Telephony Downlink unbind fail\n", err);
}
chip->telephony_curr_sink = NULL_PATH;
}
/* Since DSP requires mic source active before the sink is active,
* use the default mic source if no mic source is active on then
*/
if (chip->telephony_curr_mic == NULL_PATH) {
int mic_input_source;
mic_input_source = hw_id_to_phone_mic_source(DEFAULT_TELEPHONY_MIC);
pr_info("%s: use default mic source: %d - %d\n", __func__,
mic_input_source, DEFAULT_TELEPHONY_MIC);
err = aoc_audio_modem_mic_input(chip, START, mic_input_source);
if (err < 0)
pr_err("ERR:%d modem input start fail\n", err);
chip->telephony_curr_mic = DEFAULT_TELEPHONY_MIC;
}
sink_id = hw_id_to_sink(sink);
pr_info("open telephony sink id: %d - %d\n", sink_id, sink);
if (sink_id != NULL_PATH) {
/* Audio playback enabled for momdem output */
err = aoc_audio_path_bind(8, sink_id, START, chip);
if (err < 0)
pr_err("ERR:%d Telephony Downlink bind fail\n", err);
}
chip->telephony_curr_sink = sink;
return err;
}
int aoc_phonecall_path_open(struct aoc_chip *chip, int src, int dst, bool capture)
{
int err;
pr_info("Open phone call path - src:%d, dst:%d\n", src, dst);
if (!chip->voice_call_audio_enable) {
pr_info("phone call audio NOT enabled\n");
return 0;
}
if (dst < 0 || dst >= ARRAY_SIZE(chip->voice_path_vote)) {
pr_warn("%s: invalid dst %d\n", __func__, dst);
return -EINVAL;
}
if (chip->voice_path_vote[dst])
return 0;
chip->voice_path_vote[dst] = true;
if (chip->voip_path_vote[dst]) {
pr_info("%s: voip already votes %d\n", __func__, dst);
return 0;
}
if (capture) {
pr_info("voice call mic input: %d\n", dst);
err = aoc_telephony_mic_open(chip, dst);
if (err < 0)
pr_err("ERR:%d modem input start fail\n", err);
} else {
pr_info("voice call sink: %d\n", dst);
/* Audio playback enabled for momdem output */
err = aoc_telephony_sink_open(chip, dst);
if (err < 0)
pr_err("ERR:%d Telephony Downlink bind fail\n", err);
}
return err;
}
int aoc_phonecall_path_close(struct aoc_chip *chip, int src, int dst, bool capture)
{
int err;
pr_info("close phone call path - src:%d, dst:%d\n", src, dst);
if (!chip->voice_call_audio_enable) {
pr_info("phone call audio NOT enabled\n");
return 0;
}
if (dst < 0 || dst >= ARRAY_SIZE(chip->voice_path_vote)) {
pr_warn("%s: invalid dst %d\n", __func__, dst);
return -EINVAL;
}
if (!chip->voice_path_vote[dst])
return 0;
chip->voice_path_vote[dst] = false;
if (chip->voip_path_vote[dst]) {
pr_info("%s: voip still needs %d\n", __func__, dst);
return 0;
}
if (capture) {
/* Audio capture disabled for modem input */
err = aoc_telephony_mic_close(chip, dst);
if (err < 0) {
pr_err("ERR:%d modem input stop fail\n", err);
goto exit;
}
} else {
/* Audio playback disabled for modem output */
err = aoc_telephony_sink_close(chip, dst);
if (err < 0)
pr_err("ERR:%d Telephony Downlink unbind fail\n", err);
}
exit:
return err;
}
int aoc_voipcall_path_open(struct aoc_chip *chip, int src, int dst, bool capture)
{
int err;
pr_info("Open voip call path - src:%d, dst:%d\n", src, dst);
if (!chip->voice_call_audio_enable) {
pr_info("phone call audio NOT enabled\n");
return 0;
}
if (dst < 0 || dst >= ARRAY_SIZE(chip->voip_path_vote)) {
pr_warn("%s: invalid dst %d\n", __func__, dst);
return -EINVAL;
}
if (chip->voip_path_vote[dst])
return 0;
chip->voip_path_vote[dst] = true;
if (chip->voice_path_vote[dst]) {
pr_info("%s: voice already votes %d\n", __func__, dst);
return 0;
}
if (capture) {
pr_info("voip call mic input: %d\n", dst);
err = aoc_telephony_mic_open(chip, dst);
if (err < 0)
pr_err("ERR:%d modem input start fail\n", err);
} else {
pr_info("voice call sink: %d\n", dst);
/* Audio playback enabled for momdem output */
err = aoc_telephony_sink_open(chip, dst);
if (err < 0) {
pr_err("ERR:%d Telephony Downlink bind fail\n", err);
goto exit;
}
}
exit:
return err;
}
int aoc_voipcall_path_close(struct aoc_chip *chip, int src, int dst, bool capture)
{
int err;
pr_info("close voip call path - src:%d, dst:%d\n", src, dst);
if (!chip->voice_call_audio_enable) {
pr_info("phone call audio NOT enabled\n");
return 0;
}
if (dst < 0 || dst >= ARRAY_SIZE(chip->voip_path_vote)) {
pr_warn("%s: invalid dst %d\n", __func__, dst);
return -EINVAL;
}
if (!chip->voip_path_vote[dst])
return 0;
chip->voip_path_vote[dst] = false;
if (chip->voice_path_vote[dst]) {
pr_info("%s: voice still needs %d\n", __func__, dst);
return 0;
}
if (capture) {
/* Audio capture disabled for modem input */
err = aoc_telephony_mic_close(chip, dst);
if (err < 0) {
pr_err("ERR:%d modem input stop fail\n", err);
}
} else {
/* Audio playback disabled for modem output */
err = aoc_telephony_sink_close(chip, dst);
if (err < 0)
pr_err("ERR:%d Telephony Downlink unbind fail\n", err);
}
return err;
}
static int aoc_modem_voip_control(struct aoc_chip *chip, int cmd_id)
{
int err;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL, cmd_id, chip);
if (err < 0)
pr_err("ERR:%d modem/voip start or stop fail!\n", err);
return err;
}
static int aoc_modem_start(struct aoc_chip *chip)
{
int cmd_id = CMD_AUDIO_OUTPUT_TELEPHONY_MODEM_START_ID;
return aoc_modem_voip_control(chip, cmd_id);
}
static int aoc_modem_stop(struct aoc_chip *chip)
{
int cmd_id = CMD_AUDIO_OUTPUT_TELEPHONY_MODEM_STOP_ID;
return aoc_modem_voip_control(chip, cmd_id);
}
static int aoc_voip_start(struct aoc_chip *chip)
{
int cmd_id = CMD_AUDIO_OUTPUT_TELEPHONY_VOIP_START_ID;
return aoc_modem_voip_control(chip, cmd_id);
}
static int aoc_voip_stop(struct aoc_chip *chip)
{
int cmd_id = CMD_AUDIO_OUTPUT_TELEPHONY_VOIP_STOP_ID;
return aoc_modem_voip_control(chip, cmd_id);
}
/* TODO: entry point idx and sink id should be specified in the alsa_stream */
int prepare_phonecall(struct aoc_alsa_stream *alsa_stream)
{
int err;
int src = alsa_stream->entry_point_idx;
/* TODO: check ptrs */
if (!alsa_stream->chip->voice_call_audio_enable) {
pr_info("phone call audio NOT enabled\n");
return 0;
}
pr_debug("prepare phone call - dev %d\n", alsa_stream->entry_point_idx);
if (src != 4)
return 0;
/* Binding modem to start audio flow */
err = aoc_modem_start(alsa_stream->chip);
if (err < 0)
pr_err("ERR:%d Telephony modem start fail\n", err);
modem_voice_call_notify_event(MODEM_VOICE_CALL_ON, NULL);
return err;
}
int teardown_phonecall(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
int src = alsa_stream->entry_point_idx;
if (!alsa_stream->chip->voice_call_audio_enable)
return 0;
pr_info("stop phone call - dev %d\n", alsa_stream->entry_point_idx);
if (src != 4)
return 0;
/* Unbinding modem to stop audio flow */
err = aoc_modem_stop(alsa_stream->chip);
if (err < 0)
pr_err("ERR:%d Telephony modem stop fail\n", err);
modem_voice_call_notify_event(MODEM_VOICE_CALL_OFF, NULL);
return err;
}
int prepare_voipcall(struct aoc_alsa_stream *alsa_stream)
{
int err;
struct aoc_chip *chip = alsa_stream->chip;
/* TODO: check ptrs */
if (!chip->voice_call_audio_enable) {
pr_info("voip call audio NOT enabled\n");
return 0;
}
if (alsa_stream->substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
chip->voip_tx_prepared = true;
if (chip->voip_rx_prepared)
return 0;
} else {
chip->voip_rx_prepared = true;
if (chip->voip_tx_prepared)
return 0;
}
pr_debug("prepare voip call - dev %d\n", alsa_stream->entry_point_idx);
err = aoc_voip_start(chip);
if (err < 0)
pr_err("ERR:%d Telephony voip start fail\n", err);
return err;
}
int teardown_voipcall(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
struct aoc_chip *chip = alsa_stream->chip;
if (!chip->voice_call_audio_enable)
return 0;
if (alsa_stream->substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
chip->voip_tx_prepared = false;
if (chip->voip_rx_prepared)
return 0;
} else {
chip->voip_rx_prepared = false;
if (chip->voip_tx_prepared)
return 0;
}
pr_info("stop voip call - dev %d\n", alsa_stream->entry_point_idx);
err = aoc_voip_stop(chip);
if (err < 0)
pr_err("ERR:%d Telephony voip stop fail\n", err);
return err;
}
int aoc_compr_offload_setup(struct aoc_alsa_stream *alsa_stream, int type)
{
int err;
struct CMD_AUDIO_OUTPUT_DECODER_CFG cmd;
/* TODO: refactor may be needed for passing codec info from HAL to AoC */
AocCmdHdrSet(&(cmd.parent),
alsa_stream->gapless_offload_enable?
CMD_AUDIO_OUTPUT_DECODER_CFG_GAPLESS_ID:
CMD_AUDIO_OUTPUT_DECODER_CFG_ID,
sizeof(cmd));
/* TODO: HAL only passes MP3 or AAC, need to consider/test other AAC options */
cmd.cfg.format = (type == SND_AUDIOCODEC_MP3) ? AUDIO_OUTPUT_DECODER_MP3 :
AUDIO_OUTPUT_DECODER_AAC_LC;
cmd.cfg.samplerate = alsa_stream->params_rate;
cmd.cfg.channels = alsa_stream->channels;
cmd.address = 0;
cmd.size = 0;
pr_info("%s type=%d format=%d sr=%d chan=%d\n", __func__, type, cmd.cfg.format,
cmd.cfg.samplerate, cmd.cfg.channels);
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL,
alsa_stream->chip);
if (err < 0) {
pr_err("ERR:%d in set compress offload codec\n", err);
return err;
}
return 0;
}
int aoc_compr_offload_send_metadata(struct aoc_alsa_stream *alsa_stream)
{
int err;
struct CMD_AUDIO_OUTPUT_DECODER_GAPLESS_METADATA cmd;
if (!alsa_stream->gapless_offload_enable)
return 0;
if (!alsa_stream->send_metadata)
return 0;
if (alsa_stream->compr_padding == COMPR_INVALID_METADATA ||
alsa_stream->compr_delay == COMPR_INVALID_METADATA) {
pr_warn("invalid metadata\n");
return 0;
}
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_DECODER_GAPLESS_METADATA_ID, sizeof(cmd));
cmd.curr_track_padding_frames = alsa_stream->compr_padding;
cmd.curr_track_delay_frames = alsa_stream->compr_delay;
pr_info("send metadata, padding %d, delay %d\n", cmd.curr_track_padding_frames,
cmd.curr_track_delay_frames);
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), NULL,
alsa_stream->chip);
if (err < 0) {
pr_err("ERR:%d in set aoc compress offload in partial drain state\n", err);
return err;
}
alsa_stream->send_metadata = 0;
return 0;
}
int aoc_compr_offload_partial_drain(struct aoc_alsa_stream *alsa_stream)
{
int err;
if (!alsa_stream->gapless_offload_enable)
return 0;
pr_info("compress offload partial drain\n");
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL,
CMD_AUDIO_OUTPUT_DECODER_GAPLESS_PARTIAL_DRAIN_ID, alsa_stream->chip);
if (err < 0)
pr_err("ERR:%d compress offload partial drain!\n", err);
return err;
}
int aoc_compr_offload_close(struct aoc_alsa_stream *alsa_stream)
{
int err;
if (!alsa_stream->gapless_offload_enable)
return 0;
pr_info("compress offload decoder de-inited\n");
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL,
CMD_AUDIO_OUTPUT_DECODER_GAPLESS_DEINIT_ID, alsa_stream->chip);
if (err < 0) {
pr_err("ERR:%d in compress offload close\n", err);
return err;
}
return 0;
}
int aoc_compr_offload_get_io_samples(struct aoc_alsa_stream *alsa_stream, uint64_t *sample)
{
int err;
struct CMD_AUDIO_OUTPUT_GET_EP_SAMPLES cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_GET_EP_TOT_SAMPLES_ID,
sizeof(cmd));
cmd.source = alsa_stream->entry_point_idx;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd,
alsa_stream->chip);
if (err < 0)
pr_err("ERR:%d in getting compress offload io-sample number\n",
err);
else
*sample = cmd.samples;
return err < 0 ? err : 0;
}
int aoc_compr_offload_flush_buffer(struct aoc_alsa_stream *alsa_stream)
{
int err;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL,
CMD_AUDIO_OUTPUT_DECODE_FLUSH_RB_ID, alsa_stream->chip);
if (err < 0)
pr_err("ERR:%d flush compress offload buffer fail!\n", err);
return err;
}
static int aoc_compr_reset_gain_and_delay(struct aoc_alsa_stream *alsa_stream)
{
int err = 0;
int cmd_id = CMD_AUDIO_OUTPUT_DEC_RESET_CURRENT_GAIN_ID;
struct aoc_chip *chip = alsa_stream->chip;
err = aoc_audio_control_simple_cmd(CMD_OUTPUT_CHANNEL, cmd_id, chip);
if (err < 0) {
pr_err("ERR:%d in aoc compr reset gain\n", err);
return err;
}
/* To allow the reset finished in aoc */
msleep(COMPR_OFFLOAD_GAIN_RESET_TIME_DELAY_IN_MSECS);
return 0;
}
int aoc_compr_pause(struct aoc_alsa_stream *alsa_stream)
{
int err;
/* reset the gain in aoc for compr offload playback and then wait for 10 ms */
err = aoc_compr_reset_gain_and_delay(alsa_stream);
if (err < 0)
pr_err("ERR:%d aoc compr reset gain ramp fail\n", err);
err = aoc_audio_stop(alsa_stream);
if (err < 0)
pr_err("ERR:%d aoc_compr_pause fail\n", err);
return 0;
}
int aoc_compr_resume(struct aoc_alsa_stream *alsa_stream)
{
int err;
err = aoc_audio_start(alsa_stream);
if (err < 0)
pr_err("ERR:%d aoc_compr_resume fail\n", err);
return 0;
}
int aoc_audio_setup(struct aoc_alsa_stream *alsa_stream)
{
return 0;
}
int aoc_audio_open(struct aoc_alsa_stream *alsa_stream)
{
return 0;
}
int aoc_audio_close(struct aoc_alsa_stream *alsa_stream)
{
struct aoc_chip *chip = alsa_stream->chip;
struct snd_pcm_substream *substream = alsa_stream->substream;
/* To deal with recording with spatial module enabled */
if (substream && substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
if (alsa_stream->idx == UC_ULTRASONIC_RECORD)
ap_record_stop(chip, alsa_stream);
else if (ap_filter_capture_stream(alsa_stream)) {
/* Stop the capturing mic*/
if (aoc_audio_capture_active_stream_num(chip) == 0) {
pr_info("%s: record stop\n", __func__);
ap_record_stop(chip, alsa_stream);
}
}
}
return 0;
}
static void print_enc_param(struct AUDIO_OUTPUT_BT_A2DP_ENC_CFG *enc_cfg)
{
int i;
pr_info("codecType = %x\n", enc_cfg->codecType);
pr_info("bitrate = %x\n", enc_cfg->bitrate);
pr_info("peerMTU = %x\n", enc_cfg->peerMTU);
for (i = 0; i < 6; i++)
pr_info(" params[%d] = %x\n", i, enc_cfg->params[i]);
}
int aoc_a2dp_set_enc_param(struct aoc_chip *chip, struct AUDIO_OUTPUT_BT_A2DP_ENC_CFG *cfg)
{
int err = 0;
struct CMD_AUDIO_OUTPUT_BT_A2DP_ENC_CFG cmd;
AocCmdHdrSet(&(cmd.parent), CMD_AUDIO_OUTPUT_BT_A2DP_ENC_CFG_ID, sizeof(cmd));
memcpy(&cmd.bt_a2dp_enc_cfg, cfg, sizeof(*cfg));
print_enc_param(&cmd.bt_a2dp_enc_cfg);
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd, sizeof(cmd), (uint8_t *)&cmd,
chip);
if (err < 0)
pr_err("ERR:%d set enc parameter failed\n", err);
return err;
}
int aoc_audio_us_record(struct aoc_chip *chip, bool enable)
{
int cmd_id, err = 0;
cmd_id = enable ? CMD_AUDIO_INPUT_DIRECT_ULTRASONIC_CAPTURE_ENABLE_ID :
CMD_AUDIO_INPUT_DIRECT_ULTRASONIC_CAPTURE_DISABLE_ID;
err = aoc_audio_control_simple_cmd(CMD_INPUT_CHANNEL, cmd_id, chip);
if (err < 0)
pr_err("ERR:%d in ultra sonic record %s control\n", err, enable ? "start" : "stop");
return err;
}
int aoc_audio_set_chirp_parameter(struct aoc_chip *chip, int key, int value)
{
int err;
struct CMD_AUDIO_OUTPUT_SET_PARAMETER cmd;
AocCmdHdrSet(&cmd.parent, CMD_AUDIO_OUTPUT_SET_PARAMETER_ID,
sizeof(cmd));
cmd.block = AOC_CHIRP_BLOCK;
cmd.key = key;
cmd.val = value;
err = aoc_audio_control(CMD_OUTPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0)
pr_err("ERR:%d in AoC Set Chirp Parameter, key: %d\n", err, key);
return err < 0 ? err : 0;
}
int aoc_audio_set_chre_src_pdm_gain(struct aoc_chip *chip, int gain)
{
if (chip->chre_supported) {
int err;
struct CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN cmd;
AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_PDM_GAIN_ID,
sizeof(cmd));
cmd.gain_centibel = gain;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0)
pr_err("ERR:%d in AoC Set CHRE PDM gain\n", err);
return err < 0 ? err : 0;
} else {
pr_err("WARN: setting CHRE PDM gain is not supported\n");
return 0;
}
}
int aoc_audio_set_chre_src_aec_gain(struct aoc_chip *chip, int gain)
{
if (chip->chre_supported) {
int err;
struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN cmd;
AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_GAIN_ID,
sizeof(cmd));
cmd.gain_centibel = gain;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0)
pr_err("ERR:%d in AoC Set CHRE AEC gain\n", err);
return err < 0 ? err : 0;
} else {
pr_err("WARN: setting CHRE AEC gain is not supported\n");
return 0;
}
}
int aoc_audio_set_chre_src_aec_timeout(struct aoc_chip *chip, int timeout)
{
if (chip->chre_supported) {
int err;
struct CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT cmd;
AocCmdHdrSet(&cmd.parent, CMD_AUDIO_INPUT_SET_CHRE_SRC_AEC_TIMEOUT_ID,
sizeof(cmd));
cmd.timeout_ms = timeout;
err = aoc_audio_control(CMD_INPUT_CHANNEL, (uint8_t *)&cmd,
sizeof(cmd), (uint8_t *)&cmd, chip);
if (err < 0)
pr_err("ERR:%d in AoC Set CHRE timeout\n", err);
return err < 0 ? err : 0;
} else {
pr_err("WARN: setting CHRE AEC gain is not supported\n");
return 0;
}
}
/* Update PDM mic mask */
int aoc_audio_mic_mask_set(struct aoc_chip *chip, bool is_voice)
{
uint32_t value = UINT_MAX;
uint8_t *mask = (uint8_t *)(&value);
int key = is_voice, i;
const int cmd_id = CMD_AUDIO_INPUT_SET_PARAMETER_ID;
const int block = 139; /* ABLOCK_INPUT_PDM_MIC */
const int component = ASP_ID_NONE;
const int total_lists = min(NUM_OF_BUILTIN_MIC, (int)sizeof(uint32_t));
for (i = 0; i < total_lists; i++)
mask[i] = chip->buildin_mic_id_list[i];
return aoc_audio_set_parameters(cmd_id, block, component, key, value, chip);
}