Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e1e7188 authored by Mangesh Kunchamwar's avatar Mangesh Kunchamwar
Browse files

dsp: afe: Add support for VAD in AFE native driver



Update to AFE native driver to send VAD calibration
to DSP. Add support for per service API version query
in q6core driver.

Change-Id: I6234879054e7b5622a40912da16072fd8dbd83fe
Signed-off-by: default avatarMangesh Kunchamwar <mangeshk@codeaurora.org>
parent cd74176d
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -92,11 +92,16 @@ ifdef CONFIG_SND_SOC_MSM_QDSP6V2_INTF
	Q6_OBJS += msm_audio_ion.o
	Q6_OBJS += avtimer.o
	Q6_OBJS += q6_init.o

endif
ifdef CONFIG_DTS_SRS_TM
	Q6_OBJS += msm-dts-srs-tm-config.o
endif

ifdef CONFIG_AFE_HWDEP
	Q6_OBJS += q6afecal-hwdep.o
endif

ifdef CONFIG_MSM_ADSP_LOADER
ADSP_LOADER_OBJS += adsp-loader.o
endif
+228 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <dsp/msm-audio-event-notify.h>
#include <ipc/apr_tal.h>
#include "adsp_err.h"
#include "q6afecal-hwdep.h"

#define WAKELOCK_TIMEOUT	5000
enum {
@@ -126,6 +127,10 @@ struct afe_ctl {
	int dev_acdb_id[AFE_MAX_PORTS];
	routing_cb rt_cb;
	struct audio_uevent_data *uevent_data;
	/* cal info for AFE */
	struct afe_fw_info *fw_data;
	u32 island_mode[AFE_MAX_PORTS];
	struct vad_config vad_cfg[AFE_MAX_PORTS];
};

static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX];
@@ -1810,6 +1815,148 @@ static int afe_send_port_topology_id(u16 port_id)

}


static int afe_get_island_mode(u16 port_id, u32 *island_mode)
{
	int ret = 0;
	int index = 0;
	*island_mode = 0;

	index = q6audio_get_port_index(port_id);
	if (index < 0 || index >= AFE_MAX_PORTS) {
		pr_err("%s: AFE port index[%d] invalid!\n",
				__func__, index);
		return -EINVAL;
	}

	*island_mode = this_afe.island_mode[index];
	return ret;
}

/*
 * afe_send_port_island_mode -
 *         for sending island mode to AFE
 *
 * @port_id: AFE port id number
 *
 * Returns 0 on success or error on failure.
 */
int afe_send_port_island_mode(u16 port_id)
{
	struct afe_param_id_island_cfg_t island_cfg;
	struct param_hdr_v3 param_info;
	u32 island_mode = 0;
	int ret = 0;

	memset(&island_cfg, 0, sizeof(island_cfg));
	memset(&param_info, 0, sizeof(param_info));

	ret = afe_get_island_mode(port_id, &island_mode);
	if (ret) {
		pr_err("%s: AFE port[%d] get island mode is invalid!\n",
				__func__, port_id);
		return ret;
	}
	param_info.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
	param_info.instance_id = INSTANCE_ID_0;
	param_info.param_id = AFE_PARAM_ID_ISLAND_CONFIG;
	param_info.param_size = sizeof(island_cfg);

	island_cfg.island_cfg_minor_version = AFE_API_VERSION_ISLAND_CONFIG;
	island_cfg.island_enable = island_mode;

	ret = q6afe_pack_and_set_param_in_band(port_id,
					       q6audio_get_port_index(port_id),
					       param_info, (u8 *) &island_cfg);
	if (ret) {
		pr_err("%s: AFE set island mode enable for port 0x%x failed %d\n",
			__func__, port_id, ret);
		return ret;
	}
	pr_debug("%s: AFE set island mode 0x%x  enable for port 0x%x ret %d\n",
			__func__, island_mode, port_id, ret);
	return ret;
}
EXPORT_SYMBOL(afe_send_port_island_mode);

static int afe_get_vad_preroll_cfg(u16 port_id, u32 *preroll_cfg)
{
	int ret = 0;
	int index = 0;
	*preroll_cfg = 0;

	index = q6audio_get_port_index(port_id);
	if (index < 0 || index >= AFE_MAX_PORTS) {
		pr_err("%s: AFE port index[%d] invalid!\n",
				__func__, index);
		return -EINVAL;
	}

	*preroll_cfg = this_afe.vad_cfg[index].pre_roll;
	return ret;
}

static int afe_send_port_vad_cfg_params(u16 port_id)
{
	struct afe_param_id_vad_cfg_t vad_cfg;
	struct param_hdr_v3 param_info;
	u32 pre_roll_cfg = 0;
	struct firmware_cal *hwdep_cal = NULL;
	int ret = 0;

	memset(&vad_cfg, 0, sizeof(vad_cfg));
	memset(&param_info, 0, sizeof(param_info));

	ret = afe_get_vad_preroll_cfg(port_id, &pre_roll_cfg);
	if (ret) {
		pr_err("%s: AFE port[%d] get preroll cfg is invalid!\n",
				__func__, port_id);
		return ret;
	}
	param_info.module_id = AFE_MODULE_VAD;
	param_info.instance_id = INSTANCE_ID_0;
	param_info.param_id = AFE_PARAM_ID_VAD_CFG;
	param_info.param_size = sizeof(vad_cfg);

	vad_cfg.vad_cfg_minor_version = AFE_API_VERSION_VAD_CFG;
	vad_cfg.pre_roll_in_ms = pre_roll_cfg;

	ret = q6afe_pack_and_set_param_in_band(port_id,
					       q6audio_get_port_index(port_id),
					       param_info, (u8 *) &vad_cfg);
	if (ret) {
		pr_err("%s: AFE set vad cfg for port 0x%x failed %d\n",
			__func__, port_id, ret);
		return ret;
	}

	memset(&param_info, 0, sizeof(param_info));

	hwdep_cal = q6afecal_get_fw_cal(this_afe.fw_data, Q6AFE_VAD_CORE_CAL);
	if (!hwdep_cal) {
		pr_err("%s: error in retrieving vad core calibration",
			__func__);
		return -EINVAL;
	}

	param_info.module_id = AFE_MODULE_VAD;
	param_info.instance_id = INSTANCE_ID_0;
	param_info.param_id = AFE_PARAM_ID_VAD_CORE_CFG;
	param_info.param_size = hwdep_cal->size;

	ret = q6afe_pack_and_set_param_in_band(port_id,
					q6audio_get_port_index(port_id),
					param_info, (u8 *) hwdep_cal->data);
	if (ret) {
		pr_err("%s: AFE set vad cfg for port 0x%x failed %d\n",
			__func__, port_id, ret);
		return ret;
	}
	pr_debug("%s: AFE set preroll cfg %d vad core cfg  port 0x%x ret %d\n",
			__func__, pre_roll_cfg, port_id, ret);
	return ret;
}

static int remap_cal_data(struct cal_block_data *cal_block, int cal_index)
{
	int ret = 0;
@@ -2804,6 +2951,44 @@ void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode)
}
EXPORT_SYMBOL(afe_set_cal_mode);

/**
 * afe_set_vad_cfg -
 *         set configuration for VAD
 *
 * @port_id: AFE port id number
 * @vad_enable: enable/disable vad
 * @preroll_config: Preroll configuration
 *
 */
void afe_set_vad_cfg(u32 vad_enable, u32 preroll_config,
		     u32 port_id)
{
	uint16_t port_index;

	port_index = afe_get_port_index(port_id);
	this_afe.vad_cfg[port_index].is_enable = vad_enable;
	this_afe.vad_cfg[port_index].pre_roll = preroll_config;
}
EXPORT_SYMBOL(afe_set_vad_cfg);

/**
 * afe_set_island_mode_cfg -
 *         set island mode configuration
 *
 * @port_id: AFE port id number
 * @enable_flag: Enable or Disable
 *
 */
void afe_set_island_mode_cfg(u16 port_id, u32 enable_flag)
{
	uint16_t port_index;

	port_index = afe_get_port_index(port_id);
	this_afe.island_mode[port_index] = enable_flag;

}
EXPORT_SYMBOL(afe_set_island_mode_cfg);

/**
 * afe_set_routing_callback -
 *         Update callback function for routing
@@ -3245,8 +3430,21 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
	}

	mutex_lock(&this_afe.afe_cmd_lock);
	/* Also send the topology id here: */
	port_index = afe_get_port_index(port_id);

	if (q6core_get_avcs_api_version_per_service(
		APRV2_IDS_SERVICE_ID_ADSP_AFE_V) >= AFE_API_VERSION_V4) {
		/* send VAD configuration if is enabled */
		if (this_afe.vad_cfg[port_index].is_enable) {
			ret = afe_send_port_vad_cfg_params(port_id);
			if (ret)
				pr_err("%s: afe send VAD config failed %d\n",
					__func__, ret);
				goto fail_cmd;
		}
	}

	/* Also send the topology id here: */
	if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) {
		/* One time call: only for first time */
		afe_send_custom_topology();
@@ -7354,6 +7552,9 @@ int __init afe_init(void)
		this_afe.afe_cal_mode[i] = AFE_CAL_MODE_DEFAULT;
		this_afe.afe_sample_rates[i] = 0;
		this_afe.dev_acdb_id[i] = 0;
		this_afe.island_mode[i] = 0;
		this_afe.vad_cfg[i].is_enable = 0;
		this_afe.vad_cfg[i].pre_roll = 0;
		init_waitqueue_head(&this_afe.wait[i]);
	}
	wakeup_source_init(&wl.ws, "spkr-prot");
@@ -7394,3 +7595,29 @@ void afe_exit(void)
	mutex_destroy(&this_afe.afe_cmd_lock);
	wakeup_source_trash(&wl.ws);
}

/*
 * afe_cal_init_hwdep -
 *        Initiliaze AFE HW dependent Node
 *
 * @card: pointer to sound card
 *
 */
int afe_cal_init_hwdep(void *card)
{
	int ret = 0;

	this_afe.fw_data = kzalloc(sizeof(*(this_afe.fw_data)),
				      GFP_KERNEL);
	if (!this_afe.fw_data)
		return -ENOMEM;

	set_bit(Q6AFE_VAD_CORE_CAL, this_afe.fw_data->cal_bit);
	ret = q6afe_cal_create_hwdep(this_afe.fw_data, Q6AFE_HWDEP_NODE, card);
	if (ret < 0) {
		pr_err("%s: couldn't create hwdep for AFE %d\n", __func__, ret);
		return ret;
	}
	return ret;
}
EXPORT_SYMBOL(afe_cal_init_hwdep);

dsp/q6afecal-hwdep.c

0 → 100644
+235 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, 2017 - 2018 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/ioctl.h>
#include <sound/hwdep.h>
#include <sound/msmcal-hwdep.h>
#include <sound/soc.h>
#include "q6afecal-hwdep.h"

const int cal_size_info[Q6AFE_MAX_CAL] = {
	[Q6AFE_VAD_CORE_CAL] = 132,
};

const char *cal_name_info[Q6AFE_MAX_CAL] = {
	[Q6AFE_VAD_CORE_CAL] = "vad_core",
};

#define AFE_HW_NAME_LENGTH 40

/*
 * q6afecal_get_fw_cal -
 *        To get calibration from AFE HW dependent node
 *
 * @fw_data: pointer to firmware data
 * type: AFE calibration type
 *
 */
struct firmware_cal *q6afecal_get_fw_cal(struct afe_fw_info *fw_data,
					enum q6afe_cal_type type)
{
	if (!fw_data) {
		pr_err("%s: fw_data is NULL\n", __func__);
		return NULL;
	}
	if (type >= Q6AFE_MAX_CAL ||
		type < Q6AFE_MIN_CAL) {
		pr_err("%s: wrong cal type sent %d\n", __func__, type);
		return NULL;
	}
	mutex_lock(&fw_data->lock);
	if (!test_bit(Q6AFECAL_RECEIVED,
		&fw_data->q6afecal_state[type])) {
		pr_err("%s: cal not sent by userspace %d\n",
			__func__, type);
		mutex_unlock(&fw_data->lock);
		return NULL;
	}
	set_bit(Q6AFECAL_INITIALISED, &fw_data->q6afecal_state[type]);
	mutex_unlock(&fw_data->lock);
	return fw_data->fw[type];
}
EXPORT_SYMBOL(q6afecal_get_fw_cal);

static int q6afecal_hwdep_ioctl_shared(struct snd_hwdep *hw,
			struct q6afecal_ioctl_buffer fw_user)
{
	struct afe_fw_info *fw_data = hw->private_data;
	struct firmware_cal **fw = fw_data->fw;
	void *data;

	if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
		pr_err("%s: q6afe didn't set this %d!!\n",
				__func__, fw_user.cal_type);
		return -EFAULT;
	}
	if (fw_user.cal_type >= Q6AFE_MAX_CAL ||
		fw_user.cal_type < Q6AFE_MIN_CAL) {
		pr_err("%s: wrong cal type sent %d\n",
				__func__, fw_user.cal_type);
		return -EFAULT;
	}
	if (fw_user.size > cal_size_info[fw_user.cal_type] ||
		fw_user.size <= 0) {
		pr_err("%s: incorrect firmware size %d for %s\n",
			__func__, fw_user.size,
			cal_name_info[fw_user.cal_type]);
		return -EFAULT;
	}
	data = fw[fw_user.cal_type]->data;
	if (copy_from_user(data, fw_user.buffer, fw_user.size))
		return -EFAULT;
	fw[fw_user.cal_type]->size = fw_user.size;
	mutex_lock(&fw_data->lock);
	set_bit(Q6AFECAL_RECEIVED, &fw_data->q6afecal_state[fw_user.cal_type]);
	mutex_unlock(&fw_data->lock);
	return 0;
}

#ifdef CONFIG_COMPAT
struct q6afecal_ioctl_buffer32 {
	u32 size;
	compat_uptr_t buffer;
	enum q6afe_cal_type cal_type;
};

enum {
	SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
		_IOW('U', 0x1, struct q6afecal_ioctl_buffer32),
};

static int q6afecal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
		unsigned int cmd, unsigned long arg)
{
	struct q6afecal_ioctl_buffer __user *argp = (void __user *)arg;
	struct q6afecal_ioctl_buffer32 fw_user32;
	struct q6afecal_ioctl_buffer fw_user_compat;

	if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
		pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
		return -ENOIOCTLCMD;
	}
	if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
		pr_err("%s: failed to copy\n", __func__);
		return -EFAULT;
	}
	fw_user_compat.size = fw_user32.size;
	fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
	fw_user_compat.cal_type = fw_user32.cal_type;
	return q6afecal_hwdep_ioctl_shared(hw, fw_user_compat);
}
#else
#define q6afecal_hwdep_ioctl_compat NULL
#endif

static int q6afecal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
		unsigned int cmd, unsigned long arg)
{
	struct q6afecal_ioctl_buffer __user *argp = (void __user *)arg;
	struct q6afecal_ioctl_buffer fw_user;

	if (cmd != SNDRV_IOCTL_HWDEP_VAD_CAL_TYPE) {
		pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
		return -ENOIOCTLCMD;
	}
	if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
		pr_err("%s: failed to copy\n", __func__);
		return -EFAULT;
	}
	return q6afecal_hwdep_ioctl_shared(hw, fw_user);
}

static int q6afecal_hwdep_release(struct snd_hwdep *hw, struct file *file)
{
	struct afe_fw_info *fw_data = hw->private_data;

	mutex_lock(&fw_data->lock);
	/* clear all the calibrations */
	memset(fw_data->q6afecal_state, 0,
		sizeof(fw_data->q6afecal_state));
	mutex_unlock(&fw_data->lock);
	return 0;
}

/**
 * q6afe_cal_create_hwdep -
 *         for creating HW dependent node for AFE
 *
 * @data: Pointer to hold fw data
 * @node: node type
 * @card: Pointer to sound card
 *
 */
int q6afe_cal_create_hwdep(void *data, int node, void *card)
{
	char hwname[AFE_HW_NAME_LENGTH];
	struct snd_hwdep *hwdep;
	struct firmware_cal **fw;
	struct afe_fw_info *fw_data = data;
	int err, cal_bit;

	if (!fw_data || !card) {
		pr_err("%s: Invalid parameters\n", __func__);
		return -EINVAL;
	}

	fw = fw_data->fw;
	snprintf(hwname, strlen("Q6AFE"), "Q6AFE");
	err = snd_hwdep_new(((struct snd_soc_card *)card)->snd_card,
			    hwname, node, &hwdep);
	if (err < 0) {
		pr_err("%s: new hwdep for q6afe failed %d\n", __func__, err);
		return err;
	}
	snprintf(hwdep->name, strlen("Q6AFECAL"), "Q6AFECAL");
	hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE;
	hwdep->private_data = fw_data;
	hwdep->ops.ioctl_compat = q6afecal_hwdep_ioctl_compat;
	hwdep->ops.ioctl = q6afecal_hwdep_ioctl;
	hwdep->ops.release = q6afecal_hwdep_release;
	mutex_init(&fw_data->lock);

	for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) {
		set_bit(Q6AFECAL_UNINITIALISED,
				&fw_data->q6afecal_state[cal_bit]);
		fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
		if (!fw[cal_bit]) {
			pr_err("%s: no memory for %s cal\n",
				__func__, cal_name_info[cal_bit]);
			goto end;
		}
	}
	for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) {
		fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
						GFP_KERNEL);
		if (!fw[cal_bit]->data)
			goto exit;
		set_bit(Q6AFECAL_INITIALISED,
			&fw_data->q6afecal_state[cal_bit]);
	}
	return 0;
exit:
	for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) {
		kfree(fw[cal_bit]->data);
		fw[cal_bit]->data = NULL;
	}
end:
	for_each_set_bit(cal_bit, fw_data->cal_bit, Q6AFE_MAX_CAL) {
		kfree(fw[cal_bit]);
		fw[cal_bit] = NULL;
	}
	return -ENOMEM;
}
EXPORT_SYMBOL(q6afe_cal_create_hwdep);

dsp/q6afecal-hwdep.h

0 → 100644
+53 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014,2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#ifndef __Q6AFECAL_HWDEP_H__
#define __Q6AFECAL_HWDEP_H__
#include <sound/msmcal-hwdep.h>

enum q6afe_cal_states {
	Q6AFECAL_UNINITIALISED,
	Q6AFECAL_INITIALISED,
	Q6AFECAL_RECEIVED
};

struct afe_fw_info {
	struct firmware_cal *fw[Q6AFE_MAX_CAL];
	DECLARE_BITMAP(cal_bit, Q6AFE_MAX_CAL);
	/* for calibration tracking */
	unsigned long q6afecal_state[Q6AFE_MAX_CAL];
	struct mutex lock;
};

struct firmware_cal {
	u8 *data;
	size_t size;
};

#if IS_ENABLED(CONFIG_AFE_HWDEP)
int q6afe_cal_create_hwdep(void *fw, int node, void *card);
struct firmware_cal *q6afecal_get_fw_cal(struct afe_fw_info *fw_data,
					enum q6afe_cal_type type);
#else /* CONFIG_AFE_HWDEP */
static inline int q6afe_cal_create_hwdep(void *fw, int node, void *card)
{
	return 0;
}

static inline struct firmware_cal *q6afecal_get_fw_cal(
					struct afe_fw_info *fw_data,
					enum q6afe_cal_type type)
{
	return NULL;
}
#endif /* CONFIG_AFE_HWDEP */
#endif /* __Q6AFECAL_HWDEP_H__ */
+45 −2
Original line number Diff line number Diff line
@@ -513,10 +513,9 @@ int q6core_get_service_version(uint32_t service_id,
}
EXPORT_SYMBOL(q6core_get_service_version);

size_t q6core_get_fwk_version_size(uint32_t service_id)
static int q6core_get_avcs_fwk_version(void)
{
	int ret = 0;
	uint32_t num_services;

	mutex_lock(&(q6core_lcl.ver_lock));
	pr_debug("%s: q6core_avcs_ver_info.status(%d)\n", __func__,
@@ -547,7 +546,15 @@ size_t q6core_get_fwk_version_size(uint32_t service_id)
		break;
	}
	mutex_unlock(&(q6core_lcl.ver_lock));
	return ret;
}

size_t q6core_get_fwk_version_size(uint32_t service_id)
{
	int ret = 0;
	uint32_t num_services;

	ret = q6core_get_avcs_fwk_version();
	if (ret)
		goto done;

@@ -570,6 +577,42 @@ size_t q6core_get_fwk_version_size(uint32_t service_id)
}
EXPORT_SYMBOL(q6core_get_fwk_version_size);

/**
 * q6core_get_avcs_version_per_service -
 *       to get api version of a particular service
 *
 * @service_id: id of the service
 *
 * Returns valid version on success or error (negative value) on failure
 */
int q6core_get_avcs_api_version_per_service(uint32_t service_id)
{
	struct avcs_fwk_ver_info *cached_ver_info = NULL;
	int i;
	uint32_t num_services;
	int ret = 0;

	if (service_id == AVCS_SERVICE_ID_ALL)
		return -EINVAL;

	ret = q6core_get_avcs_fwk_version();
	if (ret < 0) {
		pr_err("%s: failure in getting AVCS version\n", __func__);
		return ret;
	}

	cached_ver_info = q6core_lcl.q6core_avcs_ver_info.ver_info;
	num_services = cached_ver_info->avcs_fwk_version.num_services;

	for (i = 0; i < num_services; i++) {
		if (cached_ver_info->services[i].service_id == service_id)
			return cached_ver_info->services[i].api_version;
	}
	pr_err("%s: No service matching service ID %d\n", __func__, service_id);
	return -EINVAL;
}
EXPORT_SYMBOL(q6core_get_avcs_api_version_per_service);

/**
 * core_set_license -
 *       command to set license for module
Loading