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 "q6prm.h" 16 #include "audioreach.h" 17 18 struct q6prm { 19 struct device *dev; 20 gpr_device_t *gdev; 21 wait_queue_head_t wait; 22 struct gpr_ibasic_rsp_result_t result; 23 struct mutex lock; 24 }; 25 26 #define PRM_CMD_REQUEST_HW_RSC 0x0100100F 27 #define PRM_CMD_RSP_REQUEST_HW_RSC 0x02001002 28 #define PRM_CMD_RELEASE_HW_RSC 0x01001010 29 #define PRM_CMD_RSP_RELEASE_HW_RSC 0x02001003 30 #define PARAM_ID_RSC_HW_CORE 0x08001032 31 #define PARAM_ID_RSC_LPASS_CORE 0x0800102B 32 #define PARAM_ID_RSC_AUDIO_HW_CLK 0x0800102C 33 34 struct prm_cmd_request_hw_core { 35 struct apm_module_param_data param_data; 36 uint32_t hw_clk_id; 37 } __packed; 38 39 struct prm_cmd_request_rsc { 40 struct apm_module_param_data param_data; 41 uint32_t num_clk_id; 42 struct audio_hw_clk_cfg clock_id; 43 } __packed; 44 45 static int q6prm_send_cmd_sync(struct q6prm *prm, struct gpr_pkt *pkt, uint32_t rsp_opcode) 46 { 47 return audioreach_send_cmd_sync(prm->dev, prm->gdev, &prm->result, &prm->lock, 48 NULL, &prm->wait, pkt, rsp_opcode); 49 } 50 51 static int q6prm_set_hw_core_req(struct device *dev, uint32_t hw_block_id, bool enable) 52 { 53 struct q6prm *prm = dev_get_drvdata(dev->parent); 54 struct apm_module_param_data *param_data; 55 struct prm_cmd_request_hw_core *req; 56 gpr_device_t *gdev = prm->gdev; 57 uint32_t opcode, rsp_opcode; 58 struct gpr_pkt *pkt; 59 int rc; 60 61 if (enable) { 62 opcode = PRM_CMD_REQUEST_HW_RSC; 63 rsp_opcode = PRM_CMD_RSP_REQUEST_HW_RSC; 64 } else { 65 opcode = PRM_CMD_RELEASE_HW_RSC; 66 rsp_opcode = PRM_CMD_RSP_RELEASE_HW_RSC; 67 } 68 69 pkt = audioreach_alloc_cmd_pkt(sizeof(*req), opcode, 0, gdev->svc.id, GPR_PRM_MODULE_IID); 70 if (IS_ERR(pkt)) 71 return PTR_ERR(pkt); 72 73 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; 74 75 param_data = &req->param_data; 76 77 param_data->module_instance_id = GPR_PRM_MODULE_IID; 78 param_data->error_code = 0; 79 param_data->param_id = PARAM_ID_RSC_HW_CORE; 80 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; 81 82 req->hw_clk_id = hw_block_id; 83 84 rc = q6prm_send_cmd_sync(prm, pkt, rsp_opcode); 85 86 kfree(pkt); 87 88 return rc; 89 } 90 91 int q6prm_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, 92 const char *client_name, uint32_t *client_handle) 93 { 94 return q6prm_set_hw_core_req(dev, hw_block_id, true); 95 96 } 97 EXPORT_SYMBOL_GPL(q6prm_vote_lpass_core_hw); 98 99 int q6prm_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, uint32_t client_handle) 100 { 101 return q6prm_set_hw_core_req(dev, hw_block_id, false); 102 } 103 EXPORT_SYMBOL_GPL(q6prm_unvote_lpass_core_hw); 104 105 int q6prm_set_lpass_clock(struct device *dev, int clk_id, int clk_attr, int clk_root, 106 unsigned int freq) 107 { 108 struct q6prm *prm = dev_get_drvdata(dev->parent); 109 struct apm_module_param_data *param_data; 110 struct prm_cmd_request_rsc *req; 111 gpr_device_t *gdev = prm->gdev; 112 struct gpr_pkt *pkt; 113 int rc; 114 115 pkt = audioreach_alloc_cmd_pkt(sizeof(*req), PRM_CMD_REQUEST_HW_RSC, 0, gdev->svc.id, 116 GPR_PRM_MODULE_IID); 117 if (IS_ERR(pkt)) 118 return PTR_ERR(pkt); 119 120 req = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE; 121 122 param_data = &req->param_data; 123 124 param_data->module_instance_id = GPR_PRM_MODULE_IID; 125 param_data->error_code = 0; 126 param_data->param_id = PARAM_ID_RSC_AUDIO_HW_CLK; 127 param_data->param_size = sizeof(*req) - APM_MODULE_PARAM_DATA_SIZE; 128 129 req->num_clk_id = 1; 130 req->clock_id.clock_id = clk_id; 131 req->clock_id.clock_freq = freq; 132 req->clock_id.clock_attri = clk_attr; 133 req->clock_id.clock_root = clk_root; 134 135 rc = q6prm_send_cmd_sync(prm, pkt, PRM_CMD_RSP_REQUEST_HW_RSC); 136 137 kfree(pkt); 138 139 return rc; 140 } 141 EXPORT_SYMBOL_GPL(q6prm_set_lpass_clock); 142 143 static int prm_callback(struct gpr_resp_pkt *data, void *priv, int op) 144 { 145 gpr_device_t *gdev = priv; 146 struct q6prm *prm = dev_get_drvdata(&gdev->dev); 147 struct gpr_ibasic_rsp_result_t *result; 148 struct gpr_hdr *hdr = &data->hdr; 149 150 switch (hdr->opcode) { 151 case PRM_CMD_RSP_REQUEST_HW_RSC: 152 case PRM_CMD_RSP_RELEASE_HW_RSC: 153 result = data->payload; 154 prm->result.opcode = hdr->opcode; 155 prm->result.status = result->status; 156 wake_up(&prm->wait); 157 break; 158 default: 159 break; 160 } 161 162 return 0; 163 } 164 165 static int prm_probe(gpr_device_t *gdev) 166 { 167 struct device *dev = &gdev->dev; 168 struct q6prm *cc; 169 170 cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); 171 if (!cc) 172 return -ENOMEM; 173 174 cc->dev = dev; 175 cc->gdev = gdev; 176 mutex_init(&cc->lock); 177 init_waitqueue_head(&cc->wait); 178 dev_set_drvdata(dev, cc); 179 180 return devm_of_platform_populate(dev); 181 } 182 183 #ifdef CONFIG_OF 184 static const struct of_device_id prm_device_id[] = { 185 { .compatible = "qcom,q6prm" }, 186 {}, 187 }; 188 MODULE_DEVICE_TABLE(of, prm_device_id); 189 #endif 190 191 static gpr_driver_t prm_driver = { 192 .probe = prm_probe, 193 .gpr_callback = prm_callback, 194 .driver = { 195 .name = "qcom-prm", 196 .of_match_table = of_match_ptr(prm_device_id), 197 }, 198 }; 199 200 module_gpr_driver(prm_driver); 201 MODULE_DESCRIPTION("Audio Process Manager"); 202 MODULE_LICENSE("GPL"); 203