summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2018-05-27 09:11:14 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2018-05-27 09:11:14 -0700
commit2115d78a54e930848afc29593e5a5b86d00f073b (patch)
tree69e2dc8ea5fcab9e48f7159519fc8a75f632b0f0
parentfd10e25d1831386337a3234694789ac9a074ef94 (diff)
parent836a4d5bf39be4451174d08a10b717a0493415fd (diff)
Merge "ARM: dts: msm: add lpass resource mgr on automotive msm8996"
-rw-r--r--Documentation/devicetree/bindings/sound/qcom-audio-dev.txt62
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi14
-rw-r--r--arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi14
-rw-r--r--drivers/soc/qcom/Kconfig12
-rw-r--r--drivers/soc/qcom/qdsp6v2/Makefile2
-rw-r--r--drivers/soc/qcom/qdsp6v2/lpass_resource_mgr.c552
6 files changed, 655 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 4cf7b93b922e..80c3f7c462b1 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -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:
diff --git a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
index 692dd58ac956..a8e6cbad47b0 100644
--- a/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi
@@ -1152,6 +1152,20 @@
interrupt-names ="pmic_id_irq";
};
+ 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 = <0>;
+ qcom,num-reserved-wrdma = <0>;
+ qcom,reserved-rddma = <0>;
+ qcom,reserved-wrdma = <0>;
+ qcom,early-audio-enabled = <0>;
+ qcom,max-num-pcm-intf = <4>;
+ qcom,early-audio-pcm = <2>;
+ };
+
loopback1: qcom,msm-pcm-loopback-low-latency {
compatible = "qcom,msm-pcm-loopback";
qcom,msm-pcm-loopback-low-latency;
diff --git a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
index 4ece4991bef5..848a647e0172 100644
--- a/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi
@@ -1087,6 +1087,20 @@
qcom,user-type = <1>; /* user type */
};
};
+
+ 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 = <0>;
+ qcom,num-reserved-wrdma = <0>;
+ qcom,reserved-rddma = <0>;
+ qcom,reserved-wrdma = <0>;
+ qcom,early-audio-enabled = <0>;
+ qcom,max-num-pcm-intf = <4>;
+ qcom,early-audio-pcm = <2>;
+ };
};
&pm8994_gpios {
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 1b68d1602c94..62b7d12629e4 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -712,6 +712,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
diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile
index 0a0e258e6ec1..250cc88ba32d 100644
--- a/drivers/soc/qcom/qdsp6v2/Makefile
+++ b/drivers/soc/qcom/qdsp6v2/Makefile
@@ -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 \ No newline at end of file
diff --git a/drivers/soc/qcom/qdsp6v2/lpass_resource_mgr.c b/drivers/soc/qcom/qdsp6v2/lpass_resource_mgr.c
new file mode 100644
index 000000000000..6b097c0205bd
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/lpass_resource_mgr.c
@@ -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");