ASoC: qdsp6: audioreach: add q6prm support

Add support to q6prm (Proxy Resource Manager) module used for clock resources

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20211026111655.1702-17-srinivas.kandagatla@linaro.org
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Srinivas Kandagatla 2021-10-26 12:16:54 +01:00 committed by Mark Brown
parent 30ad723b93
commit 9a0e5d6fb1
No known key found for this signature in database
GPG key ID: 24D68B725D5487D0
4 changed files with 285 additions and 0 deletions

View file

@ -97,6 +97,9 @@ config SND_SOC_QDSP6_APM
select SND_SOC_QDSP6_APM_DAI select SND_SOC_QDSP6_APM_DAI
select SND_SOC_QDSP6_APM_LPASS_DAI select SND_SOC_QDSP6_APM_LPASS_DAI
config SND_SOC_QDSP6_PRM
tristate
config SND_SOC_QDSP6 config SND_SOC_QDSP6
tristate "SoC ALSA audio driver for QDSP6" tristate "SoC ALSA audio driver for QDSP6"
depends on QCOM_APR depends on QCOM_APR
@ -112,6 +115,7 @@ config SND_SOC_QDSP6
select SND_SOC_QDSP6_ASM_DAI select SND_SOC_QDSP6_ASM_DAI
select SND_SOC_TOPOLOGY select SND_SOC_TOPOLOGY
select SND_SOC_QDSP6_APM select SND_SOC_QDSP6_APM
select SND_SOC_QDSP6_PRM
help help
To add support for MSM QDSP6 Soc Audio. To add support for MSM QDSP6 Soc Audio.
This will enable sound soc platform specific This will enable sound soc platform specific

View file

@ -15,3 +15,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o
obj-$(CONFIG_SND_SOC_QDSP6_APM) += snd-q6apm.o obj-$(CONFIG_SND_SOC_QDSP6_APM) += snd-q6apm.o
obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o
obj-$(CONFIG_SND_SOC_QDSP6_APM_LPASS_DAI) += q6apm-lpass-dais.o obj-$(CONFIG_SND_SOC_QDSP6_APM_LPASS_DAI) += q6apm-lpass-dais.o
obj-$(CONFIG_SND_SOC_QDSP6_PRM) += q6prm.o

View file

@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2021, Linaro Limited
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/of_platform.h>
#include <linux/jiffies.h>
#include <linux/soc/qcom/apr.h>
#include <dt-bindings/soc/qcom,gpr.h>
#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
#include "q6prm.h"
#include "audioreach.h"
struct q6prm {
struct device *dev;
gpr_device_t *gdev;
wait_queue_head_t wait;
struct gpr_ibasic_rsp_result_t result;
struct mutex lock;
};
#define PRM_CMD_REQUEST_HW_RSC 0x0100100F
#define PRM_CMD_RSP_REQUEST_HW_RSC 0x02001002
#define PRM_CMD_RELEASE_HW_RSC 0x01001010
#define PRM_CMD_RSP_RELEASE_HW_RSC 0x02001003
#define PARAM_ID_RSC_HW_CORE 0x08001032
#define PARAM_ID_RSC_LPASS_CORE 0x0800102B
#define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C
struct prm_cmd_request_hw_core {
struct apm_module_param_data param_data;
uint32_t hw_clk_id;
} __packed;
struct prm_cmd_request_rsc {
struct apm_module_param_data param_data;
uint32_t num_clk_id;
struct audio_hw_clk_cfg clock_id;
} __packed;
static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
{
return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,
NULL, &prm->wait, pkt, rsp_opcode);
}
static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable)
{
struct q6prm *prm = dev_get_drvdata(dev->parent);
struct apm_module_param_data *param_data;
struct prm_cmd_request_hw_core *req;
gpr_device_t *gdev = prm->gdev;
uint32_t opcode, rsp_opcode;
struct gpr_pkt *pkt;
int rc;
if (enable) {
opcode = PRM_CMD_REQUEST_HW_RSC;
rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC;
} else {
opcode = PRM_CMD_RELEASE_HW_RSC;
rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;
}
pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = &req->param_data;
param_data->module_instance_id = GPR_PRM_MODULE_IID;
param_data->error_code = 0;
param_data->param_id = PARAM_ID_RSC_HW_CORE;
param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
req->hw_clk_id = hw_block_id;
rc = q6prm_send_cmd_sync(prm, pkt, rsp_opcode);
kfree(pkt);
return rc;
}
int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
const char *client_name, uint32_t *client_handle)
{
return q6prm_set_hw_core_req(dev, hw_block_id, true);
}
EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw);
int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle)
{
return q6prm_set_hw_core_req(dev, hw_block_id, false);
}
EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw);
int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
unsigned int freq)
{
struct q6prm *prm = dev_get_drvdata(dev->parent);
struct apm_module_param_data *param_data;
struct prm_cmd_request_rsc *req;
gpr_device_t *gdev = prm->gdev;
struct gpr_pkt *pkt;
int rc;
pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,
GPR_PRM_MODULE_IID);
if (IS_ERR(pkt))
return PTR_ERR(pkt);
req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
param_data = &req->param_data;
param_data->module_instance_id = GPR_PRM_MODULE_IID;
param_data->error_code = 0;
param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
req->num_clk_id = 1;
req->clock_id.clock_id = clk_id;
req->clock_id.clock_freq = freq;
req->clock_id.clock_attri = clk_attr;
req->clock_id.clock_root = clk_root;
rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC);
kfree(pkt);
return rc;
}
EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);
static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)
{
gpr_device_t *gdev = priv;
struct q6prm *prm = dev_get_drvdata(&gdev->dev);
struct gpr_ibasic_rsp_result_t *result;
struct gpr_hdr *hdr = &data->hdr;
switch (hdr->opcode) {
case PRM_CMD_RSP_REQUEST_HW_RSC:
case PRM_CMD_RSP_RELEASE_HW_RSC:
result = data->payload;
prm->result.opcode = hdr->opcode;
prm->result.status = result->status;
wake_up(&prm->wait);
break;
default:
break;
}
return 0;
}
static int prm_probe(gpr_device_t *gdev)
{
struct device *dev = &gdev->dev;
struct q6prm *cc;
cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
if (!cc)
return -ENOMEM;
cc->dev = dev;
cc->gdev = gdev;
mutex_init(&cc->lock);
init_waitqueue_head(&cc->wait);
dev_set_drvdata(dev, cc);
return devm_of_platform_populate(dev);
}
#ifdef CONFIG_OF
static const struct of_device_id prm_device_id[] = {
{ .compatible = "qcom,q6prm" },
{},
};
MODULE_DEVICE_TABLE(of, prm_device_id);
#endif
static gpr_driver_t prm_driver = {
.probe = prm_probe,
.gpr_callback = prm_callback,
.driver = {
.name = "qcom-prm",
.of_match_table = of_match_ptr(prm_device_id),
},
};
module_gpr_driver(prm_driver);
MODULE_DESCRIPTION("Audio Process Manager");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __Q6PRM_H__
#define __Q6PRM_H__
/* Clock ID for Primary I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100
/* Clock ID for Primary I2S EBIT */
#define Q6PRM_LPASS_CLK_ID_PRI_MI2S_EBIT 0x101
/* Clock ID for Secondary I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_SEC_MI2S_IBIT 0x102
/* Clock ID for Secondary I2S EBIT */
#define Q6PRM_LPASS_CLK_ID_SEC_MI2S_EBIT 0x103
/* Clock ID for Tertiary I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_TER_MI2S_IBIT 0x104
/* Clock ID for Tertiary I2S EBIT */
#define Q6PRM_LPASS_CLK_ID_TER_MI2S_EBIT 0x105
/* Clock ID for Quartnery I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_QUAD_MI2S_IBIT 0x106
/* Clock ID for Quartnery I2S EBIT */
#define Q6PRM_LPASS_CLK_ID_QUAD_MI2S_EBIT 0x107
/* Clock ID for Speaker I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_IBIT 0x108
/* Clock ID for Speaker I2S EBIT */
#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_EBIT 0x109
/* Clock ID for Speaker I2S OSR */
#define Q6PRM_LPASS_CLK_ID_SPEAKER_I2S_OSR 0x10A
/* Clock ID for QUINARY I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_IBIT 0x10B
/* Clock ID for QUINARY I2S EBIT */
#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_EBIT 0x10C
/* Clock ID for SENARY I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_SEN_MI2S_IBIT 0x10D
/* Clock ID for SENARY I2S EBIT */
#define Q6PRM_LPASS_CLK_ID_SEN_MI2S_EBIT 0x10E
/* Clock ID for INT0 I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_INT0_MI2S_IBIT 0x10F
/* Clock ID for INT1 I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_INT1_MI2S_IBIT 0x110
/* Clock ID for INT2 I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_INT2_MI2S_IBIT 0x111
/* Clock ID for INT3 I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_INT3_MI2S_IBIT 0x112
/* Clock ID for INT4 I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_INT4_MI2S_IBIT 0x113
/* Clock ID for INT5 I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_INT5_MI2S_IBIT 0x114
/* Clock ID for INT6 I2S IBIT */
#define Q6PRM_LPASS_CLK_ID_INT6_MI2S_IBIT 0x115
/* Clock ID for QUINARY MI2S OSR CLK */
#define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116
#define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305
#define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306
#define Q6PRM_LPASS_CLK_ID_VA_CORE_MCLK 0x307
#define Q6PRM_LPASS_CLK_ID_VA_CORE_2X_MCLK 0x308
#define Q6PRM_LPASS_CLK_ID_TX_CORE_MCLK 0x30c
#define Q6PRM_LPASS_CLK_ID_TX_CORE_NPL_MCLK 0x30d
#define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK 0x30e
#define Q6PRM_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f
#define Q6PRM_LPASS_CLK_SRC_INTERNAL 1
#define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0
#define Q6PRM_HW_CORE_ID_LPASS 1
#define Q6PRM_HW_CORE_ID_DCODEC 2
int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr,
int clk_root, unsigned int freq);
int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
const char *client_name, uint32_t *client_handle);
int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
uint32_t client_handle);
#endif /* __Q6PRM_H__ */