0001
0002
0003
0004 #include <linux/slab.h>
0005 #include <linux/wait.h>
0006 #include <linux/kernel.h>
0007 #include <linux/module.h>
0008 #include <linux/of.h>
0009 #include <linux/delay.h>
0010 #include <linux/of_platform.h>
0011 #include <linux/jiffies.h>
0012 #include <linux/soc/qcom/apr.h>
0013 #include <dt-bindings/soc/qcom,gpr.h>
0014 #include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
0015 #include "q6prm.h"
0016 #include "audioreach.h"
0017
0018 struct q6prm {
0019 struct device *dev;
0020 gpr_device_t *gdev;
0021 wait_queue_head_t wait;
0022 struct gpr_ibasic_rsp_result_t result;
0023 struct mutex lock;
0024 };
0025
0026 #define PRM_CMD_REQUEST_HW_RSC 0x0100100F
0027 #define PRM_CMD_RSP_REQUEST_HW_RSC 0x02001002
0028 #define PRM_CMD_RELEASE_HW_RSC 0x01001010
0029 #define PRM_CMD_RSP_RELEASE_HW_RSC 0x02001003
0030 #define PARAM_ID_RSC_HW_CORE 0x08001032
0031 #define PARAM_ID_RSC_LPASS_CORE 0x0800102B
0032 #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C
0033
0034 struct prm_cmd_request_hw_core {
0035 struct apm_module_param_data param_data;
0036 uint32_t hw_clk_id;
0037 } __packed;
0038
0039 struct prm_cmd_request_rsc {
0040 struct apm_module_param_data param_data;
0041 uint32_t num_clk_id;
0042 struct audio_hw_clk_cfg clock_id;
0043 } __packed;
0044
0045 struct prm_cmd_release_rsc {
0046 struct apm_module_param_data param_data;
0047 uint32_t num_clk_id;
0048 struct audio_hw_clk_rel_cfg clock_id;
0049 } __packed;
0050
0051 static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
0052 {
0053 return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,
0054 NULL, &prm->wait, pkt, rsp_opcode);
0055 }
0056
0057 static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable)
0058 {
0059 struct q6prm *prm = dev_get_drvdata(dev->parent);
0060 struct apm_module_param_data *param_data;
0061 struct prm_cmd_request_hw_core *req;
0062 gpr_device_t *gdev = prm->gdev;
0063 uint32_t opcode, rsp_opcode;
0064 struct gpr_pkt *pkt;
0065 int rc;
0066
0067 if (enable) {
0068 opcode = PRM_CMD_REQUEST_HW_RSC;
0069 rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC;
0070 } else {
0071 opcode = PRM_CMD_RELEASE_HW_RSC;
0072 rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;
0073 }
0074
0075 pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);
0076 if (IS_ERR(pkt))
0077 return PTR_ERR(pkt);
0078
0079 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
0080
0081 param_data = &req->param_data;
0082
0083 param_data->module_instance_id = GPR_PRM_MODULE_IID;
0084 param_data->error_code = 0;
0085 param_data->param_id = PARAM_ID_RSC_HW_CORE;
0086 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
0087
0088 req->hw_clk_id = hw_block_id;
0089
0090 rc = q6prm_send_cmd_sync(prm, pkt, rsp_opcode);
0091
0092 kfree(pkt);
0093
0094 return rc;
0095 }
0096
0097 int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,
0098 const char *client_name, uint32_t *client_handle)
0099 {
0100 return q6prm_set_hw_core_req(dev, hw_block_id, true);
0101
0102 }
0103 EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw);
0104
0105 int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle)
0106 {
0107 return q6prm_set_hw_core_req(dev, hw_block_id, false);
0108 }
0109 EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw);
0110
0111 static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
0112 unsigned int freq)
0113 {
0114 struct q6prm *prm = dev_get_drvdata(dev->parent);
0115 struct apm_module_param_data *param_data;
0116 struct prm_cmd_request_rsc *req;
0117 gpr_device_t *gdev = prm->gdev;
0118 struct gpr_pkt *pkt;
0119 int rc;
0120
0121 pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,
0122 GPR_PRM_MODULE_IID);
0123 if (IS_ERR(pkt))
0124 return PTR_ERR(pkt);
0125
0126 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
0127
0128 param_data = &req->param_data;
0129
0130 param_data->module_instance_id = GPR_PRM_MODULE_IID;
0131 param_data->error_code = 0;
0132 param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
0133 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;
0134
0135 req->num_clk_id = 1;
0136 req->clock_id.clock_id = clk_id;
0137 req->clock_id.clock_freq = freq;
0138 req->clock_id.clock_attri = clk_attr;
0139 req->clock_id.clock_root = clk_root;
0140
0141 rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC);
0142
0143 kfree(pkt);
0144
0145 return rc;
0146 }
0147
0148 static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
0149 unsigned int freq)
0150 {
0151 struct q6prm *prm = dev_get_drvdata(dev->parent);
0152 struct apm_module_param_data *param_data;
0153 struct prm_cmd_release_rsc *rel;
0154 gpr_device_t *gdev = prm->gdev;
0155 struct gpr_pkt *pkt;
0156 int rc;
0157
0158 pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id,
0159 GPR_PRM_MODULE_IID);
0160 if (IS_ERR(pkt))
0161 return PTR_ERR(pkt);
0162
0163 rel = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
0164
0165 param_data = &rel->param_data;
0166
0167 param_data->module_instance_id = GPR_PRM_MODULE_IID;
0168 param_data->error_code = 0;
0169 param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;
0170 param_data->param_size = sizeof(*rel) - APM_MODULE_PARAM_DATA_SIZE;
0171
0172 rel->num_clk_id = 1;
0173 rel->clock_id.clock_id = clk_id;
0174
0175 rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_RELEASE_HW_RSC);
0176
0177 kfree(pkt);
0178
0179 return rc;
0180 }
0181
0182 int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,
0183 unsigned int freq)
0184 {
0185 if (freq)
0186 return q6prm_request_lpass_clock(dev, clk_id, clk_attr, clk_attr, freq);
0187
0188 return q6prm_release_lpass_clock(dev, clk_id, clk_attr, clk_attr, freq);
0189 }
0190 EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);
0191
0192 static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)
0193 {
0194 gpr_device_t *gdev = priv;
0195 struct q6prm *prm = dev_get_drvdata(&gdev->dev);
0196 struct gpr_ibasic_rsp_result_t *result;
0197 struct gpr_hdr *hdr = &data->hdr;
0198
0199 switch (hdr->opcode) {
0200 case PRM_CMD_RSP_REQUEST_HW_RSC:
0201 case PRM_CMD_RSP_RELEASE_HW_RSC:
0202 result = data->payload;
0203 prm->result.opcode = hdr->opcode;
0204 prm->result.status = result->status;
0205 wake_up(&prm->wait);
0206 break;
0207 default:
0208 break;
0209 }
0210
0211 return 0;
0212 }
0213
0214 static int prm_probe(gpr_device_t *gdev)
0215 {
0216 struct device *dev = &gdev->dev;
0217 struct q6prm *cc;
0218
0219 cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
0220 if (!cc)
0221 return -ENOMEM;
0222
0223 cc->dev = dev;
0224 cc->gdev = gdev;
0225 mutex_init(&cc->lock);
0226 init_waitqueue_head(&cc->wait);
0227 dev_set_drvdata(dev, cc);
0228
0229 return devm_of_platform_populate(dev);
0230 }
0231
0232 #ifdef CONFIG_OF
0233 static const struct of_device_id prm_device_id[] = {
0234 { .compatible = "qcom,q6prm" },
0235 {},
0236 };
0237 MODULE_DEVICE_TABLE(of, prm_device_id);
0238 #endif
0239
0240 static gpr_driver_t prm_driver = {
0241 .probe = prm_probe,
0242 .gpr_callback = prm_callback,
0243 .driver = {
0244 .name = "qcom-prm",
0245 .of_match_table = of_match_ptr(prm_device_id),
0246 },
0247 };
0248
0249 module_gpr_driver(prm_driver);
0250 MODULE_DESCRIPTION("Audio Process Manager");
0251 MODULE_LICENSE("GPL");