18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
38c2ecf20Sopenharmony_ci// Copyright (c) 2018, Linaro Limited
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/device.h>
68c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/kref.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/sched.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/soc/qcom/apr.h>
168c2ecf20Sopenharmony_ci#include <linux/wait.h>
178c2ecf20Sopenharmony_ci#include <sound/asound.h>
188c2ecf20Sopenharmony_ci#include "q6adm.h"
198c2ecf20Sopenharmony_ci#include "q6afe.h"
208c2ecf20Sopenharmony_ci#include "q6core.h"
218c2ecf20Sopenharmony_ci#include "q6dsp-common.h"
228c2ecf20Sopenharmony_ci#include "q6dsp-errno.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define ADM_CMD_DEVICE_OPEN_V5		0x00010326
258c2ecf20Sopenharmony_ci#define ADM_CMDRSP_DEVICE_OPEN_V5	0x00010329
268c2ecf20Sopenharmony_ci#define ADM_CMD_DEVICE_CLOSE_V5		0x00010327
278c2ecf20Sopenharmony_ci#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5	0x00010325
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define TIMEOUT_MS 1000
308c2ecf20Sopenharmony_ci#define RESET_COPP_ID 99
318c2ecf20Sopenharmony_ci#define INVALID_COPP_ID 0xFF
328c2ecf20Sopenharmony_ci/* Definition for a legacy device session. */
338c2ecf20Sopenharmony_ci#define ADM_LEGACY_DEVICE_SESSION	0
348c2ecf20Sopenharmony_ci#define ADM_MATRIX_ID_AUDIO_RX		0
358c2ecf20Sopenharmony_ci#define ADM_MATRIX_ID_AUDIO_TX		1
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct q6copp {
388c2ecf20Sopenharmony_ci	int afe_port;
398c2ecf20Sopenharmony_ci	int copp_idx;
408c2ecf20Sopenharmony_ci	int id;
418c2ecf20Sopenharmony_ci	int topology;
428c2ecf20Sopenharmony_ci	int mode;
438c2ecf20Sopenharmony_ci	int rate;
448c2ecf20Sopenharmony_ci	int bit_width;
458c2ecf20Sopenharmony_ci	int channels;
468c2ecf20Sopenharmony_ci	int app_type;
478c2ecf20Sopenharmony_ci	int acdb_id;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	struct aprv2_ibasic_rsp_result_t result;
508c2ecf20Sopenharmony_ci	struct kref refcount;
518c2ecf20Sopenharmony_ci	wait_queue_head_t wait;
528c2ecf20Sopenharmony_ci	struct list_head node;
538c2ecf20Sopenharmony_ci	struct q6adm *adm;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct q6adm {
578c2ecf20Sopenharmony_ci	struct apr_device *apr;
588c2ecf20Sopenharmony_ci	struct device *dev;
598c2ecf20Sopenharmony_ci	struct q6core_svc_api_info ainfo;
608c2ecf20Sopenharmony_ci	unsigned long copp_bitmap[AFE_MAX_PORTS];
618c2ecf20Sopenharmony_ci	struct list_head copps_list;
628c2ecf20Sopenharmony_ci	spinlock_t copps_list_lock;
638c2ecf20Sopenharmony_ci	struct aprv2_ibasic_rsp_result_t result;
648c2ecf20Sopenharmony_ci	struct mutex lock;
658c2ecf20Sopenharmony_ci	wait_queue_head_t matrix_map_wait;
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistruct q6adm_cmd_device_open_v5 {
698c2ecf20Sopenharmony_ci	u16 flags;
708c2ecf20Sopenharmony_ci	u16 mode_of_operation;
718c2ecf20Sopenharmony_ci	u16 endpoint_id_1;
728c2ecf20Sopenharmony_ci	u16 endpoint_id_2;
738c2ecf20Sopenharmony_ci	u32 topology_id;
748c2ecf20Sopenharmony_ci	u16 dev_num_channel;
758c2ecf20Sopenharmony_ci	u16 bit_width;
768c2ecf20Sopenharmony_ci	u32 sample_rate;
778c2ecf20Sopenharmony_ci	u8 dev_channel_mapping[8];
788c2ecf20Sopenharmony_ci} __packed;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistruct q6adm_cmd_matrix_map_routings_v5 {
818c2ecf20Sopenharmony_ci	u32 matrix_id;
828c2ecf20Sopenharmony_ci	u32 num_sessions;
838c2ecf20Sopenharmony_ci} __packed;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistruct q6adm_session_map_node_v5 {
868c2ecf20Sopenharmony_ci	u16 session_id;
878c2ecf20Sopenharmony_ci	u16 num_copps;
888c2ecf20Sopenharmony_ci} __packed;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic struct q6copp *q6adm_find_copp(struct q6adm *adm, int port_idx,
918c2ecf20Sopenharmony_ci				  int copp_idx)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct q6copp *c = NULL;
948c2ecf20Sopenharmony_ci	struct q6copp *ret = NULL;
958c2ecf20Sopenharmony_ci	unsigned long flags;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adm->copps_list_lock, flags);
988c2ecf20Sopenharmony_ci	list_for_each_entry(c, &adm->copps_list, node) {
998c2ecf20Sopenharmony_ci		if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) {
1008c2ecf20Sopenharmony_ci			ret = c;
1018c2ecf20Sopenharmony_ci			kref_get(&c->refcount);
1028c2ecf20Sopenharmony_ci			break;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return ret;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void q6adm_free_copp(struct kref *ref)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct q6copp *c = container_of(ref, struct q6copp, refcount);
1158c2ecf20Sopenharmony_ci	struct q6adm *adm = c->adm;
1168c2ecf20Sopenharmony_ci	unsigned long flags;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adm->copps_list_lock, flags);
1198c2ecf20Sopenharmony_ci	clear_bit(c->copp_idx, &adm->copp_bitmap[c->afe_port]);
1208c2ecf20Sopenharmony_ci	list_del(&c->node);
1218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
1228c2ecf20Sopenharmony_ci	kfree(c);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct aprv2_ibasic_rsp_result_t *result = data->payload;
1288c2ecf20Sopenharmony_ci	int port_idx, copp_idx;
1298c2ecf20Sopenharmony_ci	struct apr_hdr *hdr = &data->hdr;
1308c2ecf20Sopenharmony_ci	struct q6copp *copp;
1318c2ecf20Sopenharmony_ci	struct q6adm *adm = dev_get_drvdata(&adev->dev);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!data->payload_size)
1348c2ecf20Sopenharmony_ci		return 0;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	copp_idx = (hdr->token) & 0XFF;
1378c2ecf20Sopenharmony_ci	port_idx = ((hdr->token) >> 16) & 0xFF;
1388c2ecf20Sopenharmony_ci	if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
1398c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Invalid port idx %d token %d\n",
1408c2ecf20Sopenharmony_ci		       port_idx, hdr->token);
1418c2ecf20Sopenharmony_ci		return 0;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
1448c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Invalid copp idx %d token %d\n",
1458c2ecf20Sopenharmony_ci			copp_idx, hdr->token);
1468c2ecf20Sopenharmony_ci		return 0;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	switch (hdr->opcode) {
1508c2ecf20Sopenharmony_ci	case APR_BASIC_RSP_RESULT: {
1518c2ecf20Sopenharmony_ci		if (result->status != 0) {
1528c2ecf20Sopenharmony_ci			dev_err(&adev->dev, "cmd = 0x%x return error = 0x%x\n",
1538c2ecf20Sopenharmony_ci				result->opcode, result->status);
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci		switch (result->opcode) {
1568c2ecf20Sopenharmony_ci		case ADM_CMD_DEVICE_OPEN_V5:
1578c2ecf20Sopenharmony_ci		case ADM_CMD_DEVICE_CLOSE_V5:
1588c2ecf20Sopenharmony_ci			copp = q6adm_find_copp(adm, port_idx, copp_idx);
1598c2ecf20Sopenharmony_ci			if (!copp)
1608c2ecf20Sopenharmony_ci				return 0;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci			copp->result = *result;
1638c2ecf20Sopenharmony_ci			wake_up(&copp->wait);
1648c2ecf20Sopenharmony_ci			kref_put(&copp->refcount, q6adm_free_copp);
1658c2ecf20Sopenharmony_ci			break;
1668c2ecf20Sopenharmony_ci		case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
1678c2ecf20Sopenharmony_ci			adm->result = *result;
1688c2ecf20Sopenharmony_ci			wake_up(&adm->matrix_map_wait);
1698c2ecf20Sopenharmony_ci			break;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		default:
1728c2ecf20Sopenharmony_ci			dev_err(&adev->dev, "Unknown Cmd: 0x%x\n",
1738c2ecf20Sopenharmony_ci				result->opcode);
1748c2ecf20Sopenharmony_ci			break;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci		return 0;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	case ADM_CMDRSP_DEVICE_OPEN_V5: {
1798c2ecf20Sopenharmony_ci		struct adm_cmd_rsp_device_open_v5 {
1808c2ecf20Sopenharmony_ci			u32 status;
1818c2ecf20Sopenharmony_ci			u16 copp_id;
1828c2ecf20Sopenharmony_ci			u16 reserved;
1838c2ecf20Sopenharmony_ci		} __packed * open = data->payload;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		copp = q6adm_find_copp(adm, port_idx, copp_idx);
1868c2ecf20Sopenharmony_ci		if (!copp)
1878c2ecf20Sopenharmony_ci			return 0;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		if (open->copp_id == INVALID_COPP_ID) {
1908c2ecf20Sopenharmony_ci			dev_err(&adev->dev, "Invalid coppid rxed %d\n",
1918c2ecf20Sopenharmony_ci				open->copp_id);
1928c2ecf20Sopenharmony_ci			copp->result.status = ADSP_EBADPARAM;
1938c2ecf20Sopenharmony_ci			wake_up(&copp->wait);
1948c2ecf20Sopenharmony_ci			kref_put(&copp->refcount, q6adm_free_copp);
1958c2ecf20Sopenharmony_ci			break;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci		copp->result.opcode = hdr->opcode;
1988c2ecf20Sopenharmony_ci		copp->id = open->copp_id;
1998c2ecf20Sopenharmony_ci		wake_up(&copp->wait);
2008c2ecf20Sopenharmony_ci		kref_put(&copp->refcount, q6adm_free_copp);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	break;
2038c2ecf20Sopenharmony_ci	default:
2048c2ecf20Sopenharmony_ci		dev_err(&adev->dev, "Unknown cmd:0x%x\n",
2058c2ecf20Sopenharmony_ci		       hdr->opcode);
2068c2ecf20Sopenharmony_ci		break;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return 0;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic struct q6copp *q6adm_alloc_copp(struct q6adm *adm, int port_idx)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	struct q6copp *c;
2158c2ecf20Sopenharmony_ci	int idx;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	idx = find_first_zero_bit(&adm->copp_bitmap[port_idx],
2188c2ecf20Sopenharmony_ci				  MAX_COPPS_PER_PORT);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (idx >= MAX_COPPS_PER_PORT)
2218c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	c = kzalloc(sizeof(*c), GFP_ATOMIC);
2248c2ecf20Sopenharmony_ci	if (!c)
2258c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	set_bit(idx, &adm->copp_bitmap[port_idx]);
2288c2ecf20Sopenharmony_ci	c->copp_idx = idx;
2298c2ecf20Sopenharmony_ci	c->afe_port = port_idx;
2308c2ecf20Sopenharmony_ci	c->adm = adm;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	init_waitqueue_head(&c->wait);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return c;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp,
2388c2ecf20Sopenharmony_ci				   struct apr_pkt *pkt, uint32_t rsp_opcode)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct device *dev = adm->dev;
2418c2ecf20Sopenharmony_ci	uint32_t opcode = pkt->hdr.opcode;
2428c2ecf20Sopenharmony_ci	int ret;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	mutex_lock(&adm->lock);
2458c2ecf20Sopenharmony_ci	copp->result.opcode = 0;
2468c2ecf20Sopenharmony_ci	copp->result.status = 0;
2478c2ecf20Sopenharmony_ci	ret = apr_send_pkt(adm->apr, pkt);
2488c2ecf20Sopenharmony_ci	if (ret < 0) {
2498c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to send APR packet\n");
2508c2ecf20Sopenharmony_ci		ret = -EINVAL;
2518c2ecf20Sopenharmony_ci		goto err;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Wait for the callback with copp id */
2558c2ecf20Sopenharmony_ci	if (rsp_opcode)
2568c2ecf20Sopenharmony_ci		ret = wait_event_timeout(copp->wait,
2578c2ecf20Sopenharmony_ci					 (copp->result.opcode == opcode) ||
2588c2ecf20Sopenharmony_ci					 (copp->result.opcode == rsp_opcode),
2598c2ecf20Sopenharmony_ci					 msecs_to_jiffies(TIMEOUT_MS));
2608c2ecf20Sopenharmony_ci	else
2618c2ecf20Sopenharmony_ci		ret = wait_event_timeout(copp->wait,
2628c2ecf20Sopenharmony_ci					 (copp->result.opcode == opcode),
2638c2ecf20Sopenharmony_ci					 msecs_to_jiffies(TIMEOUT_MS));
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (!ret) {
2668c2ecf20Sopenharmony_ci		dev_err(dev, "ADM copp cmd timedout\n");
2678c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
2688c2ecf20Sopenharmony_ci	} else if (copp->result.status > 0) {
2698c2ecf20Sopenharmony_ci		dev_err(dev, "DSP returned error[%d]\n",
2708c2ecf20Sopenharmony_ci			copp->result.status);
2718c2ecf20Sopenharmony_ci		ret = -EINVAL;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cierr:
2758c2ecf20Sopenharmony_ci	mutex_unlock(&adm->lock);
2768c2ecf20Sopenharmony_ci	return ret;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int q6adm_device_close(struct q6adm *adm, struct q6copp *copp,
2808c2ecf20Sopenharmony_ci			      int port_id, int copp_idx)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct apr_pkt close;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
2858c2ecf20Sopenharmony_ci					APR_HDR_LEN(APR_HDR_SIZE),
2868c2ecf20Sopenharmony_ci					APR_PKT_VER);
2878c2ecf20Sopenharmony_ci	close.hdr.pkt_size = sizeof(close);
2888c2ecf20Sopenharmony_ci	close.hdr.src_port = port_id;
2898c2ecf20Sopenharmony_ci	close.hdr.dest_port = copp->id;
2908c2ecf20Sopenharmony_ci	close.hdr.token = port_id << 16 | copp_idx;
2918c2ecf20Sopenharmony_ci	close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return q6adm_apr_send_copp_pkt(adm, copp, &close, 0);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic struct q6copp *q6adm_find_matching_copp(struct q6adm *adm,
2978c2ecf20Sopenharmony_ci					       int port_id, int topology,
2988c2ecf20Sopenharmony_ci					       int mode, int rate,
2998c2ecf20Sopenharmony_ci					       int channel_mode, int bit_width,
3008c2ecf20Sopenharmony_ci					       int app_type)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct q6copp *c = NULL;
3038c2ecf20Sopenharmony_ci	struct q6copp *ret = NULL;
3048c2ecf20Sopenharmony_ci	unsigned long flags;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adm->copps_list_lock, flags);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	list_for_each_entry(c, &adm->copps_list, node) {
3098c2ecf20Sopenharmony_ci		if ((port_id == c->afe_port) && (topology == c->topology) &&
3108c2ecf20Sopenharmony_ci		    (mode == c->mode) && (rate == c->rate) &&
3118c2ecf20Sopenharmony_ci		    (bit_width == c->bit_width) && (app_type == c->app_type)) {
3128c2ecf20Sopenharmony_ci			ret = c;
3138c2ecf20Sopenharmony_ci			kref_get(&c->refcount);
3148c2ecf20Sopenharmony_ci		}
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return ret;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int q6adm_device_open(struct q6adm *adm, struct q6copp *copp,
3228c2ecf20Sopenharmony_ci			     int port_id, int path, int topology,
3238c2ecf20Sopenharmony_ci			     int channel_mode, int bit_width, int rate)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct q6adm_cmd_device_open_v5 *open;
3268c2ecf20Sopenharmony_ci	int afe_port = q6afe_get_port_id(port_id);
3278c2ecf20Sopenharmony_ci	struct apr_pkt *pkt;
3288c2ecf20Sopenharmony_ci	void *p;
3298c2ecf20Sopenharmony_ci	int ret, pkt_size;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	pkt_size = APR_HDR_SIZE + sizeof(*open);
3328c2ecf20Sopenharmony_ci	p = kzalloc(pkt_size, GFP_KERNEL);
3338c2ecf20Sopenharmony_ci	if (!p)
3348c2ecf20Sopenharmony_ci		return -ENOMEM;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	pkt = p;
3378c2ecf20Sopenharmony_ci	open = p + APR_HDR_SIZE;
3388c2ecf20Sopenharmony_ci	pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
3398c2ecf20Sopenharmony_ci					   APR_HDR_LEN(APR_HDR_SIZE),
3408c2ecf20Sopenharmony_ci					   APR_PKT_VER);
3418c2ecf20Sopenharmony_ci	pkt->hdr.pkt_size = pkt_size;
3428c2ecf20Sopenharmony_ci	pkt->hdr.src_port = afe_port;
3438c2ecf20Sopenharmony_ci	pkt->hdr.dest_port = afe_port;
3448c2ecf20Sopenharmony_ci	pkt->hdr.token = port_id << 16 | copp->copp_idx;
3458c2ecf20Sopenharmony_ci	pkt->hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
3468c2ecf20Sopenharmony_ci	open->flags = ADM_LEGACY_DEVICE_SESSION;
3478c2ecf20Sopenharmony_ci	open->mode_of_operation = path;
3488c2ecf20Sopenharmony_ci	open->endpoint_id_1 = afe_port;
3498c2ecf20Sopenharmony_ci	open->topology_id = topology;
3508c2ecf20Sopenharmony_ci	open->dev_num_channel = channel_mode & 0x00FF;
3518c2ecf20Sopenharmony_ci	open->bit_width = bit_width;
3528c2ecf20Sopenharmony_ci	open->sample_rate = rate;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	ret = q6dsp_map_channels(&open->dev_channel_mapping[0],
3558c2ecf20Sopenharmony_ci				 channel_mode);
3568c2ecf20Sopenharmony_ci	if (ret)
3578c2ecf20Sopenharmony_ci		goto err;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	ret = q6adm_apr_send_copp_pkt(adm, copp, pkt,
3608c2ecf20Sopenharmony_ci				      ADM_CMDRSP_DEVICE_OPEN_V5);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cierr:
3638c2ecf20Sopenharmony_ci	kfree(pkt);
3648c2ecf20Sopenharmony_ci	return ret;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/**
3688c2ecf20Sopenharmony_ci * q6adm_open() - open adm and grab a free copp
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci * @dev: Pointer to adm child device.
3718c2ecf20Sopenharmony_ci * @port_id: port id
3728c2ecf20Sopenharmony_ci * @path: playback or capture path.
3738c2ecf20Sopenharmony_ci * @rate: rate at which copp is required.
3748c2ecf20Sopenharmony_ci * @channel_mode: channel mode
3758c2ecf20Sopenharmony_ci * @topology: adm topology id
3768c2ecf20Sopenharmony_ci * @perf_mode: performace mode.
3778c2ecf20Sopenharmony_ci * @bit_width: audio sample bit width
3788c2ecf20Sopenharmony_ci * @app_type: Application type.
3798c2ecf20Sopenharmony_ci * @acdb_id: ACDB id
3808c2ecf20Sopenharmony_ci *
3818c2ecf20Sopenharmony_ci * Return: Will be an negative on error or a valid copp pointer on success.
3828c2ecf20Sopenharmony_ci */
3838c2ecf20Sopenharmony_cistruct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate,
3848c2ecf20Sopenharmony_ci	       int channel_mode, int topology, int perf_mode,
3858c2ecf20Sopenharmony_ci	       uint16_t bit_width, int app_type, int acdb_id)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct q6adm *adm = dev_get_drvdata(dev->parent);
3888c2ecf20Sopenharmony_ci	struct q6copp *copp;
3898c2ecf20Sopenharmony_ci	unsigned long flags;
3908c2ecf20Sopenharmony_ci	int ret = 0;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (port_id < 0) {
3938c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid port_id 0x%x\n", port_id);
3948c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	copp = q6adm_find_matching_copp(adm, port_id, topology, perf_mode,
3988c2ecf20Sopenharmony_ci				      rate, channel_mode, bit_width, app_type);
3998c2ecf20Sopenharmony_ci	if (copp) {
4008c2ecf20Sopenharmony_ci		dev_err(dev, "Found Matching Copp 0x%x\n", copp->copp_idx);
4018c2ecf20Sopenharmony_ci		return copp;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	spin_lock_irqsave(&adm->copps_list_lock, flags);
4058c2ecf20Sopenharmony_ci	copp = q6adm_alloc_copp(adm, port_id);
4068c2ecf20Sopenharmony_ci	if (IS_ERR(copp)) {
4078c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&adm->copps_list_lock, flags);
4088c2ecf20Sopenharmony_ci		return ERR_CAST(copp);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	list_add_tail(&copp->node, &adm->copps_list);
4128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	kref_init(&copp->refcount);
4158c2ecf20Sopenharmony_ci	copp->topology = topology;
4168c2ecf20Sopenharmony_ci	copp->mode = perf_mode;
4178c2ecf20Sopenharmony_ci	copp->rate = rate;
4188c2ecf20Sopenharmony_ci	copp->channels = channel_mode;
4198c2ecf20Sopenharmony_ci	copp->bit_width = bit_width;
4208c2ecf20Sopenharmony_ci	copp->app_type = app_type;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	ret = q6adm_device_open(adm, copp, port_id, path, topology,
4238c2ecf20Sopenharmony_ci				channel_mode, bit_width, rate);
4248c2ecf20Sopenharmony_ci	if (ret < 0) {
4258c2ecf20Sopenharmony_ci		kref_put(&copp->refcount, q6adm_free_copp);
4268c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return copp;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(q6adm_open);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci/**
4348c2ecf20Sopenharmony_ci * q6adm_get_copp_id() - get copp index
4358c2ecf20Sopenharmony_ci *
4368c2ecf20Sopenharmony_ci * @copp: Pointer to valid copp
4378c2ecf20Sopenharmony_ci *
4388c2ecf20Sopenharmony_ci * Return: Will be an negative on error or a valid copp index on success.
4398c2ecf20Sopenharmony_ci **/
4408c2ecf20Sopenharmony_ciint q6adm_get_copp_id(struct q6copp *copp)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	if (!copp)
4438c2ecf20Sopenharmony_ci		return -EINVAL;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return copp->copp_idx;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(q6adm_get_copp_id);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci/**
4508c2ecf20Sopenharmony_ci * q6adm_matrix_map() - Map asm streams and afe ports using payload
4518c2ecf20Sopenharmony_ci *
4528c2ecf20Sopenharmony_ci * @dev: Pointer to adm child device.
4538c2ecf20Sopenharmony_ci * @path: playback or capture path.
4548c2ecf20Sopenharmony_ci * @payload_map: map between session id and afe ports.
4558c2ecf20Sopenharmony_ci * @perf_mode: Performace mode.
4568c2ecf20Sopenharmony_ci *
4578c2ecf20Sopenharmony_ci * Return: Will be an negative on error or a zero on success.
4588c2ecf20Sopenharmony_ci */
4598c2ecf20Sopenharmony_ciint q6adm_matrix_map(struct device *dev, int path,
4608c2ecf20Sopenharmony_ci		     struct route_payload payload_map, int perf_mode)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	struct q6adm *adm = dev_get_drvdata(dev->parent);
4638c2ecf20Sopenharmony_ci	struct q6adm_cmd_matrix_map_routings_v5 *route;
4648c2ecf20Sopenharmony_ci	struct q6adm_session_map_node_v5 *node;
4658c2ecf20Sopenharmony_ci	struct apr_pkt *pkt;
4668c2ecf20Sopenharmony_ci	uint16_t *copps_list;
4678c2ecf20Sopenharmony_ci	int pkt_size, ret, i, copp_idx;
4688c2ecf20Sopenharmony_ci	void *matrix_map = NULL;
4698c2ecf20Sopenharmony_ci	struct q6copp *copp;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* Assumes port_ids have already been validated during adm_open */
4728c2ecf20Sopenharmony_ci	pkt_size = (APR_HDR_SIZE + sizeof(*route) +  sizeof(*node) +
4738c2ecf20Sopenharmony_ci		    (sizeof(uint32_t) * payload_map.num_copps));
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	matrix_map = kzalloc(pkt_size, GFP_KERNEL);
4768c2ecf20Sopenharmony_ci	if (!matrix_map)
4778c2ecf20Sopenharmony_ci		return -ENOMEM;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	pkt = matrix_map;
4808c2ecf20Sopenharmony_ci	route = matrix_map + APR_HDR_SIZE;
4818c2ecf20Sopenharmony_ci	node = matrix_map + APR_HDR_SIZE + sizeof(*route);
4828c2ecf20Sopenharmony_ci	copps_list = matrix_map + APR_HDR_SIZE + sizeof(*route) + sizeof(*node);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
4858c2ecf20Sopenharmony_ci					   APR_HDR_LEN(APR_HDR_SIZE),
4868c2ecf20Sopenharmony_ci					   APR_PKT_VER);
4878c2ecf20Sopenharmony_ci	pkt->hdr.pkt_size = pkt_size;
4888c2ecf20Sopenharmony_ci	pkt->hdr.token = 0;
4898c2ecf20Sopenharmony_ci	pkt->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
4908c2ecf20Sopenharmony_ci	route->num_sessions = 1;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	switch (path) {
4938c2ecf20Sopenharmony_ci	case ADM_PATH_PLAYBACK:
4948c2ecf20Sopenharmony_ci		route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
4958c2ecf20Sopenharmony_ci		break;
4968c2ecf20Sopenharmony_ci	case ADM_PATH_LIVE_REC:
4978c2ecf20Sopenharmony_ci		route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
4988c2ecf20Sopenharmony_ci		break;
4998c2ecf20Sopenharmony_ci	default:
5008c2ecf20Sopenharmony_ci		dev_err(dev, "Wrong path set[%d]\n", path);
5018c2ecf20Sopenharmony_ci		break;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	node->session_id = payload_map.session_id;
5058c2ecf20Sopenharmony_ci	node->num_copps = payload_map.num_copps;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	for (i = 0; i < payload_map.num_copps; i++) {
5088c2ecf20Sopenharmony_ci		int port_idx = payload_map.port_id[i];
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		if (port_idx < 0) {
5118c2ecf20Sopenharmony_ci			dev_err(dev, "Invalid port_id 0x%x\n",
5128c2ecf20Sopenharmony_ci				payload_map.port_id[i]);
5138c2ecf20Sopenharmony_ci			kfree(pkt);
5148c2ecf20Sopenharmony_ci			return -EINVAL;
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci		copp_idx = payload_map.copp_idx[i];
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		copp = q6adm_find_copp(adm, port_idx, copp_idx);
5198c2ecf20Sopenharmony_ci		if (!copp) {
5208c2ecf20Sopenharmony_ci			kfree(pkt);
5218c2ecf20Sopenharmony_ci			return -EINVAL;
5228c2ecf20Sopenharmony_ci		}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci		copps_list[i] = copp->id;
5258c2ecf20Sopenharmony_ci		kref_put(&copp->refcount, q6adm_free_copp);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	mutex_lock(&adm->lock);
5298c2ecf20Sopenharmony_ci	adm->result.status = 0;
5308c2ecf20Sopenharmony_ci	adm->result.opcode = 0;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	ret = apr_send_pkt(adm->apr, pkt);
5338c2ecf20Sopenharmony_ci	if (ret < 0) {
5348c2ecf20Sopenharmony_ci		dev_err(dev, "routing for stream %d failed ret %d\n",
5358c2ecf20Sopenharmony_ci		       payload_map.session_id, ret);
5368c2ecf20Sopenharmony_ci		goto fail_cmd;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci	ret = wait_event_timeout(adm->matrix_map_wait,
5398c2ecf20Sopenharmony_ci				 adm->result.opcode == pkt->hdr.opcode,
5408c2ecf20Sopenharmony_ci				 msecs_to_jiffies(TIMEOUT_MS));
5418c2ecf20Sopenharmony_ci	if (!ret) {
5428c2ecf20Sopenharmony_ci		dev_err(dev, "routing for stream %d failed\n",
5438c2ecf20Sopenharmony_ci		       payload_map.session_id);
5448c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
5458c2ecf20Sopenharmony_ci		goto fail_cmd;
5468c2ecf20Sopenharmony_ci	} else if (adm->result.status > 0) {
5478c2ecf20Sopenharmony_ci		dev_err(dev, "DSP returned error[%d]\n",
5488c2ecf20Sopenharmony_ci			adm->result.status);
5498c2ecf20Sopenharmony_ci		ret = -EINVAL;
5508c2ecf20Sopenharmony_ci		goto fail_cmd;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cifail_cmd:
5548c2ecf20Sopenharmony_ci	mutex_unlock(&adm->lock);
5558c2ecf20Sopenharmony_ci	kfree(pkt);
5568c2ecf20Sopenharmony_ci	return ret;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(q6adm_matrix_map);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci/**
5618c2ecf20Sopenharmony_ci * q6adm_close() - Close adm copp
5628c2ecf20Sopenharmony_ci *
5638c2ecf20Sopenharmony_ci * @dev: Pointer to adm child device.
5648c2ecf20Sopenharmony_ci * @copp: pointer to previously opened copp
5658c2ecf20Sopenharmony_ci *
5668c2ecf20Sopenharmony_ci * Return: Will be an negative on error or a zero on success.
5678c2ecf20Sopenharmony_ci */
5688c2ecf20Sopenharmony_ciint q6adm_close(struct device *dev, struct q6copp *copp)
5698c2ecf20Sopenharmony_ci{
5708c2ecf20Sopenharmony_ci	struct q6adm *adm = dev_get_drvdata(dev->parent);
5718c2ecf20Sopenharmony_ci	int ret = 0;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	ret = q6adm_device_close(adm, copp, copp->afe_port, copp->copp_idx);
5748c2ecf20Sopenharmony_ci	if (ret < 0) {
5758c2ecf20Sopenharmony_ci		dev_err(adm->dev, "Failed to close copp %d\n", ret);
5768c2ecf20Sopenharmony_ci		return ret;
5778c2ecf20Sopenharmony_ci	}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	kref_put(&copp->refcount, q6adm_free_copp);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	return 0;
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(q6adm_close);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic int q6adm_probe(struct apr_device *adev)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct device *dev = &adev->dev;
5888c2ecf20Sopenharmony_ci	struct q6adm *adm;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	adm = devm_kzalloc(dev, sizeof(*adm), GFP_KERNEL);
5918c2ecf20Sopenharmony_ci	if (!adm)
5928c2ecf20Sopenharmony_ci		return -ENOMEM;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	adm->apr = adev;
5958c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, adm);
5968c2ecf20Sopenharmony_ci	adm->dev = dev;
5978c2ecf20Sopenharmony_ci	q6core_get_svc_api_info(adev->svc_id, &adm->ainfo);
5988c2ecf20Sopenharmony_ci	mutex_init(&adm->lock);
5998c2ecf20Sopenharmony_ci	init_waitqueue_head(&adm->matrix_map_wait);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&adm->copps_list);
6028c2ecf20Sopenharmony_ci	spin_lock_init(&adm->copps_list_lock);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	return of_platform_populate(dev->of_node, NULL, NULL, dev);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic int q6adm_remove(struct apr_device *adev)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	of_platform_depopulate(&adev->dev);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	return 0;
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
6158c2ecf20Sopenharmony_cistatic const struct of_device_id q6adm_device_id[]  = {
6168c2ecf20Sopenharmony_ci	{ .compatible = "qcom,q6adm" },
6178c2ecf20Sopenharmony_ci	{},
6188c2ecf20Sopenharmony_ci};
6198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, q6adm_device_id);
6208c2ecf20Sopenharmony_ci#endif
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic struct apr_driver qcom_q6adm_driver = {
6238c2ecf20Sopenharmony_ci	.probe = q6adm_probe,
6248c2ecf20Sopenharmony_ci	.remove = q6adm_remove,
6258c2ecf20Sopenharmony_ci	.callback = q6adm_callback,
6268c2ecf20Sopenharmony_ci	.driver = {
6278c2ecf20Sopenharmony_ci		.name = "qcom-q6adm",
6288c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(q6adm_device_id),
6298c2ecf20Sopenharmony_ci	},
6308c2ecf20Sopenharmony_ci};
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cimodule_apr_driver(qcom_q6adm_driver);
6338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Q6 Audio Device Manager");
6348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
635