Add 'qcom/opensource/audio-kernel/' from commit '0ee387dfadf349618494d6f82ec8cec796ebef70'
git-subtree-dir: qcom/opensource/audio-kernel git-subtree-mainline:99ab089c55
git-subtree-split:0ee387dfad
Change-Id: repo: https://git.codelinaro.org/clo/la/platform/vendor/qcom/opensource/audio-kernel-ar tag: AUDIO.LA.9.0.r1-07400-lanai.0
This commit is contained in:
272
qcom/opensource/audio-kernel/dsp/Kbuild
Normal file
272
qcom/opensource/audio-kernel/dsp/Kbuild
Normal file
@@ -0,0 +1,272 @@
|
||||
# We can build either as part of a standalone Kernel build or as
|
||||
# an external module. Determine which mechanism is being used
|
||||
ifeq ($(MODNAME), )
|
||||
KERNEL_BUILD := 1
|
||||
else
|
||||
KERNEL_BUILD := 0
|
||||
endif
|
||||
|
||||
ifeq ($(KERNEL_BUILD), 1)
|
||||
# These are configurable via Kconfig for kernel-based builds
|
||||
# Need to explicitly configure for Android-based builds
|
||||
AUDIO_BLD_DIR := $(shell pwd)/kernel/msm-5.4
|
||||
AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SND_SOC_AUTO), y)
|
||||
ifdef CONFIG_SND_SOC_SA8155
|
||||
include $(AUDIO_ROOT)/config/sa8155auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa8155autoconf.h
|
||||
endif
|
||||
ifdef CONFIG_SND_SOC_SA6155
|
||||
include $(AUDIO_ROOT)/config/sa6155auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa6155autoconf.h
|
||||
endif
|
||||
ifdef CONFIG_SND_SOC_GVM
|
||||
include $(AUDIO_ROOT)/config/gvmauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/gvmautoconf.h
|
||||
endif
|
||||
ifdef CONFIG_SND_SOC_SA7255
|
||||
include $(AUDIO_ROOT)/config/sa7255auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa7255autoconf.h
|
||||
endif
|
||||
else
|
||||
ifeq ($(KERNEL_BUILD), 0)
|
||||
ifeq ($(CONFIG_ARCH_SM6150), y)
|
||||
ifdef CONFIG_SND_SOC_SA6155
|
||||
include $(AUDIO_ROOT)/config/sa6155auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa6155autoconf.h
|
||||
else
|
||||
include $(AUDIO_ROOT)/config/sm6150auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h
|
||||
endif
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_TRINKET), y)
|
||||
include $(AUDIO_ROOT)/config/sm6150auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_KONA), y)
|
||||
include $(AUDIO_ROOT)/config/konaauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/konaautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_WAIPIO), y)
|
||||
include $(AUDIO_ROOT)/config/waipioauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/waipioautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_KALAMA), y)
|
||||
include $(AUDIO_ROOT)/config/kalamaauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/kalamaautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_PINEAPPLE), y)
|
||||
include $(AUDIO_ROOT)/config/pineappleauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/pineappleautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_PITTI), y)
|
||||
include $(AUDIO_ROOT)/config/pittiauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/pittiautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_LITO), y)
|
||||
include $(AUDIO_ROOT)/config/litoauto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_KHAJE), y)
|
||||
include $(AUDIO_ROOT)/config/bengalauto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/bengalautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_HOLI), y)
|
||||
include $(AUDIO_ROOT)/config/holiauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/holiautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_BLAIR), y)
|
||||
include $(AUDIO_ROOT)/config/holiauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/holiautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_SM8150), y)
|
||||
ifdef CONFIG_SND_SOC_SA8155
|
||||
include $(AUDIO_ROOT)/config/sa8155auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa8155autoconf.h
|
||||
else
|
||||
include $(AUDIO_ROOT)/config/sm8150auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h
|
||||
endif
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_SDMSHRIKE), y)
|
||||
ifdef CONFIG_SND_SOC_SA8155
|
||||
include $(AUDIO_ROOT)/config/sa8155auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sa8155autoconf.h
|
||||
else
|
||||
include $(AUDIO_ROOT)/config/sm8150auto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/sm8150autoconf.h
|
||||
endif
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_QCS405), y)
|
||||
include $(AUDIO_ROOT)/config/qcs405auto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/qcs405autoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_QTI_QUIN_GVM), y)
|
||||
include $(AUDIO_ROOT)/config/gvmauto.conf
|
||||
INCS += -include $(AUDIO_ROOT)/config/gvmautoconf.h
|
||||
endif
|
||||
ifeq ($(CONFIG_ARCH_SDXLEMUR), y)
|
||||
include $(AUDIO_ROOT)/config/sdxlemurauto.conf
|
||||
export
|
||||
INCS += -include $(AUDIO_ROOT)/config/sdxlemurautoconf.h
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# As per target team, build is done as follows:
|
||||
# Defconfig : build with default flags
|
||||
# Slub : defconfig + CONFIG_SLUB_DEBUG := y +
|
||||
# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y
|
||||
# Perf : Using appropriate msmXXXX-perf_defconfig
|
||||
#
|
||||
# Shipment builds (user variants) should not have any debug feature
|
||||
# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds
|
||||
# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since
|
||||
# there is no other way to identify defconfig builds, QTI internal
|
||||
# representation of perf builds (identified using the string 'perf'),
|
||||
# is used to identify if the build is a slub or defconfig one. This
|
||||
# way no critical debug feature will be enabled for perf and shipment
|
||||
# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT
|
||||
# config.
|
||||
|
||||
############ UAPI ############
|
||||
UAPI_DIR := uapi/audio/
|
||||
UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR)
|
||||
|
||||
############ COMMON ############
|
||||
COMMON_DIR := include
|
||||
COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR)
|
||||
|
||||
############ QDSP6V2 ############
|
||||
|
||||
ifdef CONFIG_SND_SOC_MSM_QDSP6V2_INTF
|
||||
Q6_OBJS += msm-audio-event-notify.o
|
||||
Q6_OBJS += q6_init.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SND_SOC_MSM_QDSP6V2_VM
|
||||
Q6_OBJS += msm-audio-event-notify.o
|
||||
Q6_OBJS += msm_audio_ion_vm.o
|
||||
Q6_OBJS += q6_init.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MSM_AVTIMER
|
||||
Q6_OBJS += avtimer.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_XT_LOGGING
|
||||
Q6_OBJS += sp_params.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MSM_ADSP_LOADER
|
||||
ADSP_LOADER_OBJS += adsp-loader.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MSM_QDSP6_PDR
|
||||
QDSP6_PDR_OBJS += audio_pdr.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_MSM_QDSP6_NOTIFIER
|
||||
QDSP6_NOTIFIER_OBJS += audio_notifier.o audio_ssr.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SPF_CORE
|
||||
SPF_CORE_OBJS += spf-core.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_AUDIO_PRM
|
||||
AUDIO_PRM += audio_prm.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_AUDIO_PKT_ION
|
||||
AUDIO_PKT_ION += msm_audio_ion.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_VOICE_MHI
|
||||
VOICE_MHI += voice_mhi.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_DIGITAL_CDC_RSC_MGR
|
||||
SPF_CORE_OBJS += digital-cdc-rsc-mgr.o
|
||||
endif
|
||||
|
||||
|
||||
LINUX_INC += -Iinclude/linux
|
||||
|
||||
INCS += $(COMMON_INC) \
|
||||
$(UAPI_INC)
|
||||
|
||||
EXTRA_CFLAGS += $(INCS)
|
||||
|
||||
|
||||
CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \
|
||||
-DANI_LITTLE_BIT_ENDIAN \
|
||||
-DDOT11F_LITTLE_ENDIAN_HOST \
|
||||
-DANI_COMPILER_TYPE_GCC \
|
||||
-DANI_OS_TYPE_ANDROID=6 \
|
||||
-DPTT_SOCK_SVC_ENABLE \
|
||||
-Wall\
|
||||
-Werror\
|
||||
-D__linux__
|
||||
|
||||
KBUILD_CPPFLAGS += $(CDEFINES)
|
||||
|
||||
# Currently, for versions of gcc which support it, the kernel Makefile
|
||||
# is disabling the maybe-uninitialized warning. Re-enable it for the
|
||||
# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it
|
||||
# will override the kernel settings.
|
||||
ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
|
||||
EXTRA_CFLAGS += -Wmaybe-uninitialized
|
||||
endif
|
||||
#EXTRA_CFLAGS += -Wmissing-prototypes
|
||||
|
||||
ifeq ($(call cc-option-yn, -Wheader-guard),y)
|
||||
EXTRA_CFLAGS += -Wheader-guard
|
||||
endif
|
||||
|
||||
# If the module name is not "wlan", then the define MULTI_IF_NAME to be the
|
||||
# same a the QCA CHIP name. The host driver will then append MULTI_IF_NAME to
|
||||
# any string that must be unique for all instances of the driver on the system.
|
||||
# This allows multiple instances of the driver with different module names.
|
||||
# If the module name is wlan, leave MULTI_IF_NAME undefined and the code will
|
||||
# treat the driver as the primary driver.
|
||||
ifneq ($(MODNAME), qdsp6v2)
|
||||
CHIP_NAME ?= $(MODNAME)
|
||||
CDEFINES += -DMULTI_IF_NAME=\"$(CHIP_NAME)\"
|
||||
endif
|
||||
|
||||
|
||||
obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += q6_dlkm.o
|
||||
q6_dlkm-y := $(Q6_OBJS)
|
||||
|
||||
obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_VM) += q6_dlkm.o
|
||||
q6_dlkm-y := $(Q6_OBJS)
|
||||
|
||||
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp_loader_dlkm.o
|
||||
adsp_loader_dlkm-y := $(ADSP_LOADER_OBJS)
|
||||
|
||||
obj-$(CONFIG_MSM_QDSP6_PDR) += q6_pdr_dlkm.o
|
||||
q6_pdr_dlkm-y := $(QDSP6_PDR_OBJS)
|
||||
|
||||
obj-$(CONFIG_SPF_CORE) += spf_core_dlkm.o
|
||||
spf_core_dlkm-y := $(SPF_CORE_OBJS)
|
||||
|
||||
obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += q6_notifier_dlkm.o
|
||||
q6_notifier_dlkm-y := $(QDSP6_NOTIFIER_OBJS)
|
||||
|
||||
obj-$(CONFIG_AUDIO_PRM) += audio_prm_dlkm.o
|
||||
audio_prm_dlkm-y := $(AUDIO_PRM)
|
||||
|
||||
obj-$(CONFIG_AUDIO_PKT_ION) += audpkt_ion_dlkm.o
|
||||
audpkt_ion_dlkm-y := $(AUDIO_PKT_ION)
|
||||
|
||||
obj-$(CONFIG_VOICE_MHI) += voice_mhi_dlkm.o
|
||||
voice_mhi_dlkm-y := $(VOICE_MHI)
|
||||
|
||||
# inject some build related information
|
||||
DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"
|
6
qcom/opensource/audio-kernel/dsp/Makefile
Normal file
6
qcom/opensource/audio-kernel/dsp/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
modules:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) VERBOSE=1
|
||||
modules_install:
|
||||
$(MAKE) M=$(M) -C $(KERNEL_SRC) modules_install
|
||||
clean:
|
||||
$(MAKE) -C $(KERNEL_SRC) M=$(M) clean
|
585
qcom/opensource/audio-kernel/dsp/adsp-loader.c
Normal file
585
qcom/opensource/audio-kernel/dsp/adsp-loader.c
Normal file
@@ -0,0 +1,585 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2014, 2017-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <dsp/spf-core.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/remoteproc/qcom_rproc.h>
|
||||
|
||||
|
||||
#define Q6_PIL_GET_DELAY_MS 100
|
||||
#define BOOT_CMD 1
|
||||
#define SSR_RESET_CMD 1
|
||||
#define IMAGE_UNLOAD_CMD 0
|
||||
#define MAX_FW_IMAGES 4
|
||||
#define ADSP_LOADER_APM_TIMEOUT_MS 10000
|
||||
|
||||
enum spf_subsys_state {
|
||||
SPF_SUBSYS_DOWN,
|
||||
SPF_SUBSYS_UP,
|
||||
SPF_SUBSYS_LOADED,
|
||||
SPF_SUBSYS_UNKNOWN,
|
||||
};
|
||||
|
||||
static ssize_t adsp_boot_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
static ssize_t adsp_ssr_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
struct adsp_loader_private {
|
||||
void *pil_h;
|
||||
struct kobject *boot_adsp_obj;
|
||||
struct attribute_group *attr_group;
|
||||
char *adsp_fw_name;
|
||||
char *adsp_dtb_name;
|
||||
};
|
||||
|
||||
static struct kobj_attribute adsp_boot_attribute =
|
||||
__ATTR(boot, 0220, NULL, adsp_boot_store);
|
||||
|
||||
static struct kobj_attribute adsp_ssr_attribute =
|
||||
__ATTR(ssr, 0220, NULL, adsp_ssr_store);
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
&adsp_boot_attribute.attr,
|
||||
&adsp_ssr_attribute.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct work_struct adsp_ldr_work;
|
||||
static struct platform_device *adsp_private;
|
||||
static void adsp_loader_unload(struct platform_device *pdev);
|
||||
|
||||
|
||||
static void adsp_load_fw(struct work_struct *adsp_ldr_work)
|
||||
{
|
||||
struct platform_device *pdev = adsp_private;
|
||||
struct adsp_loader_private *priv = NULL;
|
||||
const char *adsp_dt = "qcom,adsp-state";
|
||||
int rc = 0;
|
||||
u32 adsp_state;
|
||||
struct property *prop;
|
||||
int size;
|
||||
phandle rproc_phandle;
|
||||
struct rproc *rproc;
|
||||
|
||||
if (!pdev) {
|
||||
dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Device tree information missing\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev,
|
||||
" %s: Private data get failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: ADSP state = %x\n", __func__, adsp_state);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
prop = of_find_property(pdev->dev.of_node, "qcom,proc-img-to-load",
|
||||
&size);
|
||||
if (!prop) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: loading default image ADSP\n", __func__);
|
||||
goto load_adsp;
|
||||
}
|
||||
|
||||
rproc_phandle = be32_to_cpup(prop->value);
|
||||
priv->pil_h = rproc_get_by_phandle(rproc_phandle);
|
||||
if (!priv->pil_h)
|
||||
goto fail;
|
||||
|
||||
rproc = priv->pil_h;
|
||||
if (!strcmp(rproc->name, "modem")) {
|
||||
if (adsp_state == SPF_SUBSYS_DOWN) {
|
||||
rc = rproc_boot(priv->pil_h);
|
||||
if (IS_ERR(priv->pil_h) || rc) {
|
||||
dev_err(&pdev->dev, "%s: pil get failed,\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
} else if (adsp_state == SPF_SUBSYS_LOADED) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: MDSP state = %x\n", __func__, adsp_state);
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "%s: Q6/MDSP image is loaded\n", __func__);
|
||||
}
|
||||
|
||||
load_adsp:
|
||||
{
|
||||
adsp_state = spf_core_is_apm_ready(ADSP_LOADER_APM_TIMEOUT_MS);
|
||||
if (adsp_state == SPF_SUBSYS_DOWN) {
|
||||
if (!priv->adsp_fw_name) {
|
||||
dev_info(&pdev->dev, "%s: Load default ADSP\n",
|
||||
__func__);
|
||||
} else {
|
||||
dev_info(&pdev->dev, "%s: Load ADSP with fw name %s\n",
|
||||
__func__, priv->adsp_fw_name);
|
||||
rc = rproc_set_firmware(priv->pil_h,
|
||||
priv->adsp_fw_name);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "%s: rproc set firmware failed,\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (!priv->adsp_dtb_name) {
|
||||
dev_info(&pdev->dev, "%s: Load default ADSP DTB\n",
|
||||
__func__);
|
||||
} else {
|
||||
dev_info(&pdev->dev, "%s: Load ADSP DTB with fw name %s\n",
|
||||
__func__, priv->adsp_dtb_name);
|
||||
rc = qcom_rproc_set_dtb_firmware(priv->pil_h,
|
||||
priv->adsp_dtb_name);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "%s: rproc set dtb firmware failed,\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
rc = rproc_boot(priv->pil_h);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "%s: pil get failed,\n",
|
||||
__func__);
|
||||
goto fail;
|
||||
}
|
||||
} else if (adsp_state == SPF_SUBSYS_LOADED) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: ADSP state = %x\n", __func__, adsp_state);
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "%s: Q6/ADSP image is loaded\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
fail:
|
||||
dev_err(&pdev->dev, "%s: Q6 image loading failed\n", __func__);
|
||||
}
|
||||
|
||||
static void adsp_loader_do(struct platform_device *pdev)
|
||||
{
|
||||
schedule_work(&adsp_ldr_work);
|
||||
}
|
||||
|
||||
static ssize_t adsp_ssr_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int ssr_command = 0;
|
||||
struct rproc *adsp_dev = NULL;
|
||||
struct platform_device *pdev = adsp_private;
|
||||
struct adsp_loader_private *priv = NULL;
|
||||
|
||||
dev_dbg(&pdev->dev, "%s: going to call adsp ssr\n ", __func__);
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
if (!priv)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtoint(buf, 10, &ssr_command) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (ssr_command != SSR_RESET_CMD)
|
||||
return -EINVAL;
|
||||
|
||||
adsp_dev = (struct rproc *)priv->pil_h;
|
||||
if (!adsp_dev)
|
||||
return -EINVAL;
|
||||
|
||||
dev_err(&pdev->dev, "requesting for ADSP restart\n");
|
||||
|
||||
rproc_shutdown(adsp_dev);
|
||||
adsp_loader_do(adsp_private);
|
||||
|
||||
dev_dbg(&pdev->dev, "%s :: ADSP restarted\n", __func__);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t adsp_boot_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
int boot = 0;
|
||||
|
||||
if (sscanf(buf, "%du", &boot) != 1) {
|
||||
pr_err("%s: failed to read boot info from string\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (boot == BOOT_CMD) {
|
||||
pr_debug("%s: going to call adsp_loader_do\n", __func__);
|
||||
adsp_loader_do(adsp_private);
|
||||
} else if (boot == IMAGE_UNLOAD_CMD) {
|
||||
pr_debug("%s: going to call adsp_unloader\n", __func__);
|
||||
adsp_loader_unload(adsp_private);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void adsp_loader_unload(struct platform_device *pdev)
|
||||
{
|
||||
struct adsp_loader_private *priv = NULL;
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
if (priv->pil_h) {
|
||||
rproc_shutdown(priv->pil_h);
|
||||
}
|
||||
}
|
||||
|
||||
static int adsp_loader_init_sysfs(struct platform_device *pdev)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct adsp_loader_private *priv = NULL;
|
||||
|
||||
adsp_private = NULL;
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
priv->pil_h = NULL;
|
||||
priv->boot_adsp_obj = NULL;
|
||||
priv->attr_group = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*(priv->attr_group)),
|
||||
GFP_KERNEL);
|
||||
if (!priv->attr_group) {
|
||||
ret = -ENOMEM;
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
priv->attr_group->attrs = attrs;
|
||||
|
||||
priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj);
|
||||
if (!priv->boot_adsp_obj) {
|
||||
dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
|
||||
__func__, ret);
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
adsp_private = pdev;
|
||||
|
||||
return 0;
|
||||
|
||||
error_return:
|
||||
|
||||
if (priv->boot_adsp_obj) {
|
||||
kobject_del(priv->boot_adsp_obj);
|
||||
priv->boot_adsp_obj = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adsp_loader_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct adsp_loader_private *priv = NULL;
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
|
||||
if (!priv)
|
||||
return 0;
|
||||
|
||||
if (priv->pil_h) {
|
||||
rproc_shutdown(priv->pil_h);
|
||||
priv->pil_h = NULL;
|
||||
}
|
||||
|
||||
if (priv->boot_adsp_obj) {
|
||||
sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group);
|
||||
kobject_del(priv->boot_adsp_obj);
|
||||
priv->boot_adsp_obj = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adsp_loader_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct adsp_loader_private *priv = NULL;
|
||||
struct nvmem_cell *cell;
|
||||
size_t len;
|
||||
u32 *buf;
|
||||
const char **adsp_fw_name_array = NULL;
|
||||
const char **adsp_dtb_fw_name_array = NULL;
|
||||
int adsp_fw_cnt;
|
||||
u32* adsp_fw_bit_values = NULL;
|
||||
int i;
|
||||
int fw_name_size;
|
||||
u32 adsp_var_idx = 0;
|
||||
int ret = 0;
|
||||
u32 adsp_fuse_not_supported = 0;
|
||||
const char *adsp_fw_name;
|
||||
const char *adsp_dtb_name;
|
||||
struct property *prop;
|
||||
int size;
|
||||
phandle rproc_phandle;
|
||||
struct rproc *adsp;
|
||||
|
||||
prop = of_find_property(pdev->dev.of_node, "qcom,rproc-handle",
|
||||
&size);
|
||||
if (!prop) {
|
||||
dev_err(&pdev->dev, "Missing remotproc handle\n");
|
||||
return -ENOPARAM;
|
||||
}
|
||||
rproc_phandle = be32_to_cpup(prop->value);
|
||||
adsp = rproc_get_by_phandle(rproc_phandle);
|
||||
if (!adsp) {
|
||||
dev_err(&pdev->dev, "fail to get rproc\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ret = adsp_loader_init_sysfs(pdev);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
|
||||
rproc_put(adsp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
priv->pil_h = adsp;
|
||||
|
||||
/* get adsp variant idx */
|
||||
cell = nvmem_cell_get(&pdev->dev, "adsp_variant");
|
||||
if (IS_ERR_OR_NULL(cell)) {
|
||||
dev_dbg(&pdev->dev, "%s: FAILED to get nvmem cell \n",
|
||||
__func__);
|
||||
|
||||
/*
|
||||
* When ADSP variant read from fuse register is not
|
||||
* supported, check if image with different fw image
|
||||
* name needs to be loaded
|
||||
*/
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"adsp-fuse-not-supported",
|
||||
&adsp_fuse_not_supported);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: adsp_fuse_not_supported prop not found %d\n",
|
||||
__func__, ret);
|
||||
goto wqueue;
|
||||
}
|
||||
|
||||
if (adsp_fuse_not_supported) {
|
||||
/* Read ADSP firmware image name */
|
||||
ret = of_property_read_string(pdev->dev.of_node,
|
||||
"adsp-fw-name",
|
||||
&adsp_fw_name);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "%s: unable to read fw-name\n",
|
||||
__func__);
|
||||
goto wqueue;
|
||||
}
|
||||
|
||||
fw_name_size = strlen(adsp_fw_name) + 1;
|
||||
priv->adsp_fw_name = devm_kzalloc(&pdev->dev,
|
||||
fw_name_size,
|
||||
GFP_KERNEL);
|
||||
if (!priv->adsp_fw_name)
|
||||
goto wqueue;
|
||||
strlcpy(priv->adsp_fw_name, adsp_fw_name,
|
||||
fw_name_size);
|
||||
|
||||
ret = of_property_read_string(pdev->dev.of_node,
|
||||
"adsp-dtb-name",
|
||||
&adsp_dtb_name);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "%s: unable to read fw-dtb-name\n",
|
||||
__func__);
|
||||
goto wqueue;
|
||||
}
|
||||
|
||||
fw_name_size = strlen(adsp_dtb_name) + 1;
|
||||
priv->adsp_dtb_name = devm_kzalloc(&pdev->dev,
|
||||
fw_name_size,
|
||||
GFP_KERNEL);
|
||||
if (!priv->adsp_dtb_name)
|
||||
goto wqueue;
|
||||
strscpy(priv->adsp_dtb_name, adsp_dtb_name,
|
||||
fw_name_size);
|
||||
}
|
||||
goto wqueue;
|
||||
}
|
||||
buf = nvmem_cell_read(cell, &len);
|
||||
nvmem_cell_put(cell);
|
||||
if (IS_ERR_OR_NULL(buf)) {
|
||||
dev_dbg(&pdev->dev, "%s: FAILED to read nvmem cell \n", __func__);
|
||||
goto wqueue;
|
||||
}
|
||||
if (len <= 0 || len > sizeof(u32)) {
|
||||
dev_dbg(&pdev->dev, "%s: nvmem cell length out of range: %d\n",
|
||||
__func__, len);
|
||||
kfree(buf);
|
||||
goto wqueue;
|
||||
}
|
||||
memcpy(&adsp_var_idx, buf, len);
|
||||
dev_info(&pdev->dev, "%s: adsp variant fuse reg value: 0x%x\n",
|
||||
__func__, adsp_var_idx);
|
||||
kfree(buf);
|
||||
|
||||
/* Get count of fw images */
|
||||
adsp_fw_cnt = of_property_count_strings(pdev->dev.of_node,
|
||||
"adsp-fw-names");
|
||||
if (adsp_fw_cnt <= 0 || adsp_fw_cnt > MAX_FW_IMAGES) {
|
||||
dev_dbg(&pdev->dev, "%s: Invalid number of fw images %d",
|
||||
__func__, adsp_fw_cnt);
|
||||
goto wqueue;
|
||||
}
|
||||
|
||||
adsp_fw_bit_values = devm_kzalloc(&pdev->dev,
|
||||
adsp_fw_cnt * sizeof(u32), GFP_KERNEL);
|
||||
if (!adsp_fw_bit_values)
|
||||
goto wqueue;
|
||||
|
||||
/* Read bit values corresponding to each firmware image entry */
|
||||
ret = of_property_read_u32_array(pdev->dev.of_node,
|
||||
"adsp-fw-bit-values",
|
||||
adsp_fw_bit_values,
|
||||
adsp_fw_cnt);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "%s: unable to read fw-bit-values\n",
|
||||
__func__);
|
||||
goto wqueue;
|
||||
}
|
||||
|
||||
adsp_fw_name_array = devm_kzalloc(&pdev->dev,
|
||||
adsp_fw_cnt * sizeof(char *), GFP_KERNEL);
|
||||
if (!adsp_fw_name_array)
|
||||
goto wqueue;
|
||||
|
||||
/* Read ADSP firmware image names */
|
||||
ret = of_property_read_string_array(pdev->dev.of_node,
|
||||
"adsp-fw-names",
|
||||
adsp_fw_name_array,
|
||||
adsp_fw_cnt);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "%s: unable to read fw-names\n",
|
||||
__func__);
|
||||
goto wqueue;
|
||||
}
|
||||
|
||||
adsp_dtb_fw_name_array = devm_kzalloc(&pdev->dev,
|
||||
adsp_fw_cnt * sizeof(char *), GFP_KERNEL);
|
||||
|
||||
/* Read ADSP dtb firmware image names */
|
||||
ret = of_property_read_string_array(pdev->dev.of_node,
|
||||
"adsp-dtb-fw-names",
|
||||
adsp_dtb_fw_name_array,
|
||||
adsp_fw_cnt);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "%s: unable to read adsp-dtb-fw-names\n",
|
||||
__func__);
|
||||
goto wqueue;
|
||||
}
|
||||
|
||||
for (i = 0; i < adsp_fw_cnt; i++) {
|
||||
if (adsp_fw_bit_values[i] == adsp_var_idx) {
|
||||
fw_name_size = strlen(adsp_fw_name_array[i]) + 1;
|
||||
priv->adsp_fw_name = devm_kzalloc(&pdev->dev,
|
||||
fw_name_size,
|
||||
GFP_KERNEL);
|
||||
if (!priv->adsp_fw_name)
|
||||
goto wqueue;
|
||||
strlcpy(priv->adsp_fw_name, adsp_fw_name_array[i],
|
||||
fw_name_size);
|
||||
|
||||
fw_name_size = strlen(adsp_dtb_fw_name_array[i]) + 1;
|
||||
priv->adsp_dtb_name = devm_kzalloc(&pdev->dev,
|
||||
fw_name_size,
|
||||
GFP_KERNEL);
|
||||
if (!priv->adsp_dtb_name)
|
||||
goto wqueue;
|
||||
strscpy(priv->adsp_dtb_name, adsp_dtb_fw_name_array[i],
|
||||
fw_name_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
wqueue:
|
||||
INIT_WORK(&adsp_ldr_work, adsp_load_fw);
|
||||
if (adsp_fw_bit_values)
|
||||
devm_kfree(&pdev->dev, adsp_fw_bit_values);
|
||||
if (adsp_fw_name_array)
|
||||
devm_kfree(&pdev->dev, adsp_fw_name_array);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static const struct of_device_id adsp_loader_dt_match[] = {
|
||||
{ .compatible = "qcom,adsp-loader" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adsp_loader_dt_match);
|
||||
|
||||
static struct platform_driver adsp_loader_driver = {
|
||||
.driver = {
|
||||
.name = "adsp-loader",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = adsp_loader_dt_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = adsp_loader_probe,
|
||||
.remove = adsp_loader_remove,
|
||||
};
|
||||
|
||||
static int __init adsp_loader_init(void)
|
||||
{
|
||||
return platform_driver_register(&adsp_loader_driver);
|
||||
}
|
||||
module_init(adsp_loader_init);
|
||||
|
||||
static void __exit adsp_loader_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&adsp_loader_driver);
|
||||
}
|
||||
module_exit(adsp_loader_exit);
|
||||
|
||||
MODULE_DESCRIPTION("ADSP Loader module");
|
||||
MODULE_LICENSE("GPL v2");
|
726
qcom/opensource/audio-kernel/dsp/audio_notifier.c
Normal file
726
qcom/opensource/audio-kernel/dsp/audio_notifier.c
Normal file
@@ -0,0 +1,726 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2017, 2020-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/remoteproc/qcom_rproc.h>
|
||||
#include <dsp/audio_notifier.h>
|
||||
#include "audio_ssr.h"
|
||||
#include "audio_pdr.h"
|
||||
|
||||
/* Audio states internal to notifier. Client */
|
||||
/* used states defined in audio_notifier.h */
|
||||
/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */
|
||||
#define NO_SERVICE -2
|
||||
#define UNINIT_SERVICE -1
|
||||
|
||||
static bool service_early_down;
|
||||
static struct platform_device *adsp_private;
|
||||
|
||||
struct adsp_notify_private {
|
||||
struct rproc *rproc_h;
|
||||
bool notifier_probe_complete;
|
||||
};
|
||||
|
||||
/*
|
||||
* Used for each client registered with audio notifier
|
||||
*/
|
||||
struct client_data {
|
||||
struct list_head list;
|
||||
/* Notifier block given by client */
|
||||
struct notifier_block *nb;
|
||||
char client_name[20];
|
||||
int service;
|
||||
int domain;
|
||||
};
|
||||
|
||||
/*
|
||||
* Used for each service and domain combination
|
||||
* Tracks information specific to the underlying
|
||||
* service.
|
||||
*/
|
||||
struct service_info {
|
||||
const char name[20];
|
||||
int domain_id;
|
||||
int state;
|
||||
void *handle;
|
||||
/* Hook registered to service */
|
||||
union {
|
||||
void (*cb)(int, char *, void *);
|
||||
struct notifier_block *nb;
|
||||
} hook;
|
||||
/* Used to determine when to register and deregister service */
|
||||
int num_of_clients;
|
||||
/* List of all clients registered to the service and domain */
|
||||
struct srcu_notifier_head client_nb_list;
|
||||
};
|
||||
|
||||
static int audio_notifier_ssr_adsp_cb(struct notifier_block *this,
|
||||
unsigned long opcode, void *data);
|
||||
static int audio_notifier_ssr_modem_cb(struct notifier_block *this,
|
||||
unsigned long opcode, void *data);
|
||||
static void audio_notifier_pdr_adsp_cb(int status, char *service_name, void *priv);
|
||||
|
||||
static struct notifier_block notifier_ssr_adsp_nb = {
|
||||
.notifier_call = audio_notifier_ssr_adsp_cb,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static struct notifier_block notifier_ssr_modem_nb = {
|
||||
.notifier_call = audio_notifier_ssr_modem_cb,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES]
|
||||
[AUDIO_NOTIFIER_MAX_DOMAINS] = {
|
||||
|
||||
{{
|
||||
.name = "SSR_ADSP",
|
||||
.domain_id = AUDIO_SSR_DOMAIN_ADSP,
|
||||
.state = AUDIO_NOTIFIER_SERVICE_DOWN,
|
||||
.hook.nb = ¬ifier_ssr_adsp_nb
|
||||
},
|
||||
{
|
||||
.name = "SSR_MODEM",
|
||||
.domain_id = AUDIO_SSR_DOMAIN_MODEM,
|
||||
.state = AUDIO_NOTIFIER_SERVICE_DOWN,
|
||||
.hook.nb = ¬ifier_ssr_modem_nb
|
||||
} },
|
||||
|
||||
{{
|
||||
.name = "PDR_ADSP",
|
||||
.domain_id = AUDIO_PDR_DOMAIN_ADSP,
|
||||
.state = UNINIT_SERVICE,
|
||||
.hook.cb = &audio_notifier_pdr_adsp_cb
|
||||
},
|
||||
{ /* PDR MODEM service not enabled */
|
||||
.name = "INVALID",
|
||||
.state = NO_SERVICE,
|
||||
.hook.nb = NULL
|
||||
} }
|
||||
};
|
||||
|
||||
/* Master list of all audio notifier clients */
|
||||
LIST_HEAD(client_list);
|
||||
struct mutex notifier_mutex;
|
||||
|
||||
static int audio_notifier_get_default_service(int domain)
|
||||
{
|
||||
int service = NO_SERVICE;
|
||||
|
||||
/* initial service to connect per domain */
|
||||
switch (domain) {
|
||||
case AUDIO_NOTIFIER_ADSP_DOMAIN:
|
||||
service = AUDIO_NOTIFIER_PDR_SERVICE;
|
||||
break;
|
||||
case AUDIO_NOTIFIER_MODEM_DOMAIN:
|
||||
service = AUDIO_NOTIFIER_SSR_SERVICE;
|
||||
break;
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MSM_QDSP6_PDR
|
||||
static void audio_notifier_init_service(int service)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) {
|
||||
if (service_data[service][i].state == UNINIT_SERVICE)
|
||||
service_data[service][i].state =
|
||||
AUDIO_NOTIFIER_SERVICE_DOWN;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void audio_notifier_init_service(int service)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
|
||||
service_data[service][i].state = NO_SERVICE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool audio_notifier_is_service_enabled(int service)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
|
||||
if (service_data[service][i].state != NO_SERVICE)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int audio_notifier_reg_service(int service, int domain)
|
||||
{
|
||||
void *handle;
|
||||
int ret = 0;
|
||||
int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
|
||||
struct platform_device *pdev = adsp_private;
|
||||
struct adsp_notify_private *priv = NULL;
|
||||
struct rproc *rproc;
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
if (!priv) {
|
||||
dev_err_ratelimited(&pdev->dev, " %s: Private data get failed\n", __func__);
|
||||
return ret;;
|
||||
}
|
||||
|
||||
rproc = priv->rproc_h;
|
||||
|
||||
switch (service) {
|
||||
case AUDIO_NOTIFIER_SSR_SERVICE:
|
||||
handle = audio_ssr_register(rproc->name,
|
||||
service_data[service][domain].hook.nb);
|
||||
break;
|
||||
case AUDIO_NOTIFIER_PDR_SERVICE:
|
||||
handle = audio_pdr_service_register(
|
||||
service_data[service][domain].domain_id,
|
||||
service_data[service][domain].hook.cb);
|
||||
|
||||
curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
|
||||
break;
|
||||
default:
|
||||
pr_err_ratelimited("%s: Invalid service %d\n",
|
||||
__func__, service);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (IS_ERR_OR_NULL(handle)) {
|
||||
pr_err_ratelimited("%s: handle is incorrect for service %s\n",
|
||||
__func__, service_data[service][domain].name);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
service_data[service][domain].state = curr_state;
|
||||
service_data[service][domain].handle = handle;
|
||||
|
||||
pr_info("%s: service %s is in use\n",
|
||||
__func__, service_data[service][domain].name);
|
||||
pr_debug("%s: service %s has current state %d, handle 0x%pK\n",
|
||||
__func__, service_data[service][domain].name,
|
||||
service_data[service][domain].state,
|
||||
service_data[service][domain].handle);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audio_notifier_dereg_service(int service, int domain)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (service) {
|
||||
case AUDIO_NOTIFIER_SSR_SERVICE:
|
||||
ret = audio_ssr_deregister(
|
||||
service_data[service][domain].handle,
|
||||
service_data[service][domain].hook.nb);
|
||||
break;
|
||||
case AUDIO_NOTIFIER_PDR_SERVICE:
|
||||
ret = audio_pdr_service_deregister(
|
||||
service_data[service][domain].domain_id);
|
||||
break;
|
||||
default:
|
||||
pr_err_ratelimited("%s: Invalid service %d\n",
|
||||
__func__, service);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (ret < 0) {
|
||||
pr_err_ratelimited("%s: deregister failed for service %s, ret %d\n",
|
||||
__func__, service_data[service][domain].name, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
pr_debug("%s: service %s with handle 0x%pK deregistered\n",
|
||||
__func__, service_data[service][domain].name,
|
||||
service_data[service][domain].handle);
|
||||
|
||||
mutex_lock(¬ifier_mutex);
|
||||
service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN;
|
||||
service_data[service][domain].handle = NULL;
|
||||
mutex_unlock(¬ifier_mutex);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audio_notifier_reg_client_service(struct client_data *client_data,
|
||||
int service)
|
||||
{
|
||||
int ret = 0;
|
||||
int domain = client_data->domain;
|
||||
struct audio_notifier_cb_data data;
|
||||
|
||||
switch (service) {
|
||||
case AUDIO_NOTIFIER_SSR_SERVICE:
|
||||
case AUDIO_NOTIFIER_PDR_SERVICE:
|
||||
if (service_data[service][domain].num_of_clients == 0)
|
||||
ret = audio_notifier_reg_service(service, domain);
|
||||
break;
|
||||
default:
|
||||
pr_err_ratelimited("%s: Invalid service for client %s, service %d, domain %d\n",
|
||||
__func__, client_data->client_name, service, domain);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err_ratelimited("%s: service registration failed on service %s for client %s\n",
|
||||
__func__, service_data[service][domain].name,
|
||||
client_data->client_name);
|
||||
goto done;
|
||||
}
|
||||
|
||||
client_data->service = service;
|
||||
srcu_notifier_chain_register(
|
||||
&service_data[service][domain].client_nb_list,
|
||||
client_data->nb);
|
||||
service_data[service][domain].num_of_clients++;
|
||||
|
||||
pr_debug("%s: registered client %s on service %s, current state 0x%x\n",
|
||||
__func__, client_data->client_name,
|
||||
service_data[service][domain].name,
|
||||
service_data[service][domain].state);
|
||||
|
||||
/*
|
||||
* PDR registration returns current state
|
||||
* Force callback of client with current state for PDR
|
||||
*/
|
||||
if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) {
|
||||
data.service = service;
|
||||
data.domain = domain;
|
||||
(void)client_data->nb->notifier_call(client_data->nb,
|
||||
service_data[service][domain].state, &data);
|
||||
}
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audio_notifier_reg_client(struct client_data *client_data)
|
||||
{
|
||||
int ret = 0;
|
||||
int service;
|
||||
int domain = client_data->domain;
|
||||
|
||||
service = audio_notifier_get_default_service(domain);
|
||||
if (service < 0) {
|
||||
pr_err_ratelimited("%s: service %d is incorrect\n", __func__, service);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Search through services to find a valid one to register client on. */
|
||||
for (; service >= 0; service--) {
|
||||
/* If a service is not initialized, wait for it to come up. */
|
||||
if (service_data[service][domain].state == UNINIT_SERVICE) {
|
||||
pr_err_ratelimited("%s: failed in client registration to PDR\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
/* Skip unsupported service and domain combinations. */
|
||||
if (service_data[service][domain].state < 0)
|
||||
continue;
|
||||
/* Only register clients who have not acquired a service. */
|
||||
if (client_data->service != NO_SERVICE)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Only register clients, who have not acquired a service, on
|
||||
* the best available service for their domain. Uninitialized
|
||||
* services will try to register all of their clients after
|
||||
* they initialize correctly or will disable their service and
|
||||
* register clients on the next best avaialable service.
|
||||
*/
|
||||
pr_debug("%s: register client %s on service %s",
|
||||
__func__, client_data->client_name,
|
||||
service_data[service][domain].name);
|
||||
|
||||
ret = audio_notifier_reg_client_service(client_data, service);
|
||||
if (ret < 0)
|
||||
pr_err_ratelimited("%s: client %s failed to register on service %s",
|
||||
__func__, client_data->client_name,
|
||||
service_data[service][domain].name);
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audio_notifier_dereg_client(struct client_data *client_data)
|
||||
{
|
||||
int ret = 0;
|
||||
int service = client_data->service;
|
||||
int domain = client_data->domain;
|
||||
|
||||
switch (client_data->service) {
|
||||
case AUDIO_NOTIFIER_SSR_SERVICE:
|
||||
case AUDIO_NOTIFIER_PDR_SERVICE:
|
||||
if (service_data[service][domain].num_of_clients == 1)
|
||||
ret = audio_notifier_dereg_service(service, domain);
|
||||
break;
|
||||
case NO_SERVICE:
|
||||
goto done;
|
||||
default:
|
||||
pr_err_ratelimited("%s: Invalid service for client %s, service %d\n",
|
||||
__func__, client_data->client_name,
|
||||
client_data->service);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err_ratelimited("%s: deregister failed for client %s on service %s, ret %d\n",
|
||||
__func__, client_data->client_name,
|
||||
service_data[service][domain].name, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = srcu_notifier_chain_unregister(&service_data[service][domain].
|
||||
client_nb_list, client_data->nb);
|
||||
if (ret < 0) {
|
||||
pr_err_ratelimited("%s: srcu_notifier_chain_unregister failed, ret %d\n",
|
||||
__func__, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
pr_debug("%s: deregistered client %s on service %s\n",
|
||||
__func__, client_data->client_name,
|
||||
service_data[service][domain].name);
|
||||
|
||||
client_data->service = NO_SERVICE;
|
||||
if (service_data[service][domain].num_of_clients > 0)
|
||||
service_data[service][domain].num_of_clients--;
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void audio_notifier_reg_all_clients(void)
|
||||
{
|
||||
struct list_head *ptr, *next;
|
||||
struct client_data *client_data;
|
||||
int ret;
|
||||
|
||||
list_for_each_safe(ptr, next, &client_list) {
|
||||
client_data = list_entry(ptr, struct client_data, list);
|
||||
|
||||
ret = audio_notifier_reg_client(client_data);
|
||||
if (ret < 0)
|
||||
pr_err_ratelimited("%s: audio_notifier_reg_client failed for client %s, \
|
||||
ret %d\n", __func__, client_data->client_name,
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
static int audio_notifier_convert_opcode(unsigned long opcode,
|
||||
unsigned long *notifier_opcode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (opcode) {
|
||||
case QCOM_SSR_BEFORE_SHUTDOWN:
|
||||
case SERVREG_SERVICE_STATE_EARLY_DOWN:
|
||||
*notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
|
||||
if (opcode == SERVREG_SERVICE_STATE_EARLY_DOWN)
|
||||
service_early_down = true;
|
||||
break;
|
||||
case QCOM_SSR_AFTER_POWERUP:
|
||||
case SERVREG_SERVICE_STATE_UP:
|
||||
*notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP;
|
||||
break;
|
||||
case SERVREG_SERVICE_STATE_DOWN:
|
||||
if (!service_early_down)
|
||||
*notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
|
||||
else {
|
||||
/* Reset service_early_down and nothing to do*/
|
||||
service_early_down = false;
|
||||
ret = -EINVAL;
|
||||
pr_info("%s: Early down aleady handled %d\n", __func__,
|
||||
service_early_down);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audio_notifier_service_cb(unsigned long opcode,
|
||||
int service, int domain)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long notifier_opcode;
|
||||
struct audio_notifier_cb_data data;
|
||||
|
||||
if (audio_notifier_convert_opcode(opcode, ¬ifier_opcode) < 0)
|
||||
return NOTIFY_OK;
|
||||
|
||||
data.service = service;
|
||||
data.domain = domain;
|
||||
|
||||
pr_info("%s: service %s, opcode 0x%lx\n",
|
||||
__func__, service_data[service][domain].name, notifier_opcode);
|
||||
|
||||
mutex_lock(¬ifier_mutex);
|
||||
service_data[service][domain].state = notifier_opcode;
|
||||
|
||||
ret = srcu_notifier_call_chain(&service_data[service][domain].
|
||||
client_nb_list, notifier_opcode, &data);
|
||||
mutex_unlock(¬ifier_mutex);
|
||||
if (ret < 0)
|
||||
pr_err_ratelimited("%s: srcu_notifier_call_chain returned %d, service %s, \
|
||||
opcode 0x%lx\n", __func__, ret, service_data[service][domain].name,
|
||||
notifier_opcode);
|
||||
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static void audio_notifier_pdr_adsp_cb(int status, char *service_name, void *priv)
|
||||
{
|
||||
audio_notifier_service_cb(status, AUDIO_NOTIFIER_PDR_SERVICE, AUDIO_NOTIFIER_ADSP_DOMAIN);
|
||||
}
|
||||
|
||||
static int audio_notifier_ssr_adsp_cb(struct notifier_block *this,
|
||||
unsigned long opcode, void *data)
|
||||
{
|
||||
return audio_notifier_service_cb(opcode,
|
||||
AUDIO_NOTIFIER_SSR_SERVICE,
|
||||
AUDIO_NOTIFIER_ADSP_DOMAIN);
|
||||
}
|
||||
|
||||
static int audio_notifier_ssr_modem_cb(struct notifier_block *this,
|
||||
unsigned long opcode, void *data)
|
||||
{
|
||||
return audio_notifier_service_cb(opcode,
|
||||
AUDIO_NOTIFIER_SSR_SERVICE,
|
||||
AUDIO_NOTIFIER_MODEM_DOMAIN);
|
||||
}
|
||||
|
||||
int audio_notifier_deregister(char *client_name)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret2;
|
||||
struct list_head *ptr, *next;
|
||||
struct client_data *client_data = NULL;
|
||||
|
||||
if (client_name == NULL) {
|
||||
pr_err_ratelimited("%s: client_name is NULL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
list_for_each_safe(ptr, next, &client_list) {
|
||||
client_data = list_entry(ptr, struct client_data, list);
|
||||
if (!strcmp(client_name, client_data->client_name)) {
|
||||
ret2 = audio_notifier_dereg_client(client_data);
|
||||
if (ret2 < 0) {
|
||||
pr_err_ratelimited("%s: audio_notifier_dereg_client failed, \
|
||||
ret %d\n, service %d, domain %d",
|
||||
__func__, ret2, client_data->service,
|
||||
client_data->domain);
|
||||
ret = ret2;
|
||||
continue;
|
||||
}
|
||||
list_del(&client_data->list);
|
||||
kfree(client_data);
|
||||
}
|
||||
}
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(audio_notifier_deregister);
|
||||
|
||||
int audio_notifier_register(char *client_name, int domain,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
int ret;
|
||||
struct client_data *client_data;
|
||||
|
||||
if (client_name == NULL) {
|
||||
pr_err_ratelimited("%s: client_name is NULL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
} else if (nb == NULL) {
|
||||
pr_err_ratelimited("%s: Notifier block is NULL\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
client_data = kmalloc(sizeof(*client_data), GFP_KERNEL);
|
||||
if (client_data == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
INIT_LIST_HEAD(&client_data->list);
|
||||
client_data->nb = nb;
|
||||
strlcpy(client_data->client_name, client_name,
|
||||
sizeof(client_data->client_name));
|
||||
client_data->service = NO_SERVICE;
|
||||
client_data->domain = domain;
|
||||
|
||||
mutex_lock(¬ifier_mutex);
|
||||
ret = audio_notifier_reg_client(client_data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(¬ifier_mutex);
|
||||
pr_err_ratelimited("%s: audio_notifier_reg_client for client %s failed ret = %d\n",
|
||||
__func__, client_data->client_name,
|
||||
ret);
|
||||
kfree(client_data);
|
||||
goto done;
|
||||
}
|
||||
list_add_tail(&client_data->list, &client_list);
|
||||
mutex_unlock(¬ifier_mutex);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(audio_notifier_register);
|
||||
|
||||
static int audio_notifier_subsys_init(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
mutex_init(¬ifier_mutex);
|
||||
for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) {
|
||||
for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) {
|
||||
if (service_data[i][j].state <= NO_SERVICE)
|
||||
continue;
|
||||
|
||||
srcu_init_notifier_head(
|
||||
&service_data[i][j].client_nb_list);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_notifier_late_init(void)
|
||||
{
|
||||
/*
|
||||
* If pdr registration failed, register clients on next service
|
||||
* Do in late init to ensure that SSR subsystem is initialized
|
||||
*/
|
||||
mutex_lock(¬ifier_mutex);
|
||||
if (!audio_notifier_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
|
||||
audio_notifier_reg_all_clients();
|
||||
|
||||
mutex_unlock(¬ifier_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool audio_notifier_probe_status(void)
|
||||
{
|
||||
struct adsp_notify_private *priv = NULL;
|
||||
struct platform_device *pdev = NULL;
|
||||
|
||||
if (!adsp_private)
|
||||
goto exit;
|
||||
|
||||
pdev = adsp_private;
|
||||
priv = platform_get_drvdata(pdev);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev," %s: Private data get failed\n", __func__);
|
||||
goto exit;
|
||||
}
|
||||
if (priv->notifier_probe_complete) {
|
||||
dev_dbg(&pdev->dev, "%s: audio notify probe successfully completed\n",
|
||||
__func__);
|
||||
return true;
|
||||
}
|
||||
exit:
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(audio_notifier_probe_status);
|
||||
|
||||
static int audio_notify_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct adsp_notify_private *priv = NULL;
|
||||
struct property *prop;
|
||||
int size;
|
||||
phandle rproc_phandle;
|
||||
|
||||
adsp_private = NULL;
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
priv->notifier_probe_complete = false;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
prop = of_find_property(pdev->dev.of_node, "qcom,rproc-handle", &size);
|
||||
if (!prop) {
|
||||
dev_err(&pdev->dev, "Missing remotproc handle\n");
|
||||
return ret;
|
||||
}
|
||||
rproc_phandle = be32_to_cpup(prop->value);
|
||||
|
||||
priv->rproc_h = rproc_get_by_phandle(rproc_phandle);
|
||||
if (!priv->rproc_h) {
|
||||
dev_info_ratelimited(&pdev->dev, "remotproc handle NULL\n");
|
||||
ret = -EPROBE_DEFER;
|
||||
return ret;
|
||||
}
|
||||
|
||||
adsp_private = pdev;
|
||||
|
||||
audio_notifier_subsys_init();
|
||||
|
||||
audio_notifier_init_service(AUDIO_NOTIFIER_PDR_SERVICE);
|
||||
/* Do not return error since PDR enablement is not critical */
|
||||
audio_notifier_late_init();
|
||||
|
||||
priv->notifier_probe_complete = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int audio_notify_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id adsp_notify_dt_match[] = {
|
||||
{ .compatible = "qcom,adsp-notify" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adsp_notify_dt_match);
|
||||
|
||||
static struct platform_driver adsp_notify_driver = {
|
||||
.driver = {
|
||||
.name = "adsp-notify",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = adsp_notify_dt_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = audio_notify_probe,
|
||||
.remove = audio_notify_remove,
|
||||
};
|
||||
|
||||
static int __init audio_notifier_init(void)
|
||||
{
|
||||
return platform_driver_register(&adsp_notify_driver);
|
||||
}
|
||||
module_init(audio_notifier_init);
|
||||
|
||||
static void __exit audio_notifier_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&adsp_notify_driver);
|
||||
}
|
||||
module_exit(audio_notifier_exit);
|
||||
|
||||
MODULE_SOFTDEP("pre: qcom_q6v5_pas");
|
||||
MODULE_DESCRIPTION("Audio notifier driver");
|
||||
MODULE_LICENSE("GPL v2");
|
65
qcom/opensource/audio-kernel/dsp/audio_pdr.c
Normal file
65
qcom/opensource/audio-kernel/dsp/audio_pdr.c
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include "audio_pdr.h"
|
||||
|
||||
struct audio_pdr_service {
|
||||
void *pdr_handle;
|
||||
char service_name[SERVREG_NAME_LENGTH + 1];
|
||||
char service_path[SERVREG_NAME_LENGTH + 1];
|
||||
};
|
||||
|
||||
static struct audio_pdr_service audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = {
|
||||
{ /* AUDIO_PDR_DOMAIN_ADSP */
|
||||
.service_name = "avs/audio",
|
||||
.service_path = "msm/adsp/audio_pd",
|
||||
}
|
||||
};
|
||||
|
||||
void *audio_pdr_service_register(int domain_id, void (*cb)(int, char *, void *))
|
||||
{
|
||||
if ((domain_id < 0) ||
|
||||
(domain_id >= AUDIO_PDR_DOMAIN_MAX)) {
|
||||
pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
audio_pdr_services[domain_id].pdr_handle = pdr_handle_alloc(cb, NULL);
|
||||
|
||||
return pdr_add_lookup(audio_pdr_services[domain_id].pdr_handle,
|
||||
audio_pdr_services[domain_id].service_name,
|
||||
audio_pdr_services[domain_id].service_path);
|
||||
}
|
||||
EXPORT_SYMBOL(audio_pdr_service_register);
|
||||
|
||||
int audio_pdr_service_deregister(int domain_id)
|
||||
{
|
||||
if ((domain_id < 0) ||
|
||||
(domain_id >= AUDIO_PDR_DOMAIN_MAX)) {
|
||||
pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
pdr_handle_release(audio_pdr_services[domain_id].pdr_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(audio_pdr_service_deregister);
|
||||
|
||||
static int __init audio_pdr_late_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
module_init(audio_pdr_late_init);
|
||||
|
||||
static void __exit audio_pdr_late_exit(void)
|
||||
{
|
||||
|
||||
}
|
||||
module_exit(audio_pdr_late_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PDR framework driver");
|
||||
MODULE_LICENSE("GPL v2");
|
60
qcom/opensource/audio-kernel/dsp/audio_pdr.h
Normal file
60
qcom/opensource/audio-kernel/dsp/audio_pdr.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-2018, 2020 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_PDR_H_
|
||||
#define __AUDIO_PDR_H_
|
||||
|
||||
#include <linux/soc/qcom/pdr.h>
|
||||
|
||||
enum {
|
||||
AUDIO_PDR_DOMAIN_ADSP,
|
||||
AUDIO_PDR_DOMAIN_MAX
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MSM_QDSP6_PDR
|
||||
|
||||
/*
|
||||
* Use audio_pdr_service_register to register with a PDR service
|
||||
* Function should be called after nb callback registered with
|
||||
* audio_pdr_register has been called back with the
|
||||
* AUDIO_PDR_FRAMEWORK_UP ioctl.
|
||||
*
|
||||
* domain_id - Domain to use, example: AUDIO_PDR_ADSP
|
||||
* *cb - Pointer to a callback function that will be notified of the state
|
||||
* of the domain requested. The ioctls received by the callback are
|
||||
* defined in pdr.h.
|
||||
*
|
||||
* Returns: Success: Client handle
|
||||
* Failure: Pointer error code
|
||||
*/
|
||||
void *audio_pdr_service_register(int domain_id, void (*cb)(int, char *, void *));
|
||||
|
||||
/*
|
||||
* Use audio_pdr_service_deregister to deregister with a PDR
|
||||
* service that was registered using the audio_pdr_service_register
|
||||
* API.
|
||||
*
|
||||
* domain_id - Domain to use, example: AUDIO_PDR_ADSP
|
||||
*
|
||||
* Returns: Success: zero
|
||||
* Failure: Error code
|
||||
*/
|
||||
int audio_pdr_service_deregister(int domain_id);
|
||||
|
||||
#else
|
||||
|
||||
static inline void *audio_pdr_service_register(int domain_id, void (*cb)(int, char *, void *))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int audio_pdr_service_deregister(int domain_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MSM_QDSP6_PDR */
|
||||
|
||||
#endif
|
609
qcom/opensource/audio-kernel/dsp/audio_prm.c
Normal file
609
qcom/opensource/audio-kernel/dsp/audio_prm.c
Normal file
@@ -0,0 +1,609 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <ipc/gpr-lite.h>
|
||||
#include <soc/snd_event.h>
|
||||
#include <dsp/audio_prm.h>
|
||||
#include <dsp/spf-core.h>
|
||||
#include <dsp/audio_notifier.h>
|
||||
|
||||
#define TIMEOUT_MS 500
|
||||
#define MAX_RETRY_COUNT 3
|
||||
#define APM_READY_WAIT_DURATION 2
|
||||
#define GPR_SEND_PKT_APM_TIMEOUT_MS 0
|
||||
|
||||
struct audio_prm {
|
||||
struct gpr_device *adev;
|
||||
wait_queue_head_t wait;
|
||||
struct mutex lock;
|
||||
bool resp_received;
|
||||
atomic_t state;
|
||||
atomic_t status;
|
||||
int lpi_pcm_logging_enable;
|
||||
bool is_adsp_up;
|
||||
u32 prm_sleep_api_supported;
|
||||
};
|
||||
|
||||
static struct audio_prm g_prm;
|
||||
|
||||
static bool is_apm_ready_check_done = false;
|
||||
|
||||
static int audio_prm_callback(struct gpr_device *adev, void *data)
|
||||
{
|
||||
struct gpr_hdr *hdr = (struct gpr_hdr *)data;
|
||||
uint32_t *payload = GPR_PKT_GET_PAYLOAD(uint32_t, data);
|
||||
|
||||
//dev_err(&adev->dev, "%s: Payload %x", __func__, hdr->opcode);
|
||||
switch (hdr->opcode) {
|
||||
case GPR_IBASIC_RSP_RESULT:
|
||||
pr_err("%s: Failed response received",__func__);
|
||||
atomic_set(&g_prm.status, payload[1]);
|
||||
g_prm.resp_received = true;
|
||||
break;
|
||||
case PRM_CMD_RSP_REQUEST_HW_RSC:
|
||||
case PRM_CMD_RSP_RELEASE_HW_RSC:
|
||||
/* payload[1] contains the error status for response */
|
||||
if (payload[1] != 0) {
|
||||
atomic_set(&g_prm.status, payload[1]);
|
||||
pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
|
||||
__func__, payload[0], payload[1]);
|
||||
}
|
||||
g_prm.resp_received = true;
|
||||
/* payload[0] contains the param_ID for response */
|
||||
switch (payload[0]) {
|
||||
case PARAM_ID_RSC_AUDIO_HW_CLK:
|
||||
case PARAM_ID_RSC_LPASS_CORE:
|
||||
case PARAM_ID_RSC_HW_CORE:
|
||||
case PARAM_ID_RSC_CPU_LPR:
|
||||
if (payload[1] != 0)
|
||||
pr_err("%s: PRM command failed with error %d\n",
|
||||
__func__, payload[1]);
|
||||
atomic_set(&g_prm.state, payload[1]);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: hit default case",__func__);
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
if (g_prm.resp_received)
|
||||
wake_up(&g_prm.wait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prm_gpr_send_pkt(struct gpr_pkt *pkt, wait_queue_head_t *wait)
|
||||
{
|
||||
int ret = 0;
|
||||
int retry;
|
||||
|
||||
mutex_lock(&g_prm.lock);
|
||||
pr_debug("%s: enter",__func__);
|
||||
|
||||
if (wait)
|
||||
atomic_set(&g_prm.state, 1);
|
||||
atomic_set(&g_prm.status, 0);
|
||||
|
||||
if (g_prm.adev == NULL) {
|
||||
pr_err("%s: apr is unregistered\n", __func__);
|
||||
mutex_unlock(&g_prm.lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!is_apm_ready_check_done && g_prm.is_adsp_up &&
|
||||
(gpr_get_q6_state() == GPR_SUBSYS_LOADED)) {
|
||||
pr_info("%s: apm ready check not done\n", __func__);
|
||||
retry = 0;
|
||||
while (!spf_core_is_apm_ready(GPR_SEND_PKT_APM_TIMEOUT_MS) &&
|
||||
retry < MAX_RETRY_COUNT) {
|
||||
msleep(APM_READY_WAIT_DURATION);
|
||||
++retry;
|
||||
}
|
||||
is_apm_ready_check_done = true;
|
||||
pr_info("%s: apm ready check done\n", __func__);
|
||||
}
|
||||
g_prm.resp_received = false;
|
||||
ret = gpr_send_pkt(g_prm.adev, pkt);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: packet not transmitted %d\n", __func__, ret);
|
||||
mutex_unlock(&g_prm.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (wait) {
|
||||
ret = wait_event_timeout(g_prm.wait,
|
||||
(g_prm.resp_received),
|
||||
msecs_to_jiffies(2 * TIMEOUT_MS));
|
||||
if (!ret) {
|
||||
pr_err("%s: pkt send timeout\n", __func__);
|
||||
ret = -ETIMEDOUT;
|
||||
} else if (atomic_read(&g_prm.status) > 0) {
|
||||
pr_err("%s: DSP returned error %d\n", __func__,
|
||||
atomic_read(&g_prm.status));
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
pr_debug("%s: exit",__func__);
|
||||
mutex_unlock(&g_prm.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void audio_prm_set_lpi_logging_status(int lpi_pcm_logging_enable)
|
||||
{
|
||||
g_prm.lpi_pcm_logging_enable = lpi_pcm_logging_enable;
|
||||
}
|
||||
EXPORT_SYMBOL(audio_prm_set_lpi_logging_status);
|
||||
|
||||
static int _audio_prm_set_lpass_cpu_lpr_req(uint8_t enable)
|
||||
{
|
||||
struct gpr_pkt *pkt;
|
||||
struct prm_cpu_lpr_request_t prm_lpr_request;
|
||||
int ret = 0;
|
||||
uint32_t size;
|
||||
|
||||
size = GPR_HDR_SIZE + sizeof(struct prm_cpu_lpr_request_t);
|
||||
pkt = kzalloc(size, GFP_KERNEL);
|
||||
if (!pkt)
|
||||
return -ENOMEM;
|
||||
|
||||
pkt->hdr.header = GPR_SET_FIELD(GPR_PKT_VERSION, GPR_PKT_VER) |
|
||||
GPR_SET_FIELD(GPR_PKT_HEADER_SIZE, GPR_PKT_HEADER_WORD_SIZE_V) |
|
||||
GPR_SET_FIELD(GPR_PKT_PACKET_SIZE, size);
|
||||
|
||||
pkt->hdr.src_port = GPR_SVC_ASM;
|
||||
pkt->hdr.dst_port = PRM_MODULE_INSTANCE_ID;
|
||||
pkt->hdr.dst_domain_id = GPR_IDS_DOMAIN_ID_ADSP_V;
|
||||
pkt->hdr.src_domain_id = GPR_IDS_DOMAIN_ID_APPS_V;
|
||||
pkt->hdr.token = 0; /* TBD */
|
||||
if (enable)
|
||||
pkt->hdr.opcode = PRM_CMD_REQUEST_HW_RSC;
|
||||
else
|
||||
pkt->hdr.opcode = PRM_CMD_RELEASE_HW_RSC;
|
||||
|
||||
prm_lpr_request.payload_header.payload_address_lsw = 0;
|
||||
prm_lpr_request.payload_header.payload_address_msw = 0;
|
||||
prm_lpr_request.payload_header.mem_map_handle = 0;
|
||||
prm_lpr_request.payload_header.payload_size = sizeof(struct prm_cpu_lpr_request_t) -
|
||||
sizeof(apm_cmd_header_t);
|
||||
|
||||
/** Populate the param payload */
|
||||
prm_lpr_request.module_payload_0.module_instance_id = PRM_MODULE_INSTANCE_ID;
|
||||
prm_lpr_request.module_payload_0.error_code = 0;
|
||||
prm_lpr_request.module_payload_0.param_id = PARAM_ID_RSC_CPU_LPR;
|
||||
prm_lpr_request.module_payload_0.param_size =
|
||||
sizeof(struct prm_cpu_lpr_request_t) -
|
||||
sizeof(apm_cmd_header_t) - sizeof(apm_module_param_data_t);
|
||||
|
||||
|
||||
prm_lpr_request.lpr_state = LPR_CPU_SS_SLEEP_DISABLED;
|
||||
|
||||
memcpy(&pkt->payload, &prm_lpr_request, sizeof(struct prm_cpu_lpr_request_t));
|
||||
|
||||
ret = prm_gpr_send_pkt(pkt, &g_prm.wait);
|
||||
|
||||
kfree(pkt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
int _audio_prm_set_lpass_hw_core_req(uint32_t hw_core_id, uint8_t enable)
|
||||
{
|
||||
struct gpr_pkt *pkt;
|
||||
prm_cmd_request_hw_core_t prm_rsc_request;
|
||||
int ret = 0;
|
||||
uint32_t size;
|
||||
|
||||
size = GPR_HDR_SIZE + sizeof(prm_cmd_request_hw_core_t);
|
||||
pkt = kzalloc(size, GFP_KERNEL);
|
||||
if (!pkt)
|
||||
return -ENOMEM;
|
||||
|
||||
pkt->hdr.header = GPR_SET_FIELD(GPR_PKT_VERSION, GPR_PKT_VER) |
|
||||
GPR_SET_FIELD(GPR_PKT_HEADER_SIZE, GPR_PKT_HEADER_WORD_SIZE_V) |
|
||||
GPR_SET_FIELD(GPR_PKT_PACKET_SIZE, size);
|
||||
|
||||
pkt->hdr.src_port = GPR_SVC_ASM;
|
||||
pkt->hdr.dst_port = PRM_MODULE_INSTANCE_ID;
|
||||
pkt->hdr.dst_domain_id = GPR_IDS_DOMAIN_ID_ADSP_V;
|
||||
pkt->hdr.src_domain_id = GPR_IDS_DOMAIN_ID_APPS_V;
|
||||
pkt->hdr.token = 0; /* TBD */
|
||||
if (enable)
|
||||
pkt->hdr.opcode = PRM_CMD_REQUEST_HW_RSC;
|
||||
else
|
||||
pkt->hdr.opcode = PRM_CMD_RELEASE_HW_RSC;
|
||||
|
||||
prm_rsc_request.payload_header.payload_address_lsw = 0;
|
||||
prm_rsc_request.payload_header.payload_address_msw = 0;
|
||||
prm_rsc_request.payload_header.mem_map_handle = 0;
|
||||
prm_rsc_request.payload_header.payload_size = sizeof(prm_cmd_request_hw_core_t) - sizeof(apm_cmd_header_t);
|
||||
|
||||
/** Populate the param payload */
|
||||
prm_rsc_request.module_payload_0.module_instance_id = PRM_MODULE_INSTANCE_ID;
|
||||
prm_rsc_request.module_payload_0.error_code = 0;
|
||||
prm_rsc_request.module_payload_0.param_id = PARAM_ID_RSC_HW_CORE;
|
||||
prm_rsc_request.module_payload_0.param_size =
|
||||
sizeof(prm_cmd_request_hw_core_t) - sizeof(apm_cmd_header_t) - sizeof(apm_module_param_data_t);
|
||||
|
||||
|
||||
prm_rsc_request.hw_core_id = hw_core_id;
|
||||
|
||||
memcpy(&pkt->payload, &prm_rsc_request, sizeof(prm_cmd_request_hw_core_t));
|
||||
|
||||
ret = prm_gpr_send_pkt(pkt, &g_prm.wait);
|
||||
|
||||
kfree(pkt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int audio_prm_set_lpass_hw_core_req(struct clk_cfg *cfg, uint32_t hw_core_id, uint8_t enable)
|
||||
{
|
||||
|
||||
if (hw_core_id == HW_CORE_ID_LPASS) {
|
||||
if (g_prm.prm_sleep_api_supported)
|
||||
return _audio_prm_set_lpass_cpu_lpr_req(enable);
|
||||
else
|
||||
return _audio_prm_set_lpass_hw_core_req(hw_core_id, enable);
|
||||
}
|
||||
|
||||
return _audio_prm_set_lpass_hw_core_req(hw_core_id, enable);
|
||||
}
|
||||
EXPORT_SYMBOL(audio_prm_set_lpass_hw_core_req);
|
||||
|
||||
/**
|
||||
* prm_set_lpass_clk_cfg() - Set PRM clock
|
||||
*
|
||||
* Return: 0 if clock set is success
|
||||
*/
|
||||
static int audio_prm_set_lpass_clk_cfg_req(struct clk_cfg *cfg)
|
||||
{
|
||||
struct gpr_pkt *pkt;
|
||||
prm_cmd_request_rsc_t prm_rsc_request;
|
||||
int ret = 0;
|
||||
uint32_t size;
|
||||
|
||||
size = GPR_HDR_SIZE + sizeof(prm_cmd_request_rsc_t);
|
||||
pkt = kzalloc(size, GFP_KERNEL);
|
||||
if (!pkt)
|
||||
return -ENOMEM;
|
||||
|
||||
pkt->hdr.header = GPR_SET_FIELD(GPR_PKT_VERSION, GPR_PKT_VER) |
|
||||
GPR_SET_FIELD(GPR_PKT_HEADER_SIZE, GPR_PKT_HEADER_WORD_SIZE_V) |
|
||||
GPR_SET_FIELD(GPR_PKT_PACKET_SIZE, size);
|
||||
|
||||
pkt->hdr.src_port = GPR_SVC_ASM;
|
||||
pkt->hdr.dst_port = PRM_MODULE_INSTANCE_ID;
|
||||
pkt->hdr.dst_domain_id = GPR_IDS_DOMAIN_ID_ADSP_V;
|
||||
pkt->hdr.src_domain_id = GPR_IDS_DOMAIN_ID_APPS_V;
|
||||
pkt->hdr.token = 0; /* TBD */
|
||||
pkt->hdr.opcode = PRM_CMD_REQUEST_HW_RSC;
|
||||
|
||||
//pr_err("%s: clk_id %d size of cmd_req %ld \n",__func__, cfg->clk_id, sizeof(prm_cmd_request_rsc_t));
|
||||
|
||||
prm_rsc_request.payload_header.payload_address_lsw = 0;
|
||||
prm_rsc_request.payload_header.payload_address_msw = 0;
|
||||
prm_rsc_request.payload_header.mem_map_handle = 0;
|
||||
prm_rsc_request.payload_header.payload_size = sizeof(prm_cmd_request_rsc_t) - sizeof(apm_cmd_header_t);
|
||||
|
||||
/** Populate the param payload */
|
||||
prm_rsc_request.module_payload_0.module_instance_id = PRM_MODULE_INSTANCE_ID;
|
||||
prm_rsc_request.module_payload_0.error_code = 0;
|
||||
prm_rsc_request.module_payload_0.param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
|
||||
prm_rsc_request.module_payload_0.param_size =
|
||||
sizeof(prm_cmd_request_rsc_t) - sizeof(apm_cmd_header_t) - sizeof(apm_module_param_data_t);
|
||||
|
||||
prm_rsc_request.num_clk_id_t.num_clock_id = MAX_AUD_HW_CLK_NUM_REQ;
|
||||
|
||||
prm_rsc_request.clock_ids_t[0].clock_id = cfg->clk_id;
|
||||
prm_rsc_request.clock_ids_t[0].clock_freq = cfg->clk_freq_in_hz;
|
||||
prm_rsc_request.clock_ids_t[0].clock_attri = cfg->clk_attri;
|
||||
/*
|
||||
* Set TX RCG to RCO if lpi pcm logging is enabled and any one of the
|
||||
* tx core clocks are enabled
|
||||
*/
|
||||
if (g_prm.lpi_pcm_logging_enable &&
|
||||
((cfg->clk_id == CLOCK_ID_TX_CORE_MCLK) ||
|
||||
(cfg->clk_id == CLOCK_ID_WSA_CORE_TX_MCLK) ||
|
||||
(cfg->clk_id == CLOCK_ID_WSA2_CORE_TX_MCLK) ||
|
||||
(cfg->clk_id == CLOCK_ID_RX_CORE_TX_MCLK)))
|
||||
prm_rsc_request.clock_ids_t[0].clock_root = CLOCK_ROOT_SRC_RCO;
|
||||
else
|
||||
prm_rsc_request.clock_ids_t[0].clock_root = cfg->clk_root;
|
||||
|
||||
memcpy(&pkt->payload, &prm_rsc_request, sizeof(prm_cmd_request_rsc_t));
|
||||
|
||||
ret = prm_gpr_send_pkt(pkt, &g_prm.wait);
|
||||
|
||||
kfree(pkt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audio_prm_set_lpass_clk_cfg_rel(struct clk_cfg *cfg)
|
||||
{
|
||||
|
||||
struct gpr_pkt *pkt;
|
||||
prm_cmd_release_rsc_t prm_rsc_release;
|
||||
int ret = 0;
|
||||
uint32_t size;
|
||||
|
||||
size = GPR_HDR_SIZE + sizeof(prm_cmd_release_rsc_t);
|
||||
pkt = kzalloc(size, GFP_KERNEL);
|
||||
if (!pkt)
|
||||
return -ENOMEM;
|
||||
|
||||
pkt->hdr.header = GPR_SET_FIELD(GPR_PKT_VERSION, GPR_PKT_VER) |
|
||||
GPR_SET_FIELD(GPR_PKT_HEADER_SIZE, GPR_PKT_HEADER_WORD_SIZE_V) |
|
||||
GPR_SET_FIELD(GPR_PKT_PACKET_SIZE, size);
|
||||
|
||||
pkt->hdr.src_port = GPR_SVC_ASM;
|
||||
pkt->hdr.dst_port = PRM_MODULE_INSTANCE_ID;
|
||||
pkt->hdr.dst_domain_id = GPR_IDS_DOMAIN_ID_ADSP_V;
|
||||
pkt->hdr.src_domain_id = GPR_IDS_DOMAIN_ID_APPS_V;
|
||||
pkt->hdr.token = 0; /* TBD */
|
||||
pkt->hdr.opcode = PRM_CMD_RELEASE_HW_RSC;
|
||||
|
||||
//pr_err("%s: clk_id %d size of cmd_req %ld \n",__func__, cfg->clk_id, sizeof(prm_cmd_release_rsc_t));
|
||||
|
||||
prm_rsc_release.payload_header.payload_address_lsw = 0;
|
||||
prm_rsc_release.payload_header.payload_address_msw = 0;
|
||||
prm_rsc_release.payload_header.mem_map_handle = 0;
|
||||
prm_rsc_release.payload_header.payload_size = sizeof(prm_cmd_release_rsc_t) - sizeof(apm_cmd_header_t);
|
||||
|
||||
/** Populate the param payload */
|
||||
prm_rsc_release.module_payload_0.module_instance_id = PRM_MODULE_INSTANCE_ID;
|
||||
prm_rsc_release.module_payload_0.error_code = 0;
|
||||
prm_rsc_release.module_payload_0.param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
|
||||
prm_rsc_release.module_payload_0.param_size =
|
||||
sizeof(prm_cmd_release_rsc_t) - sizeof(apm_cmd_header_t) - sizeof(apm_module_param_data_t);
|
||||
|
||||
prm_rsc_release.num_clk_id_t.num_clock_id = MAX_AUD_HW_CLK_NUM_REQ;
|
||||
|
||||
prm_rsc_release.clock_ids_t[0].clock_id = cfg->clk_id;
|
||||
|
||||
memcpy(&pkt->payload, &prm_rsc_release, sizeof(prm_cmd_release_rsc_t));
|
||||
|
||||
ret = prm_gpr_send_pkt(pkt, &g_prm.wait);
|
||||
|
||||
kfree(pkt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_prm_set_cdc_earpa_duty_cycling_req() - send codec reg values
|
||||
* for codec duty cycling.
|
||||
*
|
||||
* Return: 0 if reg passing is success.
|
||||
*/
|
||||
int audio_prm_set_cdc_earpa_duty_cycling_req(struct prm_earpa_hw_intf_config *earpa_config,
|
||||
uint32_t enable)
|
||||
{
|
||||
struct gpr_pkt *pkt;
|
||||
prm_cmd_request_cdc_duty_cycling_t prm_rsc_request_reg_info;
|
||||
int ret = 0;
|
||||
uint32_t size;
|
||||
|
||||
size = GPR_HDR_SIZE + sizeof(prm_cmd_request_cdc_duty_cycling_t);
|
||||
pkt = kzalloc(size, GFP_KERNEL);
|
||||
if (!pkt)
|
||||
return -ENOMEM;
|
||||
pkt->hdr.header = GPR_SET_FIELD(GPR_PKT_VERSION, GPR_PKT_VER) |
|
||||
GPR_SET_FIELD(GPR_PKT_HEADER_SIZE, GPR_PKT_HEADER_WORD_SIZE_V) |
|
||||
GPR_SET_FIELD(GPR_PKT_PACKET_SIZE, size);
|
||||
|
||||
pkt->hdr.src_port = GPR_SVC_ASM;
|
||||
pkt->hdr.dst_port = PRM_MODULE_INSTANCE_ID;
|
||||
pkt->hdr.dst_domain_id = GPR_IDS_DOMAIN_ID_ADSP_V;
|
||||
pkt->hdr.src_domain_id = GPR_IDS_DOMAIN_ID_APPS_V;
|
||||
pkt->hdr.token = 0;
|
||||
if (enable)
|
||||
pkt->hdr.opcode = PRM_CMD_REQUEST_HW_RSC;
|
||||
else
|
||||
pkt->hdr.opcode = PRM_CMD_RELEASE_HW_RSC;
|
||||
|
||||
memset(&prm_rsc_request_reg_info, 0, sizeof(prm_cmd_request_cdc_duty_cycling_t));
|
||||
prm_rsc_request_reg_info.payload_header.payload_address_lsw = 0;
|
||||
prm_rsc_request_reg_info.payload_header.payload_address_msw = 0;
|
||||
prm_rsc_request_reg_info.payload_header.mem_map_handle = 0;
|
||||
prm_rsc_request_reg_info.payload_header.payload_size =
|
||||
sizeof(prm_cmd_request_cdc_duty_cycling_t) - sizeof(apm_cmd_header_t);
|
||||
|
||||
/* Populate the param payload */
|
||||
prm_rsc_request_reg_info.module_payload_0.module_instance_id =
|
||||
PRM_MODULE_INSTANCE_ID;
|
||||
prm_rsc_request_reg_info.module_payload_0.error_code = 0;
|
||||
prm_rsc_request_reg_info.module_payload_0.param_id =
|
||||
PARAM_ID_RSC_HW_CODEC_REG_INFO;
|
||||
prm_rsc_request_reg_info.module_payload_0.param_size =
|
||||
sizeof(prm_cmd_request_cdc_duty_cycling_t) -
|
||||
sizeof(apm_cmd_header_t) - sizeof(apm_module_param_data_t);
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.num_reg_info_t = MAX_EARPA_REG;
|
||||
/* Setting up DIGITAL Mute register value */
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].hw_codec_reg_id =
|
||||
HW_CODEC_DIG_REG_ID_MUTE_CTRL;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].hw_codec_reg_addr_msw = 0;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].hw_codec_reg_addr_lsw =
|
||||
earpa_config->ear_pa_hw_reg_cfg.lpass_cdc_rx0_rx_path_ctl_phy_addr;
|
||||
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].num_ops =
|
||||
MAX_EARPA_CDC_DUTY_CYC_OPERATION;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].hw_codec_op[0].hw_codec_op_id =
|
||||
HW_CODEC_OP_DIG_MUTE_ENABLE;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].hw_codec_op[0].hw_codec_op_value =
|
||||
DIG_MUTE_ENABLE;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].hw_codec_op[1].hw_codec_op_id =
|
||||
HW_CODEC_OP_DIG_MUTE_DISABLE;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[0].hw_codec_op[1].hw_codec_op_value =
|
||||
DIG_MUTE_DISABLE;
|
||||
/* Setting up LPASS_PA_REG Values */
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].hw_codec_reg_id =
|
||||
HW_CODEC_ANALOG_REG_ID_CMD_FIFO_WRITE;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].hw_codec_reg_addr_msw = 0;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].hw_codec_reg_addr_lsw =
|
||||
earpa_config->ear_pa_hw_reg_cfg.lpass_wr_fifo_reg_phy_addr;
|
||||
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].num_ops =
|
||||
MAX_EARPA_CDC_DUTY_CYC_OPERATION;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].hw_codec_op[0].hw_codec_op_id =
|
||||
HW_CODEC_OP_ANA_PGA_ENABLE;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].hw_codec_op[0].hw_codec_op_value =
|
||||
earpa_config->ear_pa_pkd_cfg.ear_pa_enable_pkd_reg_addr;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].hw_codec_op[1].hw_codec_op_id =
|
||||
HW_CODEC_OP_ANA_PGA_DISABLE;
|
||||
prm_rsc_request_reg_info.hw_codec_reg_info_t.hw_codec_reg[1].hw_codec_op[1].hw_codec_op_value =
|
||||
earpa_config->ear_pa_pkd_cfg.ear_pa_disable_pkd_reg_addr;
|
||||
|
||||
memcpy(&pkt->payload, &prm_rsc_request_reg_info, sizeof(prm_cmd_request_cdc_duty_cycling_t));
|
||||
ret = prm_gpr_send_pkt(pkt, &g_prm.wait);
|
||||
kfree(pkt);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(audio_prm_set_cdc_earpa_duty_cycling_req);
|
||||
|
||||
int audio_prm_set_vote_against_sleep(uint8_t enable)
|
||||
{
|
||||
if (g_prm.prm_sleep_api_supported)
|
||||
return _audio_prm_set_lpass_cpu_lpr_req(enable);
|
||||
else
|
||||
return _audio_prm_set_lpass_hw_core_req(HW_CORE_ID_LPASS, enable);
|
||||
}
|
||||
EXPORT_SYMBOL(audio_prm_set_vote_against_sleep);
|
||||
|
||||
int audio_prm_set_lpass_clk_cfg (struct clk_cfg *clk, uint8_t enable)
|
||||
{
|
||||
int ret = 0;
|
||||
if (enable)
|
||||
ret = audio_prm_set_lpass_clk_cfg_req (clk);
|
||||
else
|
||||
ret = audio_prm_set_lpass_clk_cfg_rel (clk);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(audio_prm_set_lpass_clk_cfg);
|
||||
|
||||
static int audio_prm_service_cb(struct notifier_block *this,
|
||||
unsigned long opcode, void *data)
|
||||
{
|
||||
pr_info("%s: Service opcode 0x%lx\n", __func__, opcode);
|
||||
|
||||
switch (opcode) {
|
||||
case AUDIO_NOTIFIER_SERVICE_DOWN:
|
||||
mutex_lock(&g_prm.lock);
|
||||
pr_debug("%s: making apm_ready check false\n", __func__);
|
||||
is_apm_ready_check_done = false;
|
||||
g_prm.is_adsp_up = false;
|
||||
mutex_unlock(&g_prm.lock);
|
||||
break;
|
||||
case AUDIO_NOTIFIER_SERVICE_UP:
|
||||
mutex_lock(&g_prm.lock);
|
||||
g_prm.is_adsp_up = true;
|
||||
mutex_unlock(&g_prm.lock);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block service_nb = {
|
||||
.notifier_call = audio_prm_service_cb,
|
||||
.priority = -INT_MAX,
|
||||
};
|
||||
|
||||
static int audio_prm_probe(struct gpr_device *adev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!audio_notifier_probe_status()) {
|
||||
pr_err("%s: Audio notify probe not completed, defer audio prm probe\n",
|
||||
__func__);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(adev->dev.of_node,
|
||||
"qcom,sleep-api-supported", &g_prm.prm_sleep_api_supported);
|
||||
if (ret < 0)
|
||||
pr_debug("%s: sleep API not supported\n", __func__);
|
||||
|
||||
ret = audio_notifier_register("audio_prm", AUDIO_NOTIFIER_ADSP_DOMAIN,
|
||||
&service_nb);
|
||||
if (ret < 0) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
pr_err("%s: Audio notifier register failed ret = %d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&adev->dev, &g_prm);
|
||||
|
||||
g_prm.adev = adev;
|
||||
|
||||
init_waitqueue_head(&g_prm.wait);
|
||||
g_prm.is_adsp_up = true;
|
||||
pr_err("%s: prm probe success, Sleep API supported :%d\n",
|
||||
__func__, g_prm.prm_sleep_api_supported);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int audio_prm_remove(struct gpr_device *adev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
audio_notifier_deregister("audio_prm");
|
||||
mutex_lock(&g_prm.lock);
|
||||
g_prm.is_adsp_up = false;
|
||||
g_prm.adev = NULL;
|
||||
mutex_unlock(&g_prm.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id audio_prm_device_id[] = {
|
||||
{ .compatible = "qcom,audio_prm" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, audio_prm_device_id);
|
||||
|
||||
static struct gpr_driver qcom_audio_prm_driver = {
|
||||
.probe = audio_prm_probe,
|
||||
.remove = audio_prm_remove,
|
||||
.callback = audio_prm_callback,
|
||||
.driver = {
|
||||
.name = "qcom-audio_prm",
|
||||
.of_match_table = of_match_ptr(audio_prm_device_id),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init audio_prm_module_init(void)
|
||||
{
|
||||
int ret;
|
||||
ret = gpr_driver_register(&qcom_audio_prm_driver);
|
||||
|
||||
if (ret)
|
||||
pr_err("%s: gpr driver register failed = %d\n", __func__, ret);
|
||||
|
||||
mutex_init(&g_prm.lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit audio_prm_module_exit(void)
|
||||
{
|
||||
mutex_destroy(&g_prm.lock);
|
||||
gpr_driver_unregister(&qcom_audio_prm_driver);
|
||||
}
|
||||
|
||||
module_init(audio_prm_module_init);
|
||||
module_exit(audio_prm_module_exit);
|
||||
MODULE_DESCRIPTION("audio prm");
|
||||
MODULE_LICENSE("GPL v2");
|
46
qcom/opensource/audio-kernel/dsp/audio_ssr.c
Normal file
46
qcom/opensource/audio-kernel/dsp/audio_ssr.c
Normal file
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016, 2020 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/remoteproc.h>
|
||||
#include <linux/remoteproc/qcom_rproc.h>
|
||||
#include "audio_ssr.h"
|
||||
|
||||
|
||||
/**
|
||||
* audio_ssr_register -
|
||||
* register to SSR framework
|
||||
*
|
||||
* @domain_id: Domain ID to register with
|
||||
* @nb: notifier block
|
||||
*
|
||||
* Returns handle pointer on success or error PTR on failure
|
||||
*/
|
||||
void *audio_ssr_register(const char *domain_name, struct notifier_block *nb)
|
||||
{
|
||||
if (domain_name == NULL) {
|
||||
pr_err("%s: Invalid domain name\n", __func__);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return qcom_register_ssr_notifier(domain_name, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(audio_ssr_register);
|
||||
|
||||
/**
|
||||
* audio_ssr_deregister -
|
||||
* Deregister handle from SSR framework
|
||||
*
|
||||
* @handle: SSR handle
|
||||
* @nb: notifier block
|
||||
*
|
||||
* Returns 0 on success or error on failure
|
||||
*/
|
||||
int audio_ssr_deregister(void *handle, struct notifier_block *nb)
|
||||
{
|
||||
return qcom_unregister_ssr_notifier(handle, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(audio_ssr_deregister);
|
||||
|
70
qcom/opensource/audio-kernel/dsp/audio_ssr.h
Normal file
70
qcom/opensource/audio-kernel/dsp/audio_ssr.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016, 2020 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_SSR_H_
|
||||
#define __AUDIO_SSR_H_
|
||||
|
||||
enum {
|
||||
AUDIO_SSR_DOMAIN_ADSP,
|
||||
AUDIO_SSR_DOMAIN_MODEM,
|
||||
AUDIO_SSR_DOMAIN_MAX
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MSM_QDSP6_SSR
|
||||
|
||||
/*
|
||||
* Use audio_ssr_register to register with the SSR subsystem
|
||||
*
|
||||
* domain_id - Service to use, example: AUDIO_SSR_DOMAIN_ADSP
|
||||
* *nb - Pointer to a notifier block. Provide a callback function
|
||||
* to be notified of an event for that service. The ioctls
|
||||
* used by the callback are defined in subsystem_notif.h.
|
||||
*
|
||||
* Returns: Success: Client handle
|
||||
* Failure: Pointer error code
|
||||
*/
|
||||
void *audio_ssr_register(const char *domain_name, struct notifier_block *nb);
|
||||
|
||||
/*
|
||||
* Use audio_ssr_deregister to register with the SSR subsystem
|
||||
*
|
||||
* handle - Handle received from audio_ssr_register
|
||||
* *nb - Pointer to a notifier block. Callback function
|
||||
* Used from audio_ssr_register.
|
||||
*
|
||||
* Returns: Success: 0
|
||||
* Failure: Error code
|
||||
*/
|
||||
int audio_ssr_deregister(void *handle, struct notifier_block *nb);
|
||||
|
||||
|
||||
/*
|
||||
* Use audio_ssr_send_nmi to force a RAM dump on ADSP
|
||||
* down event.
|
||||
*
|
||||
* *ssr_cb_data - *data received from notifier callback
|
||||
*/
|
||||
void audio_ssr_send_nmi(void *ssr_cb_data);
|
||||
|
||||
#else
|
||||
|
||||
static inline void *audio_ssr_register(int domain_id,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline int audio_ssr_deregister(void *handle, struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void audio_ssr_send_nmi(void *ssr_cb_data)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MSM_QDSP6_SSR */
|
||||
|
||||
#endif
|
102
qcom/opensource/audio-kernel/dsp/digital-cdc-rsc-mgr.c
Normal file
102
qcom/opensource/audio-kernel/dsp/digital-cdc-rsc-mgr.c
Normal file
@@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <dsp/digital-cdc-rsc-mgr.h>
|
||||
#include <linux/dev_printk.h>
|
||||
|
||||
struct mutex hw_vote_lock;
|
||||
static bool is_init_done;
|
||||
|
||||
/**
|
||||
* digital_cdc_rsc_mgr_hw_vote_enable - Enables hw vote in DSP
|
||||
*
|
||||
* @vote_handle: vote handle for which voting needs to be done
|
||||
* @dev: indicate which device votes
|
||||
*
|
||||
* Returns 0 on success or -EINVAL/error code on failure
|
||||
*/
|
||||
int digital_cdc_rsc_mgr_hw_vote_enable(struct clk *vote_handle, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!is_init_done || vote_handle == NULL) {
|
||||
pr_err_ratelimited("%s: init failed or vote handle NULL\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&hw_vote_lock);
|
||||
ret = clk_prepare_enable(vote_handle);
|
||||
mutex_unlock(&hw_vote_lock);
|
||||
|
||||
dev_dbg(dev, "%s: return %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(digital_cdc_rsc_mgr_hw_vote_enable);
|
||||
|
||||
/**
|
||||
* digital_cdc_rsc_mgr_hw_vote_disable - Disables hw vote in DSP
|
||||
*
|
||||
* @vote_handle: vote handle for which voting needs to be disabled
|
||||
* @dev: indicate which device unvotes
|
||||
*
|
||||
*/
|
||||
void digital_cdc_rsc_mgr_hw_vote_disable(struct clk *vote_handle, struct device *dev)
|
||||
{
|
||||
if (!is_init_done || vote_handle == NULL) {
|
||||
pr_err_ratelimited("%s: init failed or vote handle NULL\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&hw_vote_lock);
|
||||
clk_disable_unprepare(vote_handle);
|
||||
mutex_unlock(&hw_vote_lock);
|
||||
dev_dbg(dev, "%s: leave\n", __func__);
|
||||
}
|
||||
EXPORT_SYMBOL(digital_cdc_rsc_mgr_hw_vote_disable);
|
||||
|
||||
/**
|
||||
* digital_cdc_rsc_mgr_hw_vote_reset - Resets hw vote count
|
||||
*
|
||||
*/
|
||||
void digital_cdc_rsc_mgr_hw_vote_reset(struct clk* vote_handle)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
if (!is_init_done || vote_handle == NULL) {
|
||||
pr_err_ratelimited("%s: init failed or vote handle NULL\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&hw_vote_lock);
|
||||
while (__clk_is_enabled(vote_handle)) {
|
||||
clk_disable_unprepare(vote_handle);
|
||||
count++;
|
||||
}
|
||||
pr_debug("%s: Vote count after SSR: %d\n", __func__, count);
|
||||
|
||||
while (count--)
|
||||
clk_prepare_enable(vote_handle);
|
||||
mutex_unlock(&hw_vote_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(digital_cdc_rsc_mgr_hw_vote_reset);
|
||||
|
||||
void digital_cdc_rsc_mgr_init(void)
|
||||
{
|
||||
mutex_init(&hw_vote_lock);
|
||||
is_init_done = true;
|
||||
}
|
||||
|
||||
void digital_cdc_rsc_mgr_exit(void)
|
||||
{
|
||||
mutex_destroy(&hw_vote_lock);
|
||||
is_init_done = false;
|
||||
}
|
74
qcom/opensource/audio-kernel/dsp/msm-audio-event-notify.c
Normal file
74
qcom/opensource/audio-kernel/dsp/msm-audio-event-notify.c
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#include <dsp/msm-audio-event-notify.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
static ATOMIC_NOTIFIER_HEAD(msm_aud_evt_notifier_list);
|
||||
static BLOCKING_NOTIFIER_HEAD(msm_aud_evt_blocking_notifier_list);
|
||||
|
||||
/**
|
||||
* msm_aud_evt_register_client - register a client notifier
|
||||
* @nb: notifier block to callback on events
|
||||
*/
|
||||
int msm_aud_evt_register_client(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_register(&msm_aud_evt_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(msm_aud_evt_register_client);
|
||||
|
||||
/**
|
||||
* msm_aud_evt_unregister_client - unregister a client notifier
|
||||
* @nb: notifier block to callback on events
|
||||
*/
|
||||
int msm_aud_evt_unregister_client(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_unregister(&msm_aud_evt_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(msm_aud_evt_unregister_client);
|
||||
|
||||
/**
|
||||
* msm_aud_evt_notifier_call_chain - notify clients of fb_events
|
||||
*
|
||||
*/
|
||||
int msm_aud_evt_notifier_call_chain(unsigned long val, void *v)
|
||||
{
|
||||
return atomic_notifier_call_chain(&msm_aud_evt_notifier_list, val, v);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(msm_aud_evt_notifier_call_chain);
|
||||
|
||||
/**
|
||||
* msm_aud_evt_blocking_register_client - register a client notifier
|
||||
* @nb: notifier block to callback on events
|
||||
*/
|
||||
int msm_aud_evt_blocking_register_client(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(
|
||||
&msm_aud_evt_blocking_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(msm_aud_evt_blocking_register_client);
|
||||
|
||||
/**
|
||||
* msm_aud_evt_unregister_client - unregister a client notifier
|
||||
* @nb: notifier block to callback on events
|
||||
*/
|
||||
int msm_aud_evt_blocking_unregister_client(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(
|
||||
&msm_aud_evt_blocking_notifier_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(msm_aud_evt_blocking_unregister_client);
|
||||
|
||||
/**
|
||||
* msm_aud_evt_notifier_call_chain - notify clients of fb_events
|
||||
* @val: event or enum maintained by caller
|
||||
* @v: private data pointer
|
||||
*
|
||||
*/
|
||||
int msm_aud_evt_blocking_notifier_call_chain(unsigned long val, void *v)
|
||||
{
|
||||
return blocking_notifier_call_chain(
|
||||
&msm_aud_evt_blocking_notifier_list, val, v);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(msm_aud_evt_blocking_notifier_call_chain);
|
1006
qcom/opensource/audio-kernel/dsp/msm_audio_ion.c
Normal file
1006
qcom/opensource/audio-kernel/dsp/msm_audio_ion.c
Normal file
File diff suppressed because it is too large
Load Diff
1013
qcom/opensource/audio-kernel/dsp/msm_audio_ion_vm.c
Normal file
1013
qcom/opensource/audio-kernel/dsp/msm_audio_ion_vm.c
Normal file
File diff suppressed because it is too large
Load Diff
55
qcom/opensource/audio-kernel/dsp/q6_init.c
Normal file
55
qcom/opensource/audio-kernel/dsp/q6_init.c
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2017, 2019-2020 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "q6_init.h"
|
||||
|
||||
static int __init audio_q6_init(void)
|
||||
{
|
||||
// TODO: Is this file required?
|
||||
// adsp_err_init();
|
||||
// audio_cal_init();
|
||||
// rtac_init();
|
||||
// adm_init();
|
||||
// afe_init();
|
||||
// spk_params_init();
|
||||
// q6asm_init();
|
||||
// q6lsm_init();
|
||||
// voice_init();
|
||||
// core_init();
|
||||
// msm_audio_ion_init();
|
||||
// audio_slimslave_init();
|
||||
// avtimer_init();
|
||||
// msm_mdf_init();
|
||||
// voice_mhi_init();
|
||||
// digital_cdc_rsc_mgr_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit audio_q6_exit(void)
|
||||
{
|
||||
// digital_cdc_rsc_mgr_exit();
|
||||
// msm_mdf_exit();
|
||||
// avtimer_exit();
|
||||
// audio_slimslave_exit();
|
||||
// msm_audio_ion_exit();
|
||||
// core_exit();
|
||||
// voice_exit();
|
||||
// q6lsm_exit();
|
||||
// q6asm_exit();
|
||||
// afe_exit();
|
||||
// spk_params_exit();
|
||||
// adm_exit();
|
||||
// rtac_exit();
|
||||
// audio_cal_exit();
|
||||
// adsp_err_exit();
|
||||
// voice_mhi_exit();
|
||||
}
|
||||
|
||||
module_init(audio_q6_init);
|
||||
module_exit(audio_q6_exit);
|
||||
MODULE_DESCRIPTION("Q6 module");
|
||||
MODULE_LICENSE("GPL v2");
|
11
qcom/opensource/audio-kernel/dsp/q6_init.h
Normal file
11
qcom/opensource/audio-kernel/dsp/q6_init.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __Q6_INIT_H__
|
||||
#define __Q6_INIT_H__
|
||||
//int msm_audio_ion_init(void);
|
||||
//void msm_audio_ion_exit(void);
|
||||
#endif
|
||||
|
425
qcom/opensource/audio-kernel/dsp/sp_params.c
Normal file
425
qcom/opensource/audio-kernel/dsp/sp_params.c
Normal file
@@ -0,0 +1,425 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <dsp/q6audio-v2.h>
|
||||
#include <dsp/q6afe-v2.h>
|
||||
#include <audio/linux/msm_audio_calibration.h>
|
||||
#include <dsp/sp_params.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
/* export or show spk params at /sys/class/spk_params/cal_data */
|
||||
#define SPK_PARAMS "spk_params"
|
||||
#define CLASS_NAME "cal_data"
|
||||
#define BUF_SZ 20
|
||||
#define Q22 (1<<22)
|
||||
#define Q13 (1<<13)
|
||||
#define Q7 (1<<7)
|
||||
|
||||
struct afe_spk_ctl {
|
||||
struct class *p_class;
|
||||
struct device *p_dev;
|
||||
struct afe_sp_rx_tmax_xmax_logging_param xt_logging;
|
||||
int32_t max_temperature_rd[SP_V2_NUM_MAX_SPKR];
|
||||
};
|
||||
struct afe_spk_ctl this_afe_spk;
|
||||
|
||||
static ssize_t sp_count_exceeded_temperature_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1]);
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(count_exceeded_temperature, 0644,
|
||||
sp_count_exceeded_temperature_l_show, NULL);
|
||||
|
||||
static ssize_t sp_count_exceeded_temperature_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2]);
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(count_exceeded_temperature_r, 0644,
|
||||
sp_count_exceeded_temperature_r_show, NULL);
|
||||
|
||||
static ssize_t sp_count_exceeded_excursion_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1]);
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(count_exceeded_excursion, 0644,
|
||||
sp_count_exceeded_excursion_l_show, NULL);
|
||||
|
||||
static ssize_t sp_count_exceeded_excursion_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2]);
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(count_exceeded_excursion_r, 0644,
|
||||
sp_count_exceeded_excursion_r_show, NULL);
|
||||
|
||||
static ssize_t sp_max_excursion_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
int32_t ex_val_frac;
|
||||
int32_t ex_q27 = this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1];
|
||||
|
||||
ex_val_frac = ex_q27/Q13;
|
||||
ex_val_frac = (ex_val_frac * 10000)/(Q7 * Q7);
|
||||
ex_val_frac /= 100;
|
||||
ret = snprintf(buf, BUF_SZ, "%d.%02d\n", 0, ex_val_frac);
|
||||
this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(max_excursion, 0644, sp_max_excursion_l_show, NULL);
|
||||
|
||||
static ssize_t sp_max_excursion_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
int32_t ex_val_frac;
|
||||
int32_t ex_q27 = this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2];
|
||||
|
||||
ex_val_frac = ex_q27/Q13;
|
||||
ex_val_frac = (ex_val_frac * 10000)/(Q7 * Q7);
|
||||
ex_val_frac /= 100;
|
||||
ret = snprintf(buf, BUF_SZ, "%d.%02d\n", 0, ex_val_frac);
|
||||
this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(max_excursion_r, 0644, sp_max_excursion_r_show, NULL);
|
||||
|
||||
static ssize_t sp_max_temperature_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1]/Q22);
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(max_temperature, 0644, sp_max_temperature_l_show, NULL);
|
||||
|
||||
static ssize_t sp_max_temperature_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t ret = 0;
|
||||
|
||||
ret = snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2]/Q22);
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] = 0;
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(max_temperature_r, 0644, sp_max_temperature_r_show, NULL);
|
||||
|
||||
static ssize_t sp_max_temperature_rd_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.max_temperature_rd[SP_V2_SPKR_1]/Q22);
|
||||
}
|
||||
static DEVICE_ATTR(max_temperature_rd, 0644,
|
||||
sp_max_temperature_rd_l_show, NULL);
|
||||
|
||||
static ssize_t sp_max_temperature_rd_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, BUF_SZ, "%d\n",
|
||||
this_afe_spk.max_temperature_rd[SP_V2_SPKR_2]/Q22);
|
||||
}
|
||||
static DEVICE_ATTR(max_temperature_rd_r, 0644,
|
||||
sp_max_temperature_rd_r_show, NULL);
|
||||
|
||||
static ssize_t q6afe_initial_cal_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, BUF_SZ, "%d\n", afe_get_spk_initial_cal());
|
||||
}
|
||||
|
||||
static ssize_t q6afe_initial_cal_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int initial_cal = 0;
|
||||
|
||||
if (!kstrtou32(buf, 0, &initial_cal)) {
|
||||
initial_cal = initial_cal > 0 ? 1 : 0;
|
||||
if (initial_cal == afe_get_spk_initial_cal())
|
||||
dev_dbg(dev, "%s: same value already present\n",
|
||||
__func__);
|
||||
else
|
||||
afe_set_spk_initial_cal(initial_cal);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(initial_cal, 0644,
|
||||
q6afe_initial_cal_show, q6afe_initial_cal_store);
|
||||
|
||||
static ssize_t q6afe_v_vali_flag_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, BUF_SZ, "%d\n", afe_get_spk_v_vali_flag());
|
||||
}
|
||||
|
||||
static ssize_t q6afe_v_vali_flag_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int v_vali_flag = 0;
|
||||
|
||||
if (!kstrtou32(buf, 0, &v_vali_flag)) {
|
||||
v_vali_flag = v_vali_flag > 0 ? 1 : 0;
|
||||
if (v_vali_flag == afe_get_spk_v_vali_flag())
|
||||
dev_dbg(dev, "%s: same value already present\n",
|
||||
__func__);
|
||||
else
|
||||
afe_set_spk_v_vali_flag(v_vali_flag);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(v_vali_flag, 0644,
|
||||
q6afe_v_vali_flag_show, q6afe_v_vali_flag_store);
|
||||
|
||||
static ssize_t q6afe_spk_r0_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int r0[SP_V2_NUM_MAX_SPKRS];
|
||||
|
||||
afe_get_spk_r0(r0);
|
||||
return snprintf(buf, BUF_SZ, "%d\n", r0[SP_V2_SPKR_1]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(spk_r0, 0644, q6afe_spk_r0_l_show, NULL);
|
||||
|
||||
static ssize_t q6afe_spk_t0_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int t0[SP_V2_NUM_MAX_SPKRS];
|
||||
|
||||
afe_get_spk_t0(t0);
|
||||
return snprintf(buf, BUF_SZ, "%d\n", t0[SP_V2_SPKR_1]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(spk_t0, 0644, q6afe_spk_t0_l_show, NULL);
|
||||
|
||||
static ssize_t q6afe_spk_r0_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int r0[SP_V2_NUM_MAX_SPKRS];
|
||||
|
||||
afe_get_spk_r0(r0);
|
||||
return snprintf(buf, BUF_SZ, "%d\n", r0[SP_V2_SPKR_2]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(spk_r0_r, 0644, q6afe_spk_r0_r_show, NULL);
|
||||
|
||||
static ssize_t q6afe_spk_t0_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int t0[SP_V2_NUM_MAX_SPKRS];
|
||||
|
||||
afe_get_spk_t0(t0);
|
||||
return snprintf(buf, BUF_SZ, "%d\n", t0[SP_V2_SPKR_2]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(spk_t0_r, 0644, q6afe_spk_t0_r_show, NULL);
|
||||
|
||||
static ssize_t q6afe_spk_v_vali_l_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int v_vali_sts[SP_V2_NUM_MAX_SPKRS];
|
||||
|
||||
afe_get_spk_v_vali_sts(v_vali_sts);
|
||||
return snprintf(buf, BUF_SZ, "%d\n", v_vali_sts[SP_V2_SPKR_1]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(spk_v_vali_status, 0644, q6afe_spk_v_vali_l_show, NULL);
|
||||
|
||||
static ssize_t q6afe_spk_v_vali_r_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int v_vali_sts[SP_V2_NUM_MAX_SPKRS];
|
||||
|
||||
afe_get_spk_v_vali_sts(v_vali_sts);
|
||||
return snprintf(buf, BUF_SZ, "%d\n", v_vali_sts[SP_V2_SPKR_2]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(spk_v_vali_r_status, 0644, q6afe_spk_v_vali_r_show, NULL);
|
||||
|
||||
static struct attribute *afe_spk_cal_attr[] = {
|
||||
&dev_attr_max_excursion.attr,
|
||||
&dev_attr_max_excursion_r.attr,
|
||||
&dev_attr_max_temperature.attr,
|
||||
&dev_attr_max_temperature_r.attr,
|
||||
&dev_attr_count_exceeded_excursion.attr,
|
||||
&dev_attr_count_exceeded_excursion_r.attr,
|
||||
&dev_attr_count_exceeded_temperature.attr,
|
||||
&dev_attr_count_exceeded_temperature_r.attr,
|
||||
&dev_attr_max_temperature_rd.attr,
|
||||
&dev_attr_max_temperature_rd_r.attr,
|
||||
&dev_attr_initial_cal.attr,
|
||||
&dev_attr_spk_r0.attr,
|
||||
&dev_attr_spk_t0.attr,
|
||||
&dev_attr_spk_r0_r.attr,
|
||||
&dev_attr_spk_t0_r.attr,
|
||||
&dev_attr_v_vali_flag.attr,
|
||||
&dev_attr_spk_v_vali_status.attr,
|
||||
&dev_attr_spk_v_vali_r_status.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group afe_spk_cal_attr_grp = {
|
||||
.attrs = afe_spk_cal_attr,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* afe_get_sp_xt_logging_data -
|
||||
* to get excursion logging data from DSP
|
||||
*
|
||||
* @port: AFE port ID
|
||||
*
|
||||
* Returns 0 on success or error on failure
|
||||
*/
|
||||
int afe_get_sp_xt_logging_data(u16 port_id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct afe_sp_rx_tmax_xmax_logging_param xt_logging_data;
|
||||
|
||||
ret = afe_get_sp_rx_tmax_xmax_logging_data(&xt_logging_data, port_id);
|
||||
if (ret) {
|
||||
pr_err("%s Excursion logging fail\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
/* storing max sp param value */
|
||||
if (this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] <
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_1])
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] =
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_1];
|
||||
|
||||
|
||||
if (this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] <
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_2])
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] =
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_2];
|
||||
|
||||
|
||||
/* update temp for max_temperature_rd node */
|
||||
if (this_afe_spk.max_temperature_rd[SP_V2_SPKR_1] <
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_1])
|
||||
this_afe_spk.max_temperature_rd[SP_V2_SPKR_1] =
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_1];
|
||||
|
||||
if (this_afe_spk.max_temperature_rd[SP_V2_SPKR_2] <
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_2])
|
||||
this_afe_spk.max_temperature_rd[SP_V2_SPKR_2] =
|
||||
xt_logging_data.max_temperature[SP_V2_SPKR_2];
|
||||
|
||||
|
||||
if (this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] <
|
||||
xt_logging_data.max_excursion[SP_V2_SPKR_1])
|
||||
this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] =
|
||||
xt_logging_data.max_excursion[SP_V2_SPKR_1];
|
||||
|
||||
if (this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] <
|
||||
xt_logging_data.max_excursion[SP_V2_SPKR_2])
|
||||
this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] =
|
||||
xt_logging_data.max_excursion[SP_V2_SPKR_2];
|
||||
|
||||
if (this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1] <
|
||||
xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_1])
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1]
|
||||
+= xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_1];
|
||||
|
||||
if (this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2] <
|
||||
xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_2])
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2]
|
||||
+= xt_logging_data.count_exceeded_temperature[SP_V2_SPKR_2];
|
||||
|
||||
if (this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1] <
|
||||
xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_1])
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1]
|
||||
+= xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_1];
|
||||
|
||||
if (this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2] <
|
||||
xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_2])
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2]
|
||||
+= xt_logging_data.count_exceeded_excursion[SP_V2_SPKR_2];
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(afe_get_sp_xt_logging_data);
|
||||
|
||||
int __init spk_params_init(void)
|
||||
{
|
||||
/* initialize xt param value with 0 */
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_1] = 0;
|
||||
this_afe_spk.xt_logging.max_temperature[SP_V2_SPKR_2] = 0;
|
||||
this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_1] = 0;
|
||||
this_afe_spk.xt_logging.max_excursion[SP_V2_SPKR_2] = 0;
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_1] = 0;
|
||||
this_afe_spk.xt_logging.count_exceeded_temperature[SP_V2_SPKR_2] = 0;
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_1] = 0;
|
||||
this_afe_spk.xt_logging.count_exceeded_excursion[SP_V2_SPKR_2] = 0;
|
||||
|
||||
this_afe_spk.p_class = class_create(THIS_MODULE, SPK_PARAMS);
|
||||
if (this_afe_spk.p_class) {
|
||||
this_afe_spk.p_dev = device_create(this_afe_spk.p_class, NULL,
|
||||
1, NULL, CLASS_NAME);
|
||||
if (!IS_ERR(this_afe_spk.p_dev)) {
|
||||
if (sysfs_create_group(&this_afe_spk.p_dev->kobj,
|
||||
&afe_spk_cal_attr_grp))
|
||||
pr_err("%s: Failed to create sysfs group\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spk_params_exit(void)
|
||||
{
|
||||
pr_debug("%s\n", __func__);
|
||||
}
|
424
qcom/opensource/audio-kernel/dsp/spf-core.c
Normal file
424
qcom/opensource/audio-kernel/dsp/spf-core.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. 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/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <ipc/gpr-lite.h>
|
||||
#include <dsp/spf-core.h>
|
||||
#include <dsp/digital-cdc-rsc-mgr.h>
|
||||
|
||||
#define Q6_READY_TIMEOUT_MS 1000
|
||||
#define Q6_CLOSE_ALL_TIMEOUT_MS 5000
|
||||
#define APM_CMD_GET_SPF_STATE 0x01001021
|
||||
#define APM_CMD_CLOSE_ALL 0x01001013
|
||||
#define APM_CMD_RSP_GET_SPF_STATE 0x02001007
|
||||
#define APM_MODULE_INSTANCE_ID 0x00000001
|
||||
#define GPR_SVC_ADSP_CORE 0x3
|
||||
#define ADD_CHILD_DEVICES_APM_TIMEOUT_MS 10000
|
||||
|
||||
struct spf_core {
|
||||
struct gpr_device *adev;
|
||||
wait_queue_head_t wait;
|
||||
struct mutex lock;
|
||||
bool resp_received;
|
||||
int32_t status;
|
||||
};
|
||||
|
||||
struct spf_core_private {
|
||||
struct device *dev;
|
||||
struct mutex lock;
|
||||
struct spf_core *spf_core_drv;
|
||||
bool is_initial_boot;
|
||||
struct work_struct add_chld_dev_work;
|
||||
};
|
||||
|
||||
static struct spf_core_private *spf_core_priv;
|
||||
|
||||
/* used to decode basic responses from Gecko */
|
||||
struct spf_cmd_basic_rsp {
|
||||
uint32_t opcode;
|
||||
int32_t status;
|
||||
};
|
||||
|
||||
struct apm_cmd_rsp_get_spf_status_t
|
||||
|
||||
{
|
||||
/* Gecko status
|
||||
* @values
|
||||
* 0 -> Not ready
|
||||
* 1 -> Ready
|
||||
*/
|
||||
uint32_t status;
|
||||
|
||||
};
|
||||
|
||||
static int spf_core_callback(struct gpr_device *adev, void *data)
|
||||
{
|
||||
struct spf_core *core = dev_get_drvdata(&adev->dev);
|
||||
struct apm_cmd_rsp_get_spf_status_t *spf_status_rsp;
|
||||
struct spf_cmd_basic_rsp *basic_rsp;
|
||||
struct gpr_hdr *hdr = data;
|
||||
|
||||
|
||||
dev_info_ratelimited(&adev->dev, "%s: Payload %x", __func__, hdr->opcode);
|
||||
switch (hdr->opcode) {
|
||||
case GPR_IBASIC_RSP_RESULT:
|
||||
basic_rsp = GPR_PKT_GET_PAYLOAD(
|
||||
struct spf_cmd_basic_rsp,
|
||||
data);
|
||||
dev_info_ratelimited(&adev->dev, "%s: op %x status %d", __func__,
|
||||
basic_rsp->opcode, basic_rsp->status);
|
||||
if (basic_rsp->opcode == APM_CMD_CLOSE_ALL) {
|
||||
core->status = basic_rsp->status;
|
||||
} else {
|
||||
dev_err_ratelimited(&adev->dev, "%s: Failed response received",
|
||||
__func__);
|
||||
}
|
||||
core->resp_received = true;
|
||||
break;
|
||||
case APM_CMD_RSP_GET_SPF_STATE:
|
||||
spf_status_rsp =
|
||||
GPR_PKT_GET_PAYLOAD(
|
||||
struct apm_cmd_rsp_get_spf_status_t,
|
||||
data);
|
||||
dev_info_ratelimited(&adev->dev, "%s: sucess response received", __func__);
|
||||
core->status = spf_status_rsp->status;
|
||||
core->resp_received = true;
|
||||
break;
|
||||
default:
|
||||
dev_err_ratelimited(&adev->dev, "Message ID from apm: 0x%x\n",
|
||||
hdr->opcode);
|
||||
break;
|
||||
}
|
||||
if (core->resp_received)
|
||||
wake_up(&core->wait);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool __spf_core_is_apm_ready(struct spf_core *core)
|
||||
{
|
||||
struct gpr_device *adev = core->adev;
|
||||
struct gpr_pkt pkt;
|
||||
int rc;
|
||||
bool ret = false;
|
||||
|
||||
pkt.hdr.header = GPR_SET_FIELD(GPR_PKT_VERSION, GPR_PKT_VER) |
|
||||
GPR_SET_FIELD(GPR_PKT_HEADER_SIZE, GPR_PKT_HEADER_WORD_SIZE_V) |
|
||||
GPR_SET_FIELD(GPR_PKT_PACKET_SIZE, GPR_PKT_HEADER_BYTE_SIZE_V);
|
||||
|
||||
pkt.hdr.dst_port = APM_MODULE_INSTANCE_ID;
|
||||
pkt.hdr.src_port = GPR_SVC_ADSP_CORE;
|
||||
pkt.hdr.dst_domain_id = GPR_IDS_DOMAIN_ID_ADSP_V;
|
||||
pkt.hdr.src_domain_id = GPR_IDS_DOMAIN_ID_APPS_V;
|
||||
pkt.hdr.opcode = APM_CMD_GET_SPF_STATE;
|
||||
|
||||
dev_err_ratelimited(spf_core_priv->dev, "%s: send_command ret\n", __func__);
|
||||
|
||||
rc = gpr_send_pkt(adev, &pkt);
|
||||
if (rc < 0) {
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rc = wait_event_timeout(core->wait, (core->resp_received),
|
||||
msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
|
||||
dev_dbg(spf_core_priv->dev, "%s: wait event unblocked \n", __func__);
|
||||
|
||||
if (rc > 0 && core->resp_received) {
|
||||
ret = core->status;
|
||||
} else {
|
||||
dev_err_ratelimited(spf_core_priv->dev, "%s: command timedout, ret\n",
|
||||
__func__);
|
||||
}
|
||||
done:
|
||||
core->resp_received = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* spf_core_is_apm_ready() - Get status of adsp
|
||||
*
|
||||
* Return: Will return true if apm is ready and false if not.
|
||||
*/
|
||||
bool spf_core_is_apm_ready(int timeout_ms)
|
||||
{
|
||||
unsigned long timeout;
|
||||
bool ret = false;
|
||||
struct spf_core *core;
|
||||
|
||||
if (!spf_core_priv)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&spf_core_priv->lock);
|
||||
core = spf_core_priv->spf_core_drv;
|
||||
if (!core)
|
||||
goto done;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
mutex_lock(&core->lock);
|
||||
for (;;) {
|
||||
if (__spf_core_is_apm_ready(core)) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
if (!timeout_ms)
|
||||
break;
|
||||
|
||||
usleep_range(50000, 50050);
|
||||
if (!time_after(timeout, jiffies)) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&core->lock);
|
||||
done:
|
||||
mutex_unlock(&spf_core_priv->lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spf_core_is_apm_ready);
|
||||
|
||||
/**
|
||||
* spf_core_apm_close_all() - Get status of adsp
|
||||
*
|
||||
* Return: Will be return true if apm is ready and false if not.
|
||||
*/
|
||||
void spf_core_apm_close_all(void)
|
||||
{
|
||||
int rc = 0;
|
||||
struct spf_core *core;
|
||||
struct gpr_pkt pkt;
|
||||
struct gpr_device *adev = NULL;
|
||||
|
||||
if (!spf_core_priv)
|
||||
return;
|
||||
|
||||
mutex_lock(&spf_core_priv->lock);
|
||||
core = spf_core_priv->spf_core_drv;
|
||||
if (!core) {
|
||||
mutex_unlock(&spf_core_priv->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&core->lock);
|
||||
|
||||
adev = core->adev;
|
||||
|
||||
pkt.hdr.header = GPR_SET_FIELD(GPR_PKT_VERSION, GPR_PKT_VER) |
|
||||
GPR_SET_FIELD(GPR_PKT_HEADER_SIZE,
|
||||
GPR_PKT_HEADER_WORD_SIZE_V) |
|
||||
GPR_SET_FIELD(GPR_PKT_PACKET_SIZE,
|
||||
GPR_PKT_HEADER_BYTE_SIZE_V);
|
||||
|
||||
pkt.hdr.dst_port = APM_MODULE_INSTANCE_ID;
|
||||
pkt.hdr.src_port = GPR_SVC_ADSP_CORE;
|
||||
pkt.hdr.dst_domain_id = GPR_IDS_DOMAIN_ID_ADSP_V;
|
||||
pkt.hdr.src_domain_id = GPR_IDS_DOMAIN_ID_APPS_V;
|
||||
pkt.hdr.opcode = APM_CMD_CLOSE_ALL;
|
||||
|
||||
dev_info_ratelimited(spf_core_priv->dev, "%s: send_command \n", __func__);
|
||||
|
||||
rc = gpr_send_pkt(adev, &pkt);
|
||||
if (rc < 0) {
|
||||
dev_err_ratelimited(spf_core_priv->dev, "%s: send_pkt_failed %d\n",
|
||||
__func__, rc);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/* While graph_open is processing by the SPF, apps receives
|
||||
* userspace(agm/pal) crash which will triggers spf_close_all
|
||||
* cmd from msm common drivers and immediately calls
|
||||
* msm_audio_ion_crash_handler() which will un-maps the memory. But
|
||||
* here SPF is still in processing the graph_open, recieved spf_close_all
|
||||
* cmd is queued in SPF. Due to un-mapping is done immediately in HLOS
|
||||
* will resulting in SMMU fault.
|
||||
* To avoid such scenarios, increased the spf_close_all cmd timeout,
|
||||
* because the AGM timeout for the graph_open is 4sec, so increase the timeout
|
||||
* for spf_close_all cmd response until graph open completes or timed out.
|
||||
*/
|
||||
rc = wait_event_timeout(core->wait, (core->resp_received),
|
||||
msecs_to_jiffies(Q6_CLOSE_ALL_TIMEOUT_MS));
|
||||
dev_info_ratelimited(spf_core_priv->dev, "%s: wait event unblocked \n", __func__);
|
||||
if (rc > 0 && core->resp_received) {
|
||||
if (core->status != 0)
|
||||
dev_err_ratelimited(spf_core_priv->dev, "%s, cmd failed status %d",
|
||||
__func__, core->status);
|
||||
} else {
|
||||
dev_err_ratelimited(spf_core_priv->dev, "%s: command timedout, ret\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
done:
|
||||
core->resp_received = false;
|
||||
mutex_unlock(&core->lock);
|
||||
mutex_unlock(&spf_core_priv->lock);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spf_core_apm_close_all);
|
||||
|
||||
|
||||
static int spf_core_probe(struct gpr_device *adev)
|
||||
{
|
||||
struct spf_core *core;
|
||||
pr_err("%s",__func__);
|
||||
if (!spf_core_priv) {
|
||||
pr_err("%s: spf_core platform probe not yet done\n", __func__);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
mutex_lock(&spf_core_priv->lock);
|
||||
core = kzalloc(sizeof(*core), GFP_KERNEL);
|
||||
if (!core) {
|
||||
mutex_unlock(&spf_core_priv->lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&adev->dev, core);
|
||||
|
||||
mutex_init(&core->lock);
|
||||
core->adev = adev;
|
||||
init_waitqueue_head(&core->wait);
|
||||
spf_core_priv->spf_core_drv = core;
|
||||
if (spf_core_priv->is_initial_boot)
|
||||
schedule_work(&spf_core_priv->add_chld_dev_work);
|
||||
mutex_unlock(&spf_core_priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spf_core_exit(struct gpr_device *adev)
|
||||
{
|
||||
struct spf_core *core = dev_get_drvdata(&adev->dev);
|
||||
if (!spf_core_priv) {
|
||||
pr_err("%s: spf_core platform probe not yet done\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
mutex_lock(&spf_core_priv->lock);
|
||||
spf_core_priv->spf_core_drv = NULL;
|
||||
kfree(core);
|
||||
mutex_unlock(&spf_core_priv->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id spf_core_device_id[] = {
|
||||
{ .compatible = "qcom,spf_core" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spf_core_device_id);
|
||||
|
||||
static struct gpr_driver qcom_spf_core_driver = {
|
||||
.probe = spf_core_probe,
|
||||
.remove = spf_core_exit,
|
||||
.callback = spf_core_callback,
|
||||
.driver = {
|
||||
.name = "qcom-spf_core",
|
||||
.of_match_table = of_match_ptr(spf_core_device_id),
|
||||
},
|
||||
};
|
||||
|
||||
static void spf_core_add_child_devices(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
pr_err("%s:enumarate machine driver\n", __func__);
|
||||
|
||||
if (spf_core_is_apm_ready(ADD_CHILD_DEVICES_APM_TIMEOUT_MS)) {
|
||||
dev_err(spf_core_priv->dev, "%s: apm is up\n",
|
||||
__func__);
|
||||
} else {
|
||||
dev_err(spf_core_priv->dev, "%s: apm is not up\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = of_platform_populate(spf_core_priv->dev->of_node,
|
||||
NULL, NULL, spf_core_priv->dev);
|
||||
if (ret)
|
||||
dev_err(spf_core_priv->dev, "%s: failed to add child nodes, ret=%d\n",
|
||||
__func__, ret);
|
||||
|
||||
spf_core_priv->is_initial_boot = false;
|
||||
|
||||
}
|
||||
|
||||
static int spf_core_platform_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
pr_err("%s",__func__);
|
||||
|
||||
spf_core_priv = devm_kzalloc(&pdev->dev, sizeof(struct spf_core_private), GFP_KERNEL);
|
||||
if (!spf_core_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
spf_core_priv->dev = &pdev->dev;
|
||||
|
||||
mutex_init(&spf_core_priv->lock);
|
||||
|
||||
INIT_WORK(&spf_core_priv->add_chld_dev_work, spf_core_add_child_devices);
|
||||
|
||||
spf_core_priv->is_initial_boot = true;
|
||||
ret = gpr_driver_register(&qcom_spf_core_driver);
|
||||
if (ret) {
|
||||
pr_err("%s: gpr driver register failed = %d\n",
|
||||
__func__, ret);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
ret = snd_event_client_register(&pdev->dev, &gpr_ssr_ops, NULL);
|
||||
if (ret) {
|
||||
pr_err("%s: Registration with SND event fwk failed ret = %d\n",
|
||||
__func__, ret);
|
||||
ret = 0;
|
||||
}
|
||||
#endif
|
||||
digital_cdc_rsc_mgr_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spf_core_platform_driver_remove(struct platform_device *pdev)
|
||||
{
|
||||
//snd_event_client_deregister(&pdev->dev);
|
||||
gpr_driver_unregister(&qcom_spf_core_driver);
|
||||
spf_core_priv = NULL;
|
||||
digital_cdc_rsc_mgr_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id spf_core_of_match[] = {
|
||||
{ .compatible = "qcom,spf-core-platform", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver spf_core_driver = {
|
||||
.probe = spf_core_platform_driver_probe,
|
||||
.remove = spf_core_platform_driver_remove,
|
||||
.driver = {
|
||||
.name = "spf-core-platform",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = spf_core_of_match,
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(spf_core_driver);
|
||||
|
||||
MODULE_DESCRIPTION("q6 core");
|
||||
MODULE_LICENSE("GPL v2");
|
Reference in New Issue
Block a user