~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/qcom/qdsp6/q6core.c

Version: ~ [ linux-5.6-rc1 ] ~ [ linux-5.5.2 ] ~ [ linux-5.4.17 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.102 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.170 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.213 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.213 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.81 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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                 int bytes;
102 
103                 fwk = data->payload;
104                 bytes = sizeof(*fwk) + fwk->num_services *
105                                 sizeof(fwk->svc_api_info[0]);
106 
107                 core->fwk_version = kmemdup(data->payload, bytes, 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                 int len;
119 
120                 v = data->payload;
121 
122                 len = sizeof(*v) + v->num_services * sizeof(v->svc_api_info[0]);
123 
124                 core->svc_version = kmemdup(data->payload, len, 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 int 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         return 0;
355 }
356 
357 static const struct of_device_id q6core_device_id[]  = {
358         { .compatible = "qcom,q6core" },
359         {},
360 };
361 MODULE_DEVICE_TABLE(of, q6core_device_id);
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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp