GNU Linux-libre 4.19.263-gnu1
[releases.git] / sound / soc / qcom / qdsp6 / q6core.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/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");