File Index
Files and directories
Sign in
FILE

q6core.c

File preview with syntax highlighting.

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