ASoC: msm: qdsp6v2: add early audio interface support
Add early audio interface support to allow synchronization between ADSP loading and early audio playback. Execute the command 'echo 0 > /sys/kernel/lpass_resource_mgr/check_early_audio' to check if early audio playback is ongoing. Output will be "Online" if early audio playback is ongoing. CRs-fixed: 2126727 Change-Id: I95d07c8f3cd93ded112b551238a973a76fb6b7ed Signed-off-by: Derek Chen <chenche@codeaurora.org>
This commit is contained in:
parent
125ff102eb
commit
cdbb6fba72
4 changed files with 627 additions and 1 deletions
|
@ -1069,6 +1069,68 @@ qcom,msm-adsp-loader {
|
|||
qcom,proc-img-to-load = "modem";
|
||||
};
|
||||
|
||||
* msm-lpass-resource-manager
|
||||
|
||||
Required properties:
|
||||
- compatible : "qcom,lpass-resource-manager"
|
||||
- qcom,lpass-lpaif-reg:
|
||||
The physical base address and size of the LPASS LPAIF registers
|
||||
should be specified here.
|
||||
- qcom,lpass-max-rddma:
|
||||
The maximum number of LPASS read DMA indices for the chipset.
|
||||
- qcom,lpass-max-wrdma:
|
||||
The maximum number of LPASS write DMA indices for the chipset.
|
||||
- qcom,num-reserved-rddma:
|
||||
The number of LPASS read DMA indices to be reserved. The value
|
||||
must be less than the value given in lpass-max-rddma.
|
||||
- qcom,num-reserved-wrdma:
|
||||
The number of LPASS write DMA indices to be reserved. The value
|
||||
must be less than the value given in lpass-max-wrdma.
|
||||
- qcom,reserved-rddma:
|
||||
The specific LPASS read DMA indices reserved by HLOS in an array.
|
||||
The number of values in this field should be equal to
|
||||
num-reserved-rddma. If num-reserved-rddma is 0, this property won't
|
||||
be read. The values themselves should be less than
|
||||
lpass-max-rddma.
|
||||
- qcom,reserved-wrdma:
|
||||
The specific LPASS write DMA indices reserved by HLOS in an array.
|
||||
The number of values in this field should be equal to
|
||||
num-reserved-wrdma. If num-reserved-wrdma is 0, this property won't
|
||||
be read. The values themselves should be less than
|
||||
lpass-max-wrdma.
|
||||
|
||||
Optional properties:
|
||||
- qcom,early-audio-enabled:
|
||||
It is possible that early audio is enabled in the bootloader. A value
|
||||
of 0 indicates that early audio is not being used. A value of 1
|
||||
will launch a thread that will check the LPASS to see if
|
||||
early audio playback is active. If it is not, the thread will
|
||||
then check the state of the LPASS. Once all services are up,
|
||||
the thread will reserve the DMA index used by early audio.
|
||||
- qcom,max-num-pcm-intf:
|
||||
The number of PCM interfaces present in the LPASS for the
|
||||
chipset.
|
||||
- qcom,early-audio-pcm:
|
||||
Must be included if early-audio-enabled is of value 1. This
|
||||
specifies the PCM interface index used by the Early Audio
|
||||
feature. The value must be less than max-num-pcm-intf.
|
||||
|
||||
Example:
|
||||
|
||||
qcom,msm-lpass-resource-manager {
|
||||
compatible = "qcom,lpass-resource-manager";
|
||||
qcom,lpass-lpaif-reg = <0x09100000 0x20000>;
|
||||
qcom,lpass-max-rddma = <5>;
|
||||
qcom,lpass-max-wrdma = <4>;
|
||||
qcom,num-reserved-rddma = <2>;
|
||||
qcom,num-reserved-wrdma = <1>;
|
||||
qcom,reserved-rddma = <0>, <1>;
|
||||
qcom,reserved-wrdma = <0>;
|
||||
qcom,early-audio-enabled = <1>;
|
||||
qcom,max-num-pcm-intf = <4>;
|
||||
qcom,early-audio-pcm = <2>;
|
||||
};
|
||||
|
||||
* msm-audio-ion
|
||||
|
||||
Required properties:
|
||||
|
|
|
@ -705,6 +705,18 @@ config MSM_CDSP_LOADER
|
|||
during boot.
|
||||
Say M if you want to enable this module.
|
||||
|
||||
config MSM_LPASS_RESOURCE_MANAGER
|
||||
tristate "LPASS Resource Manager support"
|
||||
select SND_SOC_MSM_APRV2_INTF
|
||||
depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3 || \
|
||||
MSM_QDSP6_APRV2_GLINK || MSM_QDSP6_APRV3_GLINK
|
||||
help
|
||||
Manages the allocation of LPASS resources. It also
|
||||
can check LPAIF for Early Audio playback progress.
|
||||
To check early audio playback, PCM registers are read.
|
||||
If register is enabled, playback is on-going.
|
||||
Say M if you want to enable this module.
|
||||
|
||||
config MSM_PERFORMANCE
|
||||
tristate "msm_performance driver to support perflock request"
|
||||
help
|
||||
|
|
|
@ -11,4 +11,4 @@ obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o
|
|||
obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o
|
||||
obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.o
|
||||
obj-$(CONFIG_EXT_ANC) += sdsp-anc.o audio_anc.o audio-anc-dev-mgr.o
|
||||
|
||||
obj-$(CONFIG_MSM_LPASS_RESOURCE_MANAGER) += lpass_resource_mgr.o
|
552
drivers/soc/qcom/qdsp6v2/lpass_resource_mgr.c
Normal file
552
drivers/soc/qcom/qdsp6v2/lpass_resource_mgr.c
Normal file
|
@ -0,0 +1,552 @@
|
|||
/*
|
||||
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/qdsp6v2/apr.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <sound/q6afe-v2.h>
|
||||
#include <sound/q6core.h>
|
||||
|
||||
#define LPASS_LPAIF_PCM_CTLa(a) (0x1500 + 0x1000 * (a))
|
||||
#define LPASS_LPAIF_PCM_CTLa_ELEM 4
|
||||
#define LPASS_LPAIF_PCM_CTLa_MAX 3
|
||||
#define LPASS_LPAIF_PCM_CTLa__ENABLE_TX___M 0x02000000
|
||||
|
||||
#define LPASS_RES_MGR_THREAD_NAME "lpass_resource_mgr_thread"
|
||||
|
||||
#define lpass_io_r(a) readl_relaxed(a)
|
||||
#define LPASS_REG_OFFSET(_virt_addr_, _phys_addr_) \
|
||||
((_virt_addr_)-(_phys_addr_))
|
||||
|
||||
#define CHECK_EARLY_AUDIO_CMD 0
|
||||
#define MAX_TIMEOUT_COUNT 20
|
||||
#define LPASS_CHECK_DELAY_MS 1000
|
||||
#define LPASS_BOOT_DELAY_MS 2000
|
||||
#define LPASS_STATUS_DELAY_MS 500
|
||||
|
||||
static ssize_t check_early_audio_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
|
||||
struct lpass_resource_mgr_private {
|
||||
struct kobject *lpass_resource_mgr_obj;
|
||||
struct attribute_group *attr_group;
|
||||
void __iomem *lpaif_mapped_base;
|
||||
struct task_struct *lpass_res_mgr_thread;
|
||||
uint32_t lpass_lpaif_base_addr;
|
||||
uint32_t lpass_lpaif_reg_size;
|
||||
uint32_t lpass_max_rddma;
|
||||
uint32_t lpass_max_wrdma;
|
||||
uint32_t num_reserved_rddma;
|
||||
uint32_t num_reserved_wrdma;
|
||||
uint32_t *reserved_rddma;
|
||||
uint32_t *reserved_wrdma;
|
||||
uint32_t early_audio_pcm_idx;
|
||||
u32 is_early_audio_enabled;
|
||||
};
|
||||
|
||||
static struct kobj_attribute check_early_audio_attribute =
|
||||
__ATTR(check_early_audio, 0220, NULL, check_early_audio_store);
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
&check_early_audio_attribute.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct lpass_resource_mgr_private *priv;
|
||||
|
||||
static struct platform_device *dev_private;
|
||||
|
||||
static uint32_t lpass_read_reg(void __iomem *phys_addr, uint32_t virt_offset)
|
||||
{
|
||||
uint32_t read_val;
|
||||
|
||||
read_val = lpass_io_r(phys_addr+virt_offset);
|
||||
return read_val;
|
||||
}
|
||||
|
||||
static void lpass_resource_mgr_check_early_audio(struct platform_device *pdev)
|
||||
{
|
||||
if (priv->is_early_audio_enabled)
|
||||
dev_err(&pdev->dev, "%s: Online\n",
|
||||
__func__);
|
||||
else
|
||||
dev_err(&pdev->dev, "%s: Offline\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
static int lpass_resource_mgr_thread(void *data)
|
||||
{
|
||||
struct platform_device *pdev = dev_private;
|
||||
int i, ret = 0;
|
||||
bool *ret_rddma;
|
||||
bool *ret_wrdma;
|
||||
int total_num_allocated_dma;
|
||||
int timeout_count = 0;
|
||||
|
||||
if (!pdev) {
|
||||
dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check early audio status if it's enabled */
|
||||
if (priv->is_early_audio_enabled) {
|
||||
int mask, read_val = 0;
|
||||
bool is_check_done = false;
|
||||
int pcm_idx = priv->early_audio_pcm_idx;
|
||||
|
||||
mask = LPASS_LPAIF_PCM_CTLa__ENABLE_TX___M;
|
||||
while (!is_check_done) {
|
||||
if (timeout_count > MAX_TIMEOUT_COUNT) {
|
||||
dev_err(&pdev->dev, "%s: Early audio check TIMED OUT.\n",
|
||||
__func__);
|
||||
ret = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
read_val = lpass_read_reg(priv->lpaif_mapped_base,
|
||||
LPASS_LPAIF_PCM_CTLa(pcm_idx));
|
||||
|
||||
if (!(read_val & mask)) {
|
||||
dev_dbg(&pdev->dev, "%s: PCM interface %d is disabled\n",
|
||||
__func__, pcm_idx);
|
||||
is_check_done = true;
|
||||
} else {
|
||||
dev_dbg_ratelimited(&pdev->dev,
|
||||
"%s: PCM Interface %d enabled\n",
|
||||
__func__, pcm_idx);
|
||||
}
|
||||
|
||||
msleep(LPASS_CHECK_DELAY_MS);
|
||||
timeout_count++;
|
||||
}
|
||||
priv->is_early_audio_enabled = false;
|
||||
}
|
||||
|
||||
total_num_allocated_dma = priv->num_reserved_rddma +
|
||||
priv->num_reserved_wrdma;
|
||||
if (total_num_allocated_dma == 0) {
|
||||
dev_dbg(&pdev->dev, "%s: No DMAs to allocate\n",
|
||||
__func__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
timeout_count = 0;
|
||||
while (apr_get_q6_state() == APR_SUBSYS_DOWN) {
|
||||
if (timeout_count > MAX_TIMEOUT_COUNT) {
|
||||
dev_err(&pdev->dev, "%s: apr_get_q6_state() TIMED OUT.\n",
|
||||
__func__);
|
||||
ret = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dev_dbg_ratelimited(&pdev->dev, "%s: ADSP is down\n",
|
||||
__func__);
|
||||
msleep(LPASS_BOOT_DELAY_MS);
|
||||
timeout_count++;
|
||||
}
|
||||
|
||||
timeout_count = 0;
|
||||
while (q6core_is_adsp_ready() != AVCS_SERVICE_AND_ALL_MODULES_READY) {
|
||||
if (timeout_count > MAX_TIMEOUT_COUNT) {
|
||||
dev_err(&pdev->dev, "%s: q6core_is_adsp_ready() TIMED OUT.\n",
|
||||
__func__);
|
||||
ret = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dev_dbg_ratelimited(&pdev->dev,
|
||||
"%s: Not All QADSP6 Services are ready!!\n",
|
||||
__func__);
|
||||
msleep(LPASS_STATUS_DELAY_MS);
|
||||
timeout_count++;
|
||||
}
|
||||
|
||||
/* Allocated resources then check DMA indices allocated */
|
||||
ret = afe_request_dma_resources(AFE_LPAIF_DEFAULT_DMA_TYPE,
|
||||
priv->num_reserved_rddma,
|
||||
priv->num_reserved_wrdma);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: AFE DMA Request failed with code %d\n",
|
||||
__func__, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = afe_get_dma_idx(&ret_rddma, &ret_wrdma);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: Cannot obtain DMA info %d\n",
|
||||
__func__, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->num_reserved_rddma; i++) {
|
||||
if (ret_rddma[priv->reserved_rddma[i]])
|
||||
break;
|
||||
|
||||
dev_err(&pdev->dev, "%s: ret rddma %d idx no match\n",
|
||||
__func__, priv->reserved_rddma[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->num_reserved_wrdma; i++) {
|
||||
if (ret_wrdma[priv->reserved_wrdma[i]])
|
||||
break;
|
||||
|
||||
dev_err(&pdev->dev, "%s: ret wrdma %d idx no match\n",
|
||||
__func__, priv->reserved_wrdma[i]);
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t check_early_audio_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct platform_device *pdev = dev_private;
|
||||
int cmd = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!pdev) {
|
||||
dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
|
||||
goto store_end;
|
||||
}
|
||||
|
||||
ret = sscanf(buf, "%du", &cmd);
|
||||
|
||||
if (ret != 1) {
|
||||
dev_err(&pdev->dev, "%s: Invalid number of arguments %d\n",
|
||||
__func__, ret);
|
||||
goto store_end;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CHECK_EARLY_AUDIO_CMD:
|
||||
lpass_resource_mgr_check_early_audio(dev_private);
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "%s: Unrecoginized cmd %d\n",
|
||||
__func__, cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
store_end:
|
||||
dev_dbg(&pdev->dev, "%s: Exiting. Count is %d\n",
|
||||
__func__, (int) count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int lpass_resource_mgr_init_sysfs(struct platform_device *pdev)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u32 max_num_pcm_interfaces;
|
||||
u32 lpass_lpaif_vals[2];
|
||||
|
||||
dev_private = NULL;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto priv_err_ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
priv->lpass_resource_mgr_obj = NULL;
|
||||
priv->attr_group = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*(priv->attr_group)),
|
||||
GFP_KERNEL);
|
||||
if (!priv->attr_group) {
|
||||
ret = -ENOMEM;
|
||||
goto priv_err_ret;
|
||||
}
|
||||
|
||||
priv->attr_group->attrs = attrs;
|
||||
|
||||
priv->lpass_resource_mgr_obj = kobject_create_and_add(
|
||||
"lpass_resource_mgr", kernel_kobj);
|
||||
if (!priv->lpass_resource_mgr_obj) {
|
||||
dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
|
||||
__func__);
|
||||
ret = -ENOMEM;
|
||||
goto priv_err_ret;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(priv->lpass_resource_mgr_obj,
|
||||
priv->attr_group);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
|
||||
__func__, ret);
|
||||
goto lpass_obj_err_ret;
|
||||
}
|
||||
|
||||
dev_private = pdev;
|
||||
|
||||
if (!pdev->dev.of_node) {
|
||||
dev_err(&pdev->dev, "%s: Device tree information is missing\n",
|
||||
__func__);
|
||||
ret = -ENODATA;
|
||||
goto lpass_obj_err_ret;
|
||||
}
|
||||
|
||||
/* Read Device Tree Information */
|
||||
ret = of_property_read_u32_array(pdev->dev.of_node,
|
||||
"qcom,lpass-lpaif-reg", lpass_lpaif_vals, 2);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading lpass-lpaif-reg.\n",
|
||||
__func__, ret);
|
||||
goto lpass_obj_err_ret;
|
||||
}
|
||||
|
||||
priv->lpass_lpaif_base_addr = lpass_lpaif_vals[0];
|
||||
priv->lpass_lpaif_reg_size = lpass_lpaif_vals[1];
|
||||
priv->lpaif_mapped_base = ioremap(priv->lpass_lpaif_base_addr,
|
||||
priv->lpass_lpaif_reg_size);
|
||||
if (!priv->lpaif_mapped_base) {
|
||||
dev_err(&pdev->dev, "%s: Failed to map LPASS LPAIF Base Address 0x%08x\n",
|
||||
__func__, priv->lpass_lpaif_base_addr);
|
||||
ret = -ENOMEM;
|
||||
goto lpass_obj_err_ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,lpass-max-rddma", &priv->lpass_max_rddma);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading lpass-max-rddma.\n",
|
||||
__func__, ret);
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
if (priv->lpass_max_rddma > AFE_MAX_RDDMA) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Device tree max RDDMA > kernel max\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,lpass-max-wrdma", &priv->lpass_max_wrdma);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading lpass-max-wrdma.\n",
|
||||
__func__, ret);
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
if (priv->lpass_max_wrdma > AFE_MAX_WRDMA) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Device tree max WRDMA > kernel max\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,num-reserved-rddma", &priv->num_reserved_rddma);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading num-reserved-rddma.\n",
|
||||
__func__, ret);
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,num-reserved-wrdma", &priv->num_reserved_wrdma);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading num-reserved-wrdma.\n",
|
||||
__func__, ret);
|
||||
ret = -EINVAL;
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
if ((priv->num_reserved_rddma > priv->lpass_max_rddma) ||
|
||||
(priv->num_reserved_wrdma > priv->lpass_max_wrdma)) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Reserved DMA greater than max\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
if (priv->num_reserved_rddma > 0) {
|
||||
priv->reserved_rddma = devm_kcalloc(&pdev->dev,
|
||||
priv->num_reserved_rddma,
|
||||
sizeof(uint32_t),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!priv->reserved_rddma) {
|
||||
ret = -ENOMEM;
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(pdev->dev.of_node,
|
||||
"qcom,reserved-rddma", priv->reserved_rddma,
|
||||
priv->num_reserved_rddma);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading reserved-rddma.\n",
|
||||
__func__, ret);
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->num_reserved_wrdma > 0) {
|
||||
priv->reserved_wrdma = devm_kcalloc(&pdev->dev,
|
||||
priv->num_reserved_wrdma,
|
||||
sizeof(uint32_t),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!priv->reserved_wrdma) {
|
||||
ret = -ENOMEM;
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
ret = of_property_read_u32_array(pdev->dev.of_node,
|
||||
"qcom,reserved-wrdma", priv->reserved_wrdma,
|
||||
priv->num_reserved_wrdma);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading reserved-wrdma.\n",
|
||||
__func__, ret);
|
||||
goto lpaif_map_err_ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,early-audio-enabled", &priv->is_early_audio_enabled);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev,
|
||||
"%s: Error %d reading early-audio-enabled\n",
|
||||
__func__, ret);
|
||||
priv->is_early_audio_enabled = 0;
|
||||
}
|
||||
|
||||
if (priv->is_early_audio_enabled) {
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,max-num-pcm-intf", &max_num_pcm_interfaces);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading max-num-pcm-intf\n",
|
||||
__func__, ret);
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
"qcom,early-audio-pcm", &priv->early_audio_pcm_idx);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev,
|
||||
"%s: Error %d reading early-audio-pcm\n",
|
||||
__func__, ret);
|
||||
}
|
||||
|
||||
priv->lpass_res_mgr_thread = kthread_run(
|
||||
lpass_resource_mgr_thread,
|
||||
NULL,
|
||||
LPASS_RES_MGR_THREAD_NAME);
|
||||
|
||||
return 0;
|
||||
lpaif_map_err_ret:
|
||||
if (priv->lpaif_mapped_base)
|
||||
iounmap(priv->lpaif_mapped_base);
|
||||
|
||||
lpass_obj_err_ret:
|
||||
if (priv->lpass_resource_mgr_obj) {
|
||||
kobject_del(priv->lpass_resource_mgr_obj);
|
||||
priv->lpass_resource_mgr_obj = NULL;
|
||||
}
|
||||
|
||||
priv_err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lpass_resource_mgr_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lpass_resource_mgr_private *priv = NULL;
|
||||
|
||||
priv = platform_get_drvdata(pdev);
|
||||
|
||||
if (!priv)
|
||||
return 0;
|
||||
|
||||
if (priv->lpaif_mapped_base)
|
||||
iounmap(priv->lpaif_mapped_base);
|
||||
|
||||
if (priv->lpass_resource_mgr_obj) {
|
||||
sysfs_remove_group(priv->lpass_resource_mgr_obj,
|
||||
priv->attr_group);
|
||||
|
||||
kobject_del(priv->lpass_resource_mgr_obj);
|
||||
priv->lpass_resource_mgr_obj = NULL;
|
||||
}
|
||||
|
||||
kthread_stop(priv->lpass_res_mgr_thread);
|
||||
afe_release_all_dma_resources();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpass_resource_mgr_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = lpass_resource_mgr_init_sysfs(pdev);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id lpass_resource_mgr_dt_match[] = {
|
||||
{ .compatible = "qcom,lpass-resource-manager" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpass_resource_mgr_dt_match);
|
||||
|
||||
static struct platform_driver lpass_resource_mgr_driver = {
|
||||
.driver = {
|
||||
.name = "lpass-resource-manager",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = lpass_resource_mgr_dt_match,
|
||||
},
|
||||
.probe = lpass_resource_mgr_probe,
|
||||
.remove = lpass_resource_mgr_remove,
|
||||
};
|
||||
|
||||
static int __init lpass_resource_mgr_init(void)
|
||||
{
|
||||
return platform_driver_register(&lpass_resource_mgr_driver);
|
||||
}
|
||||
module_init(lpass_resource_mgr_init);
|
||||
|
||||
static void __exit lpass_resource_mgr_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&lpass_resource_mgr_driver);
|
||||
}
|
||||
module_exit(lpass_resource_mgr_exit);
|
||||
|
||||
MODULE_DESCRIPTION("LPASS Resource Manager module");
|
||||
MODULE_LICENSE("GPL v2");
|
Loading…
Add table
Reference in a new issue