256 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2021, Linaro Limited3 4#include <linux/slab.h>5#include <linux/wait.h>6#include <linux/kernel.h>7#include <linux/module.h>8#include <linux/of.h>9#include <linux/delay.h>10#include <linux/of_platform.h>11#include <linux/jiffies.h>12#include <linux/soc/qcom/apr.h>13#include <dt-bindings/soc/qcom,gpr.h>14#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>15#include "q6apm.h"16#include "q6prm.h"17#include "audioreach.h"18 19struct q6prm {20 struct device *dev;21 gpr_device_t *gdev;22 wait_queue_head_t wait;23 struct gpr_ibasic_rsp_result_t result;24 struct mutex lock;25};26 27#define PRM_CMD_REQUEST_HW_RSC 0x0100100F28#define PRM_CMD_RSP_REQUEST_HW_RSC 0x0200100229#define PRM_CMD_RELEASE_HW_RSC 0x0100101030#define PRM_CMD_RSP_RELEASE_HW_RSC 0x0200100331#define PARAM_ID_RSC_HW_CORE 0x0800103232#define PARAM_ID_RSC_LPASS_CORE 0x0800102B33#define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C34 35struct prm_cmd_request_hw_core {36 struct apm_module_param_data param_data;37 uint32_t hw_clk_id;38} __packed;39 40struct prm_cmd_request_rsc {41 struct apm_module_param_data param_data;42 uint32_t num_clk_id;43 struct audio_hw_clk_cfg clock_id;44} __packed;45 46struct prm_cmd_release_rsc {47 struct apm_module_param_data param_data;48 uint32_t num_clk_id;49 struct audio_hw_clk_rel_cfg clock_id;50} __packed;51 52static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode)53{54 return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock,55 NULL, &prm->wait, pkt, rsp_opcode);56}57 58static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable)59{60 struct q6prm *prm = dev_get_drvdata(dev->parent);61 struct apm_module_param_data *param_data;62 struct prm_cmd_request_hw_core *req;63 gpr_device_t *gdev = prm->gdev;64 uint32_t opcode, rsp_opcode;65 struct gpr_pkt *pkt;66 int rc;67 68 if (enable) {69 opcode = PRM_CMD_REQUEST_HW_RSC;70 rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC;71 } else {72 opcode = PRM_CMD_RELEASE_HW_RSC;73 rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC;74 }75 76 pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID);77 if (IS_ERR(pkt))78 return PTR_ERR(pkt);79 80 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;81 82 param_data = &req->param_data;83 84 param_data->module_instance_id = GPR_PRM_MODULE_IID;85 param_data->error_code = 0;86 param_data->param_id = PARAM_ID_RSC_HW_CORE;87 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;88 89 req->hw_clk_id = hw_block_id;90 91 rc = q6prm_send_cmd_sync(prm, pkt, rsp_opcode);92 93 kfree(pkt);94 95 return rc;96}97 98int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id,99 const char *client_name, uint32_t *client_handle)100{101 return q6prm_set_hw_core_req(dev, hw_block_id, true);102 103}104EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw);105 106int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle)107{108 return q6prm_set_hw_core_req(dev, hw_block_id, false);109}110EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw);111 112static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,113 unsigned int freq)114{115 struct q6prm *prm = dev_get_drvdata(dev->parent);116 struct apm_module_param_data *param_data;117 struct prm_cmd_request_rsc *req;118 gpr_device_t *gdev = prm->gdev;119 struct gpr_pkt *pkt;120 int rc;121 122 pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id,123 GPR_PRM_MODULE_IID);124 if (IS_ERR(pkt))125 return PTR_ERR(pkt);126 127 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;128 129 param_data = &req->param_data;130 131 param_data->module_instance_id = GPR_PRM_MODULE_IID;132 param_data->error_code = 0;133 param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;134 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE;135 136 req->num_clk_id = 1;137 req->clock_id.clock_id = clk_id;138 req->clock_id.clock_freq = freq;139 req->clock_id.clock_attri = clk_attr;140 req->clock_id.clock_root = clk_root;141 142 rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC);143 144 kfree(pkt);145 146 return rc;147}148 149static int q6prm_release_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,150 unsigned int freq)151{152 struct q6prm *prm = dev_get_drvdata(dev->parent);153 struct apm_module_param_data *param_data;154 struct prm_cmd_release_rsc *rel;155 gpr_device_t *gdev = prm->gdev;156 struct gpr_pkt *pkt;157 int rc;158 159 pkt = audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, gdev->svc.id,160 GPR_PRM_MODULE_IID);161 if (IS_ERR(pkt))162 return PTR_ERR(pkt);163 164 rel = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;165 166 param_data = &rel->param_data;167 168 param_data->module_instance_id = GPR_PRM_MODULE_IID;169 param_data->error_code = 0;170 param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK;171 param_data->param_size = sizeof(*rel) - APM_MODULE_PARAM_DATA_SIZE;172 173 rel->num_clk_id = 1;174 rel->clock_id.clock_id = clk_id;175 176 rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_RELEASE_HW_RSC);177 178 kfree(pkt);179 180 return rc;181}182 183int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root,184 unsigned int freq)185{186 if (freq)187 return q6prm_request_lpass_clock(dev, clk_id, clk_attr, clk_root, freq);188 189 return q6prm_release_lpass_clock(dev, clk_id, clk_attr, clk_root, freq);190}191EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock);192 193static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op)194{195 gpr_device_t *gdev = priv;196 struct q6prm *prm = dev_get_drvdata(&gdev->dev);197 struct gpr_ibasic_rsp_result_t *result;198 struct gpr_hdr *hdr = &data->hdr;199 200 switch (hdr->opcode) {201 case PRM_CMD_RSP_REQUEST_HW_RSC:202 case PRM_CMD_RSP_RELEASE_HW_RSC:203 result = data->payload;204 prm->result.opcode = hdr->opcode;205 prm->result.status = result->status;206 wake_up(&prm->wait);207 break;208 default:209 break;210 }211 212 return 0;213}214 215static int prm_probe(gpr_device_t *gdev)216{217 struct device *dev = &gdev->dev;218 struct q6prm *cc;219 220 cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);221 if (!cc)222 return -ENOMEM;223 224 cc->dev = dev;225 cc->gdev = gdev;226 mutex_init(&cc->lock);227 init_waitqueue_head(&cc->wait);228 dev_set_drvdata(dev, cc);229 230 if (!q6apm_is_adsp_ready())231 return -EPROBE_DEFER;232 233 return devm_of_platform_populate(dev);234}235 236#ifdef CONFIG_OF237static const struct of_device_id prm_device_id[] = {238 { .compatible = "qcom,q6prm" },239 {},240};241MODULE_DEVICE_TABLE(of, prm_device_id);242#endif243 244static gpr_driver_t prm_driver = {245 .probe = prm_probe,246 .gpr_callback = prm_callback,247 .driver = {248 .name = "qcom-prm",249 .of_match_table = of_match_ptr(prm_device_id),250 },251};252 253module_gpr_driver(prm_driver);254MODULE_DESCRIPTION("Q6 Proxy Resource Manager");255MODULE_LICENSE("GPL");256