1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2021, Linaro Limited 3 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 19 struct 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 0x0100100F 28 #define PRM_CMD_RSP_REQUEST_HW_RSC 0x02001002 29 #define PRM_CMD_RELEASE_HW_RSC 0x01001010 30 #define PRM_CMD_RSP_RELEASE_HW_RSC 0x02001003 31 #define PARAM_ID_RSC_HW_CORE 0x08001032 32 #define PARAM_ID_RSC_LPASS_CORE 0x0800102B 33 #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C 34 #define PARAM_ID_RSC_CPU_LPR 0x08001A6E 35 36 #define LPR_CPU_SS_SLEEP_DISABLE 0x1 37 38 struct prm_cmd_request_hw_core { 39 struct apm_module_param_data param_data; 40 union { 41 u32 hw_clk_id; 42 u32 lpr_state; 43 }; 44 } __packed; 45 46 struct prm_cmd_request_rsc { 47 struct apm_module_param_data param_data; 48 uint32_t num_clk_id; 49 struct audio_hw_clk_cfg clock_id; 50 } __packed; 51 52 struct prm_cmd_release_rsc { 53 struct apm_module_param_data param_data; 54 uint32_t num_clk_id; 55 struct audio_hw_clk_rel_cfg clock_id; 56 } __packed; 57 58 static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode) 59 { 60 return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock, 61 NULL, &prm->wait, pkt, rsp_opcode); 62 } 63 64 static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable) 65 { 66 struct q6prm *prm = dev_get_drvdata(dev->parent); 67 struct apm_module_param_data *param_data; 68 struct prm_cmd_request_hw_core *req; 69 gpr_device_t *gdev = prm->gdev; 70 uint32_t opcode, rsp_opcode; 71 bool lpr_req = (hw_block_id == Q6PRM_HW_LPR_VOTE); 72 73 if (enable) { 74 opcode = PRM_CMD_REQUEST_HW_RSC; 75 rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC; 76 } else { 77 opcode = PRM_CMD_RELEASE_HW_RSC; 78 rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC; 79 } 80 81 struct gpr_pkt *pkt __free(kfree) = 82 audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID); 83 if (IS_ERR(pkt)) 84 return PTR_ERR(pkt); 85 86 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; 87 88 param_data = &req->param_data; 89 90 param_data->module_instance_id = GPR_PRM_MODULE_IID; 91 param_data->error_code = 0; 92 param_data->param_id = lpr_req ? PARAM_ID_RSC_CPU_LPR : PARAM_ID_RSC_HW_CORE; 93 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; 94 95 if (lpr_req) 96 req->lpr_state = LPR_CPU_SS_SLEEP_DISABLE; 97 else 98 req->hw_clk_id = hw_block_id; 99 100 return q6prm_send_cmd_sync(prm, pkt, rsp_opcode); 101 } 102 103 int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, 104 const char *client_name, uint32_t *client_handle) 105 { 106 return q6prm_set_hw_core_req(dev, hw_block_id, true); 107 108 } 109 EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw); 110 111 int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle) 112 { 113 return q6prm_set_hw_core_req(dev, hw_block_id, false); 114 } 115 EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw); 116 117 static int q6prm_request_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root, 118 unsigned int freq) 119 { 120 struct q6prm *prm = dev_get_drvdata(dev->parent); 121 struct apm_module_param_data *param_data; 122 struct prm_cmd_request_rsc *req; 123 gpr_device_t *gdev = prm->gdev; 124 125 struct gpr_pkt *pkt __free(kfree) = 126 audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, 127 gdev->svc.id, GPR_PRM_MODULE_IID); 128 if (IS_ERR(pkt)) 129 return PTR_ERR(pkt); 130 131 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; 132 133 param_data = &req->param_data; 134 135 param_data->module_instance_id = GPR_PRM_MODULE_IID; 136 param_data->error_code = 0; 137 param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK; 138 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; 139 140 req->num_clk_id = 1; 141 req->clock_id.clock_id = clk_id; 142 req->clock_id.clock_freq = freq; 143 req->clock_id.clock_attri = clk_attr; 144 req->clock_id.clock_root = clk_root; 145 146 return q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC); 147 } 148 149 static 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 157 struct gpr_pkt *pkt __free(kfree) = 158 audioreach_alloc_cmd_pkt(sizeof(*rel), PRM_CMD_RELEASE_HW_RSC, 0, 159 gdev->svc.id, GPR_PRM_MODULE_IID); 160 if (IS_ERR(pkt)) 161 return PTR_ERR(pkt); 162 163 rel = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; 164 165 param_data = &rel->param_data; 166 167 param_data->module_instance_id = GPR_PRM_MODULE_IID; 168 param_data->error_code = 0; 169 param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK; 170 param_data->param_size = sizeof(*rel) - APM_MODULE_PARAM_DATA_SIZE; 171 172 rel->num_clk_id = 1; 173 rel->clock_id.clock_id = clk_id; 174 175 return q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_RELEASE_HW_RSC); 176 } 177 178 int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root, 179 unsigned int freq) 180 { 181 if (freq) 182 return q6prm_request_lpass_clock(dev, clk_id, clk_attr, clk_root, freq); 183 184 return q6prm_release_lpass_clock(dev, clk_id, clk_attr, clk_root, freq); 185 } 186 EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock); 187 188 static int prm_callback(const struct gpr_resp_pkt *data, void *priv, int op) 189 { 190 gpr_device_t *gdev = priv; 191 struct q6prm *prm = dev_get_drvdata(&gdev->dev); 192 const struct gpr_ibasic_rsp_result_t *result; 193 const struct gpr_hdr *hdr = &data->hdr; 194 195 switch (hdr->opcode) { 196 case PRM_CMD_RSP_REQUEST_HW_RSC: 197 case PRM_CMD_RSP_RELEASE_HW_RSC: 198 result = data->payload; 199 prm->result.opcode = hdr->opcode; 200 prm->result.status = result->status; 201 wake_up(&prm->wait); 202 break; 203 default: 204 break; 205 } 206 207 return 0; 208 } 209 210 static int prm_probe(gpr_device_t *gdev) 211 { 212 struct device *dev = &gdev->dev; 213 struct q6prm *cc; 214 215 cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); 216 if (!cc) 217 return -ENOMEM; 218 219 cc->dev = dev; 220 cc->gdev = gdev; 221 mutex_init(&cc->lock); 222 init_waitqueue_head(&cc->wait); 223 dev_set_drvdata(dev, cc); 224 225 if (!q6apm_is_adsp_ready()) 226 return -EPROBE_DEFER; 227 228 return devm_of_platform_populate(dev); 229 } 230 231 #ifdef CONFIG_OF 232 static const struct of_device_id prm_device_id[] = { 233 { .compatible = "qcom,q6prm" }, 234 {}, 235 }; 236 MODULE_DEVICE_TABLE(of, prm_device_id); 237 #endif 238 239 static gpr_driver_t prm_driver = { 240 .probe = prm_probe, 241 .gpr_callback = prm_callback, 242 .driver = { 243 .name = "qcom-prm", 244 .of_match_table = of_match_ptr(prm_device_id), 245 }, 246 }; 247 248 module_gpr_driver(prm_driver); 249 MODULE_DESCRIPTION("Q6 Proxy Resource Manager"); 250 MODULE_LICENSE("GPL"); 251