1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
3 // Copyright (c) 2018, Linaro Limited
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>
11 #include <linux/of_platform.h>
12 #include <linux/jiffies.h>
13 #include <linux/soc/qcom/apr.h>
15 #include "q6dsp-errno.h"
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
26 struct avcs_svc_info
{
31 struct avcs_cmdrsp_get_version
{
33 uint32_t num_services
;
34 struct avcs_svc_info svc_api_info
[];
37 /* for ADSP2.8 and above */
38 struct avcs_svc_api_info
{
41 uint32_t api_branch_version
;
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
[];
54 struct apr_device
*adev
;
55 wait_queue_head_t wait
;
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
;
68 static struct q6core
*g_core
;
70 static int q6core_callback(struct apr_device
*adev
, struct apr_resp_pkt
*data
)
72 struct q6core
*core
= dev_get_drvdata(&adev
->dev
);
73 struct aprv2_ibasic_rsp_result_t
*result
;
74 struct apr_hdr
*hdr
= &data
->hdr
;
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;
86 case AVCS_CMD_GET_FWK_VERSION
:
87 if (result
->status
== ADSP_EUNSUPPORTED
)
88 core
->fwk_version_supported
= false;
89 core
->resp_received
= true;
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;
99 case AVCS_CMDRSP_GET_FWK_VERSION
: {
100 struct avcs_cmdrsp_get_fwk_version
*fwk
;
104 core
->fwk_version
= kmemdup(data
->payload
,
105 struct_size(fwk
, svc_api_info
,
108 if (!core
->fwk_version
)
111 core
->fwk_version_supported
= true;
112 core
->resp_received
= true;
116 case AVCS_GET_VERSIONS_RSP
: {
117 struct avcs_cmdrsp_get_version
*v
;
121 core
->svc_version
= kmemdup(data
->payload
,
122 struct_size(v
, svc_api_info
,
125 if (!core
->svc_version
)
128 core
->get_version_supported
= true;
129 core
->resp_received
= true;
133 case AVCS_CMDRSP_ADSP_EVENT_GET_STATE
:
134 core
->get_state_supported
= true;
135 core
->avcs_state
= result
->opcode
;
137 core
->resp_received
= true;
140 dev_err(&adev
->dev
, "Message id from adsp core svc: 0x%x\n",
145 if (core
->resp_received
)
146 wake_up(&core
->wait
);
151 static int q6core_get_fwk_versions(struct q6core
*core
)
153 struct apr_device
*adev
= core
->adev
;
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
;
162 rc
= apr_send_pkt(adev
, &pkt
);
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;
171 if (!core
->fwk_version_supported
)
181 static int q6core_get_svc_versions(struct q6core
*core
)
183 struct apr_device
*adev
= core
->adev
;
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
;
192 rc
= apr_send_pkt(adev
, &pkt
);
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;
206 static bool __q6core_is_adsp_ready(struct q6core
*core
)
208 struct apr_device
*adev
= core
->adev
;
212 core
->get_state_supported
= false;
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
;
219 rc
= apr_send_pkt(adev
, &pkt
);
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;
228 if (core
->avcs_state
)
232 /* assume that the adsp is up if we not support this command */
233 if (!core
->get_state_supported
)
240 * q6core_get_svc_api_info() - Get version number of a service.
242 * @svc_id: service id of the service.
243 * @ainfo: Valid struct pointer to fill svc api information.
245 * Return: zero on success and error code on failure or unsupported
247 int q6core_get_svc_api_info(int svc_id
, struct q6core_svc_api_info
*ainfo
)
252 if (!g_core
|| !ainfo
)
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;
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
;
266 info
= &g_core
->fwk_version
->svc_api_info
[i
];
267 if (svc_id
!= info
->service_id
)
270 ainfo
->api_version
= info
->api_version
;
271 ainfo
->api_branch_version
= info
->api_branch_version
;
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
;
279 info
= &g_core
->svc_version
->svc_api_info
[i
];
280 if (svc_id
!= info
->service_id
)
283 ainfo
->api_version
= info
->version
;
284 ainfo
->api_branch_version
= 0;
290 mutex_unlock(&g_core
->lock
);
294 EXPORT_SYMBOL_GPL(q6core_get_svc_api_info
);
297 * q6core_is_adsp_ready() - Get status of adsp
299 * Return: Will be an true if adsp is ready and false if not.
301 bool q6core_is_adsp_ready(void)
303 unsigned long timeout
;
309 mutex_lock(&g_core
->lock
);
310 timeout
= jiffies
+ msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS
);
312 if (__q6core_is_adsp_ready(g_core
)) {
317 if (!time_after(timeout
, jiffies
)) {
323 mutex_unlock(&g_core
->lock
);
326 EXPORT_SYMBOL_GPL(q6core_is_adsp_ready
);
328 static int q6core_probe(struct apr_device
*adev
)
330 g_core
= kzalloc(sizeof(*g_core
), GFP_KERNEL
);
334 dev_set_drvdata(&adev
->dev
, g_core
);
336 mutex_init(&g_core
->lock
);
338 init_waitqueue_head(&g_core
->wait
);
342 static void q6core_exit(struct apr_device
*adev
)
344 struct q6core
*core
= dev_get_drvdata(&adev
->dev
);
346 if (core
->fwk_version_supported
)
347 kfree(core
->fwk_version
);
348 if (core
->get_version_supported
)
349 kfree(core
->svc_version
);
356 static const struct of_device_id q6core_device_id
[] = {
357 { .compatible
= "qcom,q6core" },
360 MODULE_DEVICE_TABLE(of
, q6core_device_id
);
363 static struct apr_driver qcom_q6core_driver
= {
364 .probe
= q6core_probe
,
365 .remove
= q6core_exit
,
366 .callback
= q6core_callback
,
368 .name
= "qcom-q6core",
369 .of_match_table
= of_match_ptr(q6core_device_id
),
373 module_apr_driver(qcom_q6core_driver
);
374 MODULE_DESCRIPTION("q6 core");
375 MODULE_LICENSE("GPL v2");