xref: /linux/sound/soc/qcom/qdsp6/q6core.c (revision e9f0878c4b2004ac19581274c1ae4c61ae3ca70e)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
3 // Copyright (c) 2018, Linaro Limited
4 
5 #include <linux/slab.h>
6 #include <linux/wait.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/sched.h>
10 #include <linux/of.h>
11 #include <linux/of_platform.h>
12 #include <linux/jiffies.h>
13 #include <linux/wait.h>
14 #include <linux/soc/qcom/apr.h>
15 #include "q6core.h"
16 #include "q6dsp-errno.h"
17 
18 #define ADSP_STATE_READY_TIMEOUT_MS    3000
19 #define Q6_READY_TIMEOUT_MS 100
20 #define AVCS_CMD_ADSP_EVENT_GET_STATE		0x0001290C
21 #define AVCS_CMDRSP_ADSP_EVENT_GET_STATE	0x0001290D
22 #define AVCS_GET_VERSIONS       0x00012905
23 #define AVCS_GET_VERSIONS_RSP   0x00012906
24 #define AVCS_CMD_GET_FWK_VERSION	0x001292c
25 #define AVCS_CMDRSP_GET_FWK_VERSION	0x001292d
26 
27 struct avcs_svc_info {
28 	uint32_t service_id;
29 	uint32_t version;
30 } __packed;
31 
32 struct avcs_cmdrsp_get_version {
33 	uint32_t build_id;
34 	uint32_t num_services;
35 	struct avcs_svc_info svc_api_info[];
36 } __packed;
37 
38 /* for ADSP2.8 and above */
39 struct avcs_svc_api_info {
40 	uint32_t service_id;
41 	uint32_t api_version;
42 	uint32_t api_branch_version;
43 } __packed;
44 
45 struct avcs_cmdrsp_get_fwk_version {
46 	uint32_t build_major_version;
47 	uint32_t build_minor_version;
48 	uint32_t build_branch_version;
49 	uint32_t build_subbranch_version;
50 	uint32_t num_services;
51 	struct avcs_svc_api_info svc_api_info[];
52 } __packed;
53 
54 struct q6core {
55 	struct apr_device *adev;
56 	wait_queue_head_t wait;
57 	uint32_t avcs_state;
58 	struct mutex lock;
59 	bool resp_received;
60 	uint32_t num_services;
61 	struct avcs_cmdrsp_get_fwk_version *fwk_version;
62 	struct avcs_cmdrsp_get_version *svc_version;
63 	bool fwk_version_supported;
64 	bool get_state_supported;
65 	bool get_version_supported;
66 	bool is_version_requested;
67 };
68 
69 static struct q6core *g_core;
70 
71 static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
72 {
73 	struct q6core *core = dev_get_drvdata(&adev->dev);
74 	struct aprv2_ibasic_rsp_result_t *result;
75 	struct apr_hdr *hdr = &data->hdr;
76 
77 	result = data->payload;
78 	switch (hdr->opcode) {
79 	case APR_BASIC_RSP_RESULT:{
80 		result = data->payload;
81 		switch (result->opcode) {
82 		case AVCS_GET_VERSIONS:
83 			if (result->status == ADSP_EUNSUPPORTED)
84 				core->get_version_supported = false;
85 			core->resp_received = true;
86 			break;
87 		case AVCS_CMD_GET_FWK_VERSION:
88 			if (result->status == ADSP_EUNSUPPORTED)
89 				core->fwk_version_supported = false;
90 			core->resp_received = true;
91 			break;
92 		case AVCS_CMD_ADSP_EVENT_GET_STATE:
93 			if (result->status == ADSP_EUNSUPPORTED)
94 				core->get_state_supported = false;
95 			core->resp_received = true;
96 			break;
97 		}
98 		break;
99 	}
100 	case AVCS_CMDRSP_GET_FWK_VERSION: {
101 		struct avcs_cmdrsp_get_fwk_version *fwk;
102 		int bytes;
103 
104 		fwk = data->payload;
105 		bytes = sizeof(*fwk) + fwk->num_services *
106 				sizeof(fwk->svc_api_info[0]);
107 
108 		core->fwk_version = kzalloc(bytes, GFP_ATOMIC);
109 		if (!core->fwk_version)
110 			return -ENOMEM;
111 
112 		memcpy(core->fwk_version, data->payload, bytes);
113 
114 		core->fwk_version_supported = true;
115 		core->resp_received = true;
116 
117 		break;
118 	}
119 	case AVCS_GET_VERSIONS_RSP: {
120 		struct avcs_cmdrsp_get_version *v;
121 		int len;
122 
123 		v = data->payload;
124 
125 		len = sizeof(*v) + v->num_services * sizeof(v->svc_api_info[0]);
126 
127 		core->svc_version = kzalloc(len, GFP_ATOMIC);
128 		if (!core->svc_version)
129 			return -ENOMEM;
130 
131 		memcpy(core->svc_version, data->payload, len);
132 
133 		core->get_version_supported = true;
134 		core->resp_received = true;
135 
136 		break;
137 	}
138 	case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
139 		core->get_state_supported = true;
140 		core->avcs_state = result->opcode;
141 
142 		core->resp_received = true;
143 		break;
144 	default:
145 		dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
146 			hdr->opcode);
147 		break;
148 	}
149 
150 	if (core->resp_received)
151 		wake_up(&core->wait);
152 
153 	return 0;
154 }
155 
156 static int q6core_get_fwk_versions(struct q6core *core)
157 {
158 	struct apr_device *adev = core->adev;
159 	struct apr_pkt pkt;
160 	int rc;
161 
162 	pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
163 				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
164 	pkt.hdr.pkt_size = APR_HDR_SIZE;
165 	pkt.hdr.opcode = AVCS_CMD_GET_FWK_VERSION;
166 
167 	rc = apr_send_pkt(adev, &pkt);
168 	if (rc < 0)
169 		return rc;
170 
171 	rc = wait_event_timeout(core->wait, (core->resp_received),
172 				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
173 	if (rc > 0 && core->resp_received) {
174 		core->resp_received = false;
175 
176 		if (!core->fwk_version_supported)
177 			return -ENOTSUPP;
178 		else
179 			return 0;
180 	}
181 
182 
183 	return rc;
184 }
185 
186 static int q6core_get_svc_versions(struct q6core *core)
187 {
188 	struct apr_device *adev = core->adev;
189 	struct apr_pkt pkt;
190 	int rc;
191 
192 	pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
193 				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
194 	pkt.hdr.pkt_size = APR_HDR_SIZE;
195 	pkt.hdr.opcode = AVCS_GET_VERSIONS;
196 
197 	rc = apr_send_pkt(adev, &pkt);
198 	if (rc < 0)
199 		return rc;
200 
201 	rc = wait_event_timeout(core->wait, (core->resp_received),
202 				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
203 	if (rc > 0 && core->resp_received) {
204 		core->resp_received = false;
205 		return 0;
206 	}
207 
208 	return rc;
209 }
210 
211 static bool __q6core_is_adsp_ready(struct q6core *core)
212 {
213 	struct apr_device *adev = core->adev;
214 	struct apr_pkt pkt;
215 	int rc;
216 
217 	core->get_state_supported = false;
218 
219 	pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
220 				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
221 	pkt.hdr.pkt_size = APR_HDR_SIZE;
222 	pkt.hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
223 
224 	rc = apr_send_pkt(adev, &pkt);
225 	if (rc < 0)
226 		return false;
227 
228 	rc = wait_event_timeout(core->wait, (core->resp_received),
229 				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
230 	if (rc > 0 && core->resp_received) {
231 		core->resp_received = false;
232 
233 		if (core->avcs_state)
234 			return true;
235 	}
236 
237 	/* assume that the adsp is up if we not support this command */
238 	if (!core->get_state_supported)
239 		return true;
240 
241 	return false;
242 }
243 
244 /**
245  * q6core_get_svc_api_info() - Get version number of a service.
246  *
247  * @svc_id: service id of the service.
248  * @ainfo: Valid struct pointer to fill svc api information.
249  *
250  * Return: zero on success and error code on failure or unsupported
251  */
252 int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo)
253 {
254 	int i;
255 	int ret = -ENOTSUPP;
256 
257 	if (!g_core || !ainfo)
258 		return 0;
259 
260 	mutex_lock(&g_core->lock);
261 	if (!g_core->is_version_requested) {
262 		if (q6core_get_fwk_versions(g_core) == -ENOTSUPP)
263 			q6core_get_svc_versions(g_core);
264 		g_core->is_version_requested = true;
265 	}
266 
267 	if (g_core->fwk_version_supported) {
268 		for (i = 0; i < g_core->fwk_version->num_services; i++) {
269 			struct avcs_svc_api_info *info;
270 
271 			info = &g_core->fwk_version->svc_api_info[i];
272 			if (svc_id != info->service_id)
273 				continue;
274 
275 			ainfo->api_version = info->api_version;
276 			ainfo->api_branch_version = info->api_branch_version;
277 			ret = 0;
278 			break;
279 		}
280 	} else if (g_core->get_version_supported) {
281 		for (i = 0; i < g_core->svc_version->num_services; i++) {
282 			struct avcs_svc_info *info;
283 
284 			info = &g_core->svc_version->svc_api_info[i];
285 			if (svc_id != info->service_id)
286 				continue;
287 
288 			ainfo->api_version = info->version;
289 			ainfo->api_branch_version = 0;
290 			ret = 0;
291 			break;
292 		}
293 	}
294 
295 	mutex_unlock(&g_core->lock);
296 
297 	return ret;
298 }
299 EXPORT_SYMBOL_GPL(q6core_get_svc_api_info);
300 
301 /**
302  * q6core_is_adsp_ready() - Get status of adsp
303  *
304  * Return: Will be an true if adsp is ready and false if not.
305  */
306 bool q6core_is_adsp_ready(void)
307 {
308 	unsigned long  timeout;
309 	bool ret = false;
310 
311 	if (!g_core)
312 		return false;
313 
314 	mutex_lock(&g_core->lock);
315 	timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
316 	for (;;) {
317 		if (__q6core_is_adsp_ready(g_core)) {
318 			ret = true;
319 			break;
320 		}
321 
322 		if (!time_after(timeout, jiffies)) {
323 			ret = false;
324 			break;
325 		}
326 	}
327 
328 	mutex_unlock(&g_core->lock);
329 	return ret;
330 }
331 EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
332 
333 static int q6core_probe(struct apr_device *adev)
334 {
335 	g_core = kzalloc(sizeof(*g_core), GFP_KERNEL);
336 	if (!g_core)
337 		return -ENOMEM;
338 
339 	dev_set_drvdata(&adev->dev, g_core);
340 
341 	mutex_init(&g_core->lock);
342 	g_core->adev = adev;
343 	init_waitqueue_head(&g_core->wait);
344 	return 0;
345 }
346 
347 static int q6core_exit(struct apr_device *adev)
348 {
349 	struct q6core *core = dev_get_drvdata(&adev->dev);
350 
351 	if (core->fwk_version_supported)
352 		kfree(core->fwk_version);
353 	if (core->get_version_supported)
354 		kfree(core->svc_version);
355 
356 	g_core = NULL;
357 	kfree(core);
358 
359 	return 0;
360 }
361 
362 static const struct of_device_id q6core_device_id[]  = {
363 	{ .compatible = "qcom,q6core" },
364 	{},
365 };
366 MODULE_DEVICE_TABLE(of, q6core_device_id);
367 
368 static struct apr_driver qcom_q6core_driver = {
369 	.probe = q6core_probe,
370 	.remove = q6core_exit,
371 	.callback = q6core_callback,
372 	.driver = {
373 		.name = "qcom-q6core",
374 		.of_match_table = of_match_ptr(q6core_device_id),
375 	},
376 };
377 
378 module_apr_driver(qcom_q6core_driver);
379 MODULE_DESCRIPTION("q6 core");
380 MODULE_LICENSE("GPL v2");
381