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/device.h>
6 #include <linux/jiffies.h>
7 #include <linux/kernel.h>
8 #include <linux/kref.h>
9 #include <linux/module.h>
11 #include <linux/of_platform.h>
12 #include <linux/platform_device.h>
13 #include <linux/sched.h>
14 #include <linux/slab.h>
15 #include <linux/soc/qcom/apr.h>
16 #include <linux/wait.h>
17 #include <sound/asound.h>
21 #include "q6dsp-common.h"
22 #include "q6dsp-errno.h"
24 #define ADM_CMD_DEVICE_OPEN_V5 0x00010326
25 #define ADM_CMDRSP_DEVICE_OPEN_V5 0x00010329
26 #define ADM_CMD_DEVICE_CLOSE_V5 0x00010327
27 #define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325
29 #define TIMEOUT_MS 1000
30 #define RESET_COPP_ID 99
31 #define INVALID_COPP_ID 0xFF
32 /* Definition for a legacy device session. */
33 #define ADM_LEGACY_DEVICE_SESSION 0
34 #define ADM_MATRIX_ID_AUDIO_RX 0
35 #define ADM_MATRIX_ID_AUDIO_TX 1
49 struct aprv2_ibasic_rsp_result_t result
;
51 wait_queue_head_t wait
;
52 struct list_head node
;
57 struct apr_device
*apr
;
59 struct q6core_svc_api_info ainfo
;
60 unsigned long copp_bitmap
[AFE_MAX_PORTS
];
61 struct list_head copps_list
;
62 spinlock_t copps_list_lock
;
63 struct aprv2_ibasic_rsp_result_t result
;
65 wait_queue_head_t matrix_map_wait
;
68 struct q6adm_cmd_device_open_v5
{
70 u16 mode_of_operation
;
77 u8 dev_channel_mapping
[8];
80 struct q6adm_cmd_matrix_map_routings_v5
{
85 struct q6adm_session_map_node_v5
{
90 static struct q6copp
*q6adm_find_copp(struct q6adm
*adm
, int port_idx
,
93 struct q6copp
*c
= NULL
;
94 struct q6copp
*ret
= NULL
;
97 spin_lock_irqsave(&adm
->copps_list_lock
, flags
);
98 list_for_each_entry(c
, &adm
->copps_list
, node
) {
99 if ((port_idx
== c
->afe_port
) && (copp_idx
== c
->copp_idx
)) {
101 kref_get(&c
->refcount
);
106 spin_unlock_irqrestore(&adm
->copps_list_lock
, flags
);
112 static void q6adm_free_copp(struct kref
*ref
)
114 struct q6copp
*c
= container_of(ref
, struct q6copp
, refcount
);
115 struct q6adm
*adm
= c
->adm
;
118 spin_lock_irqsave(&adm
->copps_list_lock
, flags
);
119 clear_bit(c
->copp_idx
, &adm
->copp_bitmap
[c
->afe_port
]);
121 spin_unlock_irqrestore(&adm
->copps_list_lock
, flags
);
125 static int q6adm_callback(struct apr_device
*adev
, struct apr_resp_pkt
*data
)
127 struct aprv2_ibasic_rsp_result_t
*result
= data
->payload
;
128 int port_idx
, copp_idx
;
129 struct apr_hdr
*hdr
= &data
->hdr
;
131 struct q6adm
*adm
= dev_get_drvdata(&adev
->dev
);
133 if (!data
->payload_size
)
136 copp_idx
= (hdr
->token
) & 0XFF;
137 port_idx
= ((hdr
->token
) >> 16) & 0xFF;
138 if (port_idx
< 0 || port_idx
>= AFE_MAX_PORTS
) {
139 dev_err(&adev
->dev
, "Invalid port idx %d token %d\n",
140 port_idx
, hdr
->token
);
143 if (copp_idx
< 0 || copp_idx
>= MAX_COPPS_PER_PORT
) {
144 dev_err(&adev
->dev
, "Invalid copp idx %d token %d\n",
145 copp_idx
, hdr
->token
);
149 switch (hdr
->opcode
) {
150 case APR_BASIC_RSP_RESULT
: {
151 if (result
->status
!= 0) {
152 dev_err(&adev
->dev
, "cmd = 0x%x return error = 0x%x\n",
153 result
->opcode
, result
->status
);
155 switch (result
->opcode
) {
156 case ADM_CMD_DEVICE_OPEN_V5
:
157 case ADM_CMD_DEVICE_CLOSE_V5
:
158 copp
= q6adm_find_copp(adm
, port_idx
, copp_idx
);
162 copp
->result
= *result
;
163 wake_up(&copp
->wait
);
164 kref_put(&copp
->refcount
, q6adm_free_copp
);
166 case ADM_CMD_MATRIX_MAP_ROUTINGS_V5
:
167 adm
->result
= *result
;
168 wake_up(&adm
->matrix_map_wait
);
172 dev_err(&adev
->dev
, "Unknown Cmd: 0x%x\n",
178 case ADM_CMDRSP_DEVICE_OPEN_V5
: {
179 struct adm_cmd_rsp_device_open_v5
{
183 } __packed
* open
= data
->payload
;
185 copp
= q6adm_find_copp(adm
, port_idx
, copp_idx
);
189 if (open
->copp_id
== INVALID_COPP_ID
) {
190 dev_err(&adev
->dev
, "Invalid coppid rxed %d\n",
192 copp
->result
.status
= ADSP_EBADPARAM
;
193 wake_up(&copp
->wait
);
194 kref_put(&copp
->refcount
, q6adm_free_copp
);
197 copp
->result
.opcode
= hdr
->opcode
;
198 copp
->id
= open
->copp_id
;
199 wake_up(&copp
->wait
);
200 kref_put(&copp
->refcount
, q6adm_free_copp
);
204 dev_err(&adev
->dev
, "Unknown cmd:0x%x\n",
212 static struct q6copp
*q6adm_alloc_copp(struct q6adm
*adm
, int port_idx
)
217 idx
= find_first_zero_bit(&adm
->copp_bitmap
[port_idx
],
220 if (idx
> MAX_COPPS_PER_PORT
)
221 return ERR_PTR(-EBUSY
);
223 c
= kzalloc(sizeof(*c
), GFP_ATOMIC
);
225 return ERR_PTR(-ENOMEM
);
227 set_bit(idx
, &adm
->copp_bitmap
[port_idx
]);
229 c
->afe_port
= port_idx
;
232 init_waitqueue_head(&c
->wait
);
237 static int q6adm_apr_send_copp_pkt(struct q6adm
*adm
, struct q6copp
*copp
,
238 struct apr_pkt
*pkt
, uint32_t rsp_opcode
)
240 struct device
*dev
= adm
->dev
;
241 uint32_t opcode
= pkt
->hdr
.opcode
;
244 mutex_lock(&adm
->lock
);
245 copp
->result
.opcode
= 0;
246 copp
->result
.status
= 0;
247 ret
= apr_send_pkt(adm
->apr
, pkt
);
249 dev_err(dev
, "Failed to send APR packet\n");
254 /* Wait for the callback with copp id */
256 ret
= wait_event_timeout(copp
->wait
,
257 (copp
->result
.opcode
== opcode
) ||
258 (copp
->result
.opcode
== rsp_opcode
),
259 msecs_to_jiffies(TIMEOUT_MS
));
261 ret
= wait_event_timeout(copp
->wait
,
262 (copp
->result
.opcode
== opcode
),
263 msecs_to_jiffies(TIMEOUT_MS
));
266 dev_err(dev
, "ADM copp cmd timedout\n");
268 } else if (copp
->result
.status
> 0) {
269 dev_err(dev
, "DSP returned error[%d]\n",
270 copp
->result
.status
);
275 mutex_unlock(&adm
->lock
);
279 static int q6adm_device_close(struct q6adm
*adm
, struct q6copp
*copp
,
280 int port_id
, int copp_idx
)
282 struct apr_pkt close
;
284 close
.hdr
.hdr_field
= APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD
,
285 APR_HDR_LEN(APR_HDR_SIZE
),
287 close
.hdr
.pkt_size
= sizeof(close
);
288 close
.hdr
.src_port
= port_id
;
289 close
.hdr
.dest_port
= copp
->id
;
290 close
.hdr
.token
= port_id
<< 16 | copp_idx
;
291 close
.hdr
.opcode
= ADM_CMD_DEVICE_CLOSE_V5
;
293 return q6adm_apr_send_copp_pkt(adm
, copp
, &close
, 0);
296 static struct q6copp
*q6adm_find_matching_copp(struct q6adm
*adm
,
297 int port_id
, int topology
,
299 int channel_mode
, int bit_width
,
302 struct q6copp
*c
= NULL
;
303 struct q6copp
*ret
= NULL
;
306 spin_lock_irqsave(&adm
->copps_list_lock
, flags
);
308 list_for_each_entry(c
, &adm
->copps_list
, node
) {
309 if ((port_id
== c
->afe_port
) && (topology
== c
->topology
) &&
310 (mode
== c
->mode
) && (rate
== c
->rate
) &&
311 (bit_width
== c
->bit_width
) && (app_type
== c
->app_type
)) {
313 kref_get(&c
->refcount
);
316 spin_unlock_irqrestore(&adm
->copps_list_lock
, flags
);
321 static int q6adm_device_open(struct q6adm
*adm
, struct q6copp
*copp
,
322 int port_id
, int path
, int topology
,
323 int channel_mode
, int bit_width
, int rate
)
325 struct q6adm_cmd_device_open_v5
*open
;
326 int afe_port
= q6afe_get_port_id(port_id
);
331 pkt_size
= APR_HDR_SIZE
+ sizeof(*open
);
332 p
= kzalloc(pkt_size
, GFP_KERNEL
);
337 open
= p
+ APR_HDR_SIZE
;
338 pkt
->hdr
.hdr_field
= APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD
,
339 APR_HDR_LEN(APR_HDR_SIZE
),
341 pkt
->hdr
.pkt_size
= pkt_size
;
342 pkt
->hdr
.src_port
= afe_port
;
343 pkt
->hdr
.dest_port
= afe_port
;
344 pkt
->hdr
.token
= port_id
<< 16 | copp
->copp_idx
;
345 pkt
->hdr
.opcode
= ADM_CMD_DEVICE_OPEN_V5
;
346 open
->flags
= ADM_LEGACY_DEVICE_SESSION
;
347 open
->mode_of_operation
= path
;
348 open
->endpoint_id_1
= afe_port
;
349 open
->topology_id
= topology
;
350 open
->dev_num_channel
= channel_mode
& 0x00FF;
351 open
->bit_width
= bit_width
;
352 open
->sample_rate
= rate
;
354 ret
= q6dsp_map_channels(&open
->dev_channel_mapping
[0],
359 ret
= q6adm_apr_send_copp_pkt(adm
, copp
, pkt
,
360 ADM_CMDRSP_DEVICE_OPEN_V5
);
368 * q6adm_open() - open adm and grab a free copp
370 * @dev: Pointer to adm child device.
372 * @path: playback or capture path.
373 * @rate: rate at which copp is required.
374 * @channel_mode: channel mode
375 * @topology: adm topology id
376 * @perf_mode: performace mode.
377 * @bit_width: audio sample bit width
378 * @app_type: Application type.
381 * Return: Will be an negative on error or a valid copp pointer on success.
383 struct q6copp
*q6adm_open(struct device
*dev
, int port_id
, int path
, int rate
,
384 int channel_mode
, int topology
, int perf_mode
,
385 uint16_t bit_width
, int app_type
, int acdb_id
)
387 struct q6adm
*adm
= dev_get_drvdata(dev
->parent
);
393 dev_err(dev
, "Invalid port_id 0x%x\n", port_id
);
394 return ERR_PTR(-EINVAL
);
397 copp
= q6adm_find_matching_copp(adm
, port_id
, topology
, perf_mode
,
398 rate
, channel_mode
, bit_width
, app_type
);
400 dev_err(dev
, "Found Matching Copp 0x%x\n", copp
->copp_idx
);
404 spin_lock_irqsave(&adm
->copps_list_lock
, flags
);
405 copp
= q6adm_alloc_copp(adm
, port_id
);
407 spin_unlock_irqrestore(&adm
->copps_list_lock
, flags
);
408 return ERR_CAST(copp
);
411 list_add_tail(&copp
->node
, &adm
->copps_list
);
412 spin_unlock_irqrestore(&adm
->copps_list_lock
, flags
);
414 kref_init(&copp
->refcount
);
415 copp
->topology
= topology
;
416 copp
->mode
= perf_mode
;
418 copp
->channels
= channel_mode
;
419 copp
->bit_width
= bit_width
;
420 copp
->app_type
= app_type
;
422 ret
= q6adm_device_open(adm
, copp
, port_id
, path
, topology
,
423 channel_mode
, bit_width
, rate
);
425 kref_put(&copp
->refcount
, q6adm_free_copp
);
431 EXPORT_SYMBOL_GPL(q6adm_open
);
434 * q6adm_get_copp_id() - get copp index
436 * @copp: Pointer to valid copp
438 * Return: Will be an negative on error or a valid copp index on success.
440 int q6adm_get_copp_id(struct q6copp
*copp
)
445 return copp
->copp_idx
;
447 EXPORT_SYMBOL_GPL(q6adm_get_copp_id
);
450 * q6adm_matrix_map() - Map asm streams and afe ports using payload
452 * @dev: Pointer to adm child device.
453 * @path: playback or capture path.
454 * @payload_map: map between session id and afe ports.
455 * @perf_mode: Performace mode.
457 * Return: Will be an negative on error or a zero on success.
459 int q6adm_matrix_map(struct device
*dev
, int path
,
460 struct route_payload payload_map
, int perf_mode
)
462 struct q6adm
*adm
= dev_get_drvdata(dev
->parent
);
463 struct q6adm_cmd_matrix_map_routings_v5
*route
;
464 struct q6adm_session_map_node_v5
*node
;
466 uint16_t *copps_list
;
467 int pkt_size
, ret
, i
, copp_idx
;
468 void *matrix_map
= NULL
;
471 /* Assumes port_ids have already been validated during adm_open */
472 pkt_size
= (APR_HDR_SIZE
+ sizeof(*route
) + sizeof(*node
) +
473 (sizeof(uint32_t) * payload_map
.num_copps
));
475 matrix_map
= kzalloc(pkt_size
, GFP_KERNEL
);
480 route
= matrix_map
+ APR_HDR_SIZE
;
481 node
= matrix_map
+ APR_HDR_SIZE
+ sizeof(*route
);
482 copps_list
= matrix_map
+ APR_HDR_SIZE
+ sizeof(*route
) + sizeof(*node
);
484 pkt
->hdr
.hdr_field
= APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD
,
485 APR_HDR_LEN(APR_HDR_SIZE
),
487 pkt
->hdr
.pkt_size
= pkt_size
;
489 pkt
->hdr
.opcode
= ADM_CMD_MATRIX_MAP_ROUTINGS_V5
;
490 route
->num_sessions
= 1;
493 case ADM_PATH_PLAYBACK
:
494 route
->matrix_id
= ADM_MATRIX_ID_AUDIO_RX
;
496 case ADM_PATH_LIVE_REC
:
497 route
->matrix_id
= ADM_MATRIX_ID_AUDIO_TX
;
500 dev_err(dev
, "Wrong path set[%d]\n", path
);
504 node
->session_id
= payload_map
.session_id
;
505 node
->num_copps
= payload_map
.num_copps
;
507 for (i
= 0; i
< payload_map
.num_copps
; i
++) {
508 int port_idx
= payload_map
.port_id
[i
];
511 dev_err(dev
, "Invalid port_id 0x%x\n",
512 payload_map
.port_id
[i
]);
516 copp_idx
= payload_map
.copp_idx
[i
];
518 copp
= q6adm_find_copp(adm
, port_idx
, copp_idx
);
524 copps_list
[i
] = copp
->id
;
525 kref_put(&copp
->refcount
, q6adm_free_copp
);
528 mutex_lock(&adm
->lock
);
529 adm
->result
.status
= 0;
530 adm
->result
.opcode
= 0;
532 ret
= apr_send_pkt(adm
->apr
, pkt
);
534 dev_err(dev
, "routing for stream %d failed ret %d\n",
535 payload_map
.session_id
, ret
);
538 ret
= wait_event_timeout(adm
->matrix_map_wait
,
539 adm
->result
.opcode
== pkt
->hdr
.opcode
,
540 msecs_to_jiffies(TIMEOUT_MS
));
542 dev_err(dev
, "routing for stream %d failed\n",
543 payload_map
.session_id
);
546 } else if (adm
->result
.status
> 0) {
547 dev_err(dev
, "DSP returned error[%d]\n",
554 mutex_unlock(&adm
->lock
);
558 EXPORT_SYMBOL_GPL(q6adm_matrix_map
);
561 * q6adm_close() - Close adm copp
563 * @dev: Pointer to adm child device.
564 * @copp: pointer to previously opened copp
566 * Return: Will be an negative on error or a zero on success.
568 int q6adm_close(struct device
*dev
, struct q6copp
*copp
)
570 struct q6adm
*adm
= dev_get_drvdata(dev
->parent
);
573 ret
= q6adm_device_close(adm
, copp
, copp
->afe_port
, copp
->copp_idx
);
575 dev_err(adm
->dev
, "Failed to close copp %d\n", ret
);
579 kref_put(&copp
->refcount
, q6adm_free_copp
);
583 EXPORT_SYMBOL_GPL(q6adm_close
);
585 static int q6adm_probe(struct apr_device
*adev
)
587 struct device
*dev
= &adev
->dev
;
590 adm
= devm_kzalloc(dev
, sizeof(*adm
), GFP_KERNEL
);
595 dev_set_drvdata(dev
, adm
);
597 q6core_get_svc_api_info(adev
->svc_id
, &adm
->ainfo
);
598 mutex_init(&adm
->lock
);
599 init_waitqueue_head(&adm
->matrix_map_wait
);
601 INIT_LIST_HEAD(&adm
->copps_list
);
602 spin_lock_init(&adm
->copps_list_lock
);
604 return devm_of_platform_populate(dev
);
608 static const struct of_device_id q6adm_device_id
[] = {
609 { .compatible
= "qcom,q6adm" },
612 MODULE_DEVICE_TABLE(of
, q6adm_device_id
);
615 static struct apr_driver qcom_q6adm_driver
= {
616 .probe
= q6adm_probe
,
617 .callback
= q6adm_callback
,
619 .name
= "qcom-q6adm",
620 .of_match_table
= of_match_ptr(q6adm_device_id
),
624 module_apr_driver(qcom_q6adm_driver
);
625 MODULE_DESCRIPTION("Q6 Audio Device Manager");
626 MODULE_LICENSE("GPL v2");