diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2018-05-27 09:11:14 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2018-05-27 09:11:14 -0700 |
| commit | 2115d78a54e930848afc29593e5a5b86d00f073b (patch) | |
| tree | 69e2dc8ea5fcab9e48f7159519fc8a75f632b0f0 | |
| parent | fd10e25d1831386337a3234694789ac9a074ef94 (diff) | |
| parent | 836a4d5bf39be4451174d08a10b717a0493415fd (diff) | |
Merge "ARM: dts: msm: add lpass resource mgr on automotive msm8996"
| -rw-r--r-- | Documentation/devicetree/bindings/sound/qcom-audio-dev.txt | 62 | ||||
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8996-agave-adp.dtsi | 14 | ||||
| -rw-r--r-- | arch/arm/boot/dts/qcom/msm8996-auto-cdp.dtsi | 14 | ||||
| -rw-r--r-- | drivers/soc/qcom/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/soc/qcom/qdsp6v2/Makefile | 2 | ||||
| -rw-r--r-- | drivers/soc/qcom/qdsp6v2/lpass_resource_mgr.c | 552 |
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"); |
