Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/qcom/qdsp6/q6core.c
26444 views
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
26
struct avcs_svc_info {
27
uint32_t service_id;
28
uint32_t version;
29
} __packed;
30
31
struct 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 */
38
struct avcs_svc_api_info {
39
uint32_t service_id;
40
uint32_t api_version;
41
uint32_t api_branch_version;
42
} __packed;
43
44
struct 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
53
struct 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
68
static struct q6core *g_core;
69
70
static 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
151
static 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
181
static 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
206
static 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
*/
247
int 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
}
294
EXPORT_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
*/
301
bool 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
}
326
EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
327
328
static 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
342
static 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
356
static const struct of_device_id q6core_device_id[] = {
357
{ .compatible = "qcom,q6core" },
358
{},
359
};
360
MODULE_DEVICE_TABLE(of, q6core_device_id);
361
#endif
362
363
static 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
373
module_apr_driver(qcom_q6core_driver);
374
MODULE_DESCRIPTION("q6 core");
375
MODULE_LICENSE("GPL v2");
376
377