xref: /kernel/linux/linux-6.6/sound/soc/qcom/qdsp6/q6prm.c (revision 62306a36)
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
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		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
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_OF
237static const struct of_device_id prm_device_id[]  = {
238	{ .compatible = "qcom,q6prm" },
239	{},
240};
241MODULE_DEVICE_TABLE(of, prm_device_id);
242#endif
243
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