xref: /OK3568_Linux_fs/kernel/sound/soc/qcom/qdsp6/q6adm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
3*4882a593Smuzhiyun // Copyright (c) 2018, Linaro Limited
4*4882a593Smuzhiyun 
5*4882a593Smuzhiyun #include <linux/device.h>
6*4882a593Smuzhiyun #include <linux/jiffies.h>
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/kref.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/of.h>
11*4882a593Smuzhiyun #include <linux/of_platform.h>
12*4882a593Smuzhiyun #include <linux/platform_device.h>
13*4882a593Smuzhiyun #include <linux/sched.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun #include <linux/soc/qcom/apr.h>
16*4882a593Smuzhiyun #include <linux/wait.h>
17*4882a593Smuzhiyun #include <sound/asound.h>
18*4882a593Smuzhiyun #include "q6adm.h"
19*4882a593Smuzhiyun #include "q6afe.h"
20*4882a593Smuzhiyun #include "q6core.h"
21*4882a593Smuzhiyun #include "q6dsp-common.h"
22*4882a593Smuzhiyun #include "q6dsp-errno.h"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define ADM_CMD_DEVICE_OPEN_V5		0x00010326
25*4882a593Smuzhiyun #define ADM_CMDRSP_DEVICE_OPEN_V5	0x00010329
26*4882a593Smuzhiyun #define ADM_CMD_DEVICE_CLOSE_V5		0x00010327
27*4882a593Smuzhiyun #define ADM_CMD_MATRIX_MAP_ROUTINGS_V5	0x00010325
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define TIMEOUT_MS 1000
30*4882a593Smuzhiyun #define RESET_COPP_ID 99
31*4882a593Smuzhiyun #define INVALID_COPP_ID 0xFF
32*4882a593Smuzhiyun /* Definition for a legacy device session. */
33*4882a593Smuzhiyun #define ADM_LEGACY_DEVICE_SESSION	0
34*4882a593Smuzhiyun #define ADM_MATRIX_ID_AUDIO_RX		0
35*4882a593Smuzhiyun #define ADM_MATRIX_ID_AUDIO_TX		1
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun struct q6copp {
38*4882a593Smuzhiyun 	int afe_port;
39*4882a593Smuzhiyun 	int copp_idx;
40*4882a593Smuzhiyun 	int id;
41*4882a593Smuzhiyun 	int topology;
42*4882a593Smuzhiyun 	int mode;
43*4882a593Smuzhiyun 	int rate;
44*4882a593Smuzhiyun 	int bit_width;
45*4882a593Smuzhiyun 	int channels;
46*4882a593Smuzhiyun 	int app_type;
47*4882a593Smuzhiyun 	int acdb_id;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	struct aprv2_ibasic_rsp_result_t result;
50*4882a593Smuzhiyun 	struct kref refcount;
51*4882a593Smuzhiyun 	wait_queue_head_t wait;
52*4882a593Smuzhiyun 	struct list_head node;
53*4882a593Smuzhiyun 	struct q6adm *adm;
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun struct q6adm {
57*4882a593Smuzhiyun 	struct apr_device *apr;
58*4882a593Smuzhiyun 	struct device *dev;
59*4882a593Smuzhiyun 	struct q6core_svc_api_info ainfo;
60*4882a593Smuzhiyun 	unsigned long copp_bitmap[AFE_MAX_PORTS];
61*4882a593Smuzhiyun 	struct list_head copps_list;
62*4882a593Smuzhiyun 	spinlock_t copps_list_lock;
63*4882a593Smuzhiyun 	struct aprv2_ibasic_rsp_result_t result;
64*4882a593Smuzhiyun 	struct mutex lock;
65*4882a593Smuzhiyun 	wait_queue_head_t matrix_map_wait;
66*4882a593Smuzhiyun };
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun struct q6adm_cmd_device_open_v5 {
69*4882a593Smuzhiyun 	u16 flags;
70*4882a593Smuzhiyun 	u16 mode_of_operation;
71*4882a593Smuzhiyun 	u16 endpoint_id_1;
72*4882a593Smuzhiyun 	u16 endpoint_id_2;
73*4882a593Smuzhiyun 	u32 topology_id;
74*4882a593Smuzhiyun 	u16 dev_num_channel;
75*4882a593Smuzhiyun 	u16 bit_width;
76*4882a593Smuzhiyun 	u32 sample_rate;
77*4882a593Smuzhiyun 	u8 dev_channel_mapping[8];
78*4882a593Smuzhiyun } __packed;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun struct q6adm_cmd_matrix_map_routings_v5 {
81*4882a593Smuzhiyun 	u32 matrix_id;
82*4882a593Smuzhiyun 	u32 num_sessions;
83*4882a593Smuzhiyun } __packed;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun struct q6adm_session_map_node_v5 {
86*4882a593Smuzhiyun 	u16 session_id;
87*4882a593Smuzhiyun 	u16 num_copps;
88*4882a593Smuzhiyun } __packed;
89*4882a593Smuzhiyun 
q6adm_find_copp(struct q6adm * adm,int port_idx,int copp_idx)90*4882a593Smuzhiyun static struct q6copp *q6adm_find_copp(struct q6adm *adm, int port_idx,
91*4882a593Smuzhiyun 				  int copp_idx)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	struct q6copp *c = NULL;
94*4882a593Smuzhiyun 	struct q6copp *ret = NULL;
95*4882a593Smuzhiyun 	unsigned long flags;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	spin_lock_irqsave(&adm->copps_list_lock, flags);
98*4882a593Smuzhiyun 	list_for_each_entry(c, &adm->copps_list, node) {
99*4882a593Smuzhiyun 		if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) {
100*4882a593Smuzhiyun 			ret = c;
101*4882a593Smuzhiyun 			kref_get(&c->refcount);
102*4882a593Smuzhiyun 			break;
103*4882a593Smuzhiyun 		}
104*4882a593Smuzhiyun 	}
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	return ret;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun 
q6adm_free_copp(struct kref * ref)112*4882a593Smuzhiyun static void q6adm_free_copp(struct kref *ref)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun 	struct q6copp *c = container_of(ref, struct q6copp, refcount);
115*4882a593Smuzhiyun 	struct q6adm *adm = c->adm;
116*4882a593Smuzhiyun 	unsigned long flags;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	spin_lock_irqsave(&adm->copps_list_lock, flags);
119*4882a593Smuzhiyun 	clear_bit(c->copp_idx, &adm->copp_bitmap[c->afe_port]);
120*4882a593Smuzhiyun 	list_del(&c->node);
121*4882a593Smuzhiyun 	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
122*4882a593Smuzhiyun 	kfree(c);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
q6adm_callback(struct apr_device * adev,struct apr_resp_pkt * data)125*4882a593Smuzhiyun static int q6adm_callback(struct apr_device *adev, struct apr_resp_pkt *data)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct aprv2_ibasic_rsp_result_t *result = data->payload;
128*4882a593Smuzhiyun 	int port_idx, copp_idx;
129*4882a593Smuzhiyun 	struct apr_hdr *hdr = &data->hdr;
130*4882a593Smuzhiyun 	struct q6copp *copp;
131*4882a593Smuzhiyun 	struct q6adm *adm = dev_get_drvdata(&adev->dev);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (!data->payload_size)
134*4882a593Smuzhiyun 		return 0;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	copp_idx = (hdr->token) & 0XFF;
137*4882a593Smuzhiyun 	port_idx = ((hdr->token) >> 16) & 0xFF;
138*4882a593Smuzhiyun 	if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
139*4882a593Smuzhiyun 		dev_err(&adev->dev, "Invalid port idx %d token %d\n",
140*4882a593Smuzhiyun 		       port_idx, hdr->token);
141*4882a593Smuzhiyun 		return 0;
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun 	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
144*4882a593Smuzhiyun 		dev_err(&adev->dev, "Invalid copp idx %d token %d\n",
145*4882a593Smuzhiyun 			copp_idx, hdr->token);
146*4882a593Smuzhiyun 		return 0;
147*4882a593Smuzhiyun 	}
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	switch (hdr->opcode) {
150*4882a593Smuzhiyun 	case APR_BASIC_RSP_RESULT: {
151*4882a593Smuzhiyun 		if (result->status != 0) {
152*4882a593Smuzhiyun 			dev_err(&adev->dev, "cmd = 0x%x return error = 0x%x\n",
153*4882a593Smuzhiyun 				result->opcode, result->status);
154*4882a593Smuzhiyun 		}
155*4882a593Smuzhiyun 		switch (result->opcode) {
156*4882a593Smuzhiyun 		case ADM_CMD_DEVICE_OPEN_V5:
157*4882a593Smuzhiyun 		case ADM_CMD_DEVICE_CLOSE_V5:
158*4882a593Smuzhiyun 			copp = q6adm_find_copp(adm, port_idx, copp_idx);
159*4882a593Smuzhiyun 			if (!copp)
160*4882a593Smuzhiyun 				return 0;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 			copp->result = *result;
163*4882a593Smuzhiyun 			wake_up(&copp->wait);
164*4882a593Smuzhiyun 			kref_put(&copp->refcount, q6adm_free_copp);
165*4882a593Smuzhiyun 			break;
166*4882a593Smuzhiyun 		case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
167*4882a593Smuzhiyun 			adm->result = *result;
168*4882a593Smuzhiyun 			wake_up(&adm->matrix_map_wait);
169*4882a593Smuzhiyun 			break;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 		default:
172*4882a593Smuzhiyun 			dev_err(&adev->dev, "Unknown Cmd: 0x%x\n",
173*4882a593Smuzhiyun 				result->opcode);
174*4882a593Smuzhiyun 			break;
175*4882a593Smuzhiyun 		}
176*4882a593Smuzhiyun 		return 0;
177*4882a593Smuzhiyun 	}
178*4882a593Smuzhiyun 	case ADM_CMDRSP_DEVICE_OPEN_V5: {
179*4882a593Smuzhiyun 		struct adm_cmd_rsp_device_open_v5 {
180*4882a593Smuzhiyun 			u32 status;
181*4882a593Smuzhiyun 			u16 copp_id;
182*4882a593Smuzhiyun 			u16 reserved;
183*4882a593Smuzhiyun 		} __packed * open = data->payload;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 		copp = q6adm_find_copp(adm, port_idx, copp_idx);
186*4882a593Smuzhiyun 		if (!copp)
187*4882a593Smuzhiyun 			return 0;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 		if (open->copp_id == INVALID_COPP_ID) {
190*4882a593Smuzhiyun 			dev_err(&adev->dev, "Invalid coppid rxed %d\n",
191*4882a593Smuzhiyun 				open->copp_id);
192*4882a593Smuzhiyun 			copp->result.status = ADSP_EBADPARAM;
193*4882a593Smuzhiyun 			wake_up(&copp->wait);
194*4882a593Smuzhiyun 			kref_put(&copp->refcount, q6adm_free_copp);
195*4882a593Smuzhiyun 			break;
196*4882a593Smuzhiyun 		}
197*4882a593Smuzhiyun 		copp->result.opcode = hdr->opcode;
198*4882a593Smuzhiyun 		copp->id = open->copp_id;
199*4882a593Smuzhiyun 		wake_up(&copp->wait);
200*4882a593Smuzhiyun 		kref_put(&copp->refcount, q6adm_free_copp);
201*4882a593Smuzhiyun 	}
202*4882a593Smuzhiyun 	break;
203*4882a593Smuzhiyun 	default:
204*4882a593Smuzhiyun 		dev_err(&adev->dev, "Unknown cmd:0x%x\n",
205*4882a593Smuzhiyun 		       hdr->opcode);
206*4882a593Smuzhiyun 		break;
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	return 0;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun 
q6adm_alloc_copp(struct q6adm * adm,int port_idx)212*4882a593Smuzhiyun static struct q6copp *q6adm_alloc_copp(struct q6adm *adm, int port_idx)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun 	struct q6copp *c;
215*4882a593Smuzhiyun 	int idx;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	idx = find_first_zero_bit(&adm->copp_bitmap[port_idx],
218*4882a593Smuzhiyun 				  MAX_COPPS_PER_PORT);
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	if (idx >= MAX_COPPS_PER_PORT)
221*4882a593Smuzhiyun 		return ERR_PTR(-EBUSY);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	c = kzalloc(sizeof(*c), GFP_ATOMIC);
224*4882a593Smuzhiyun 	if (!c)
225*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	set_bit(idx, &adm->copp_bitmap[port_idx]);
228*4882a593Smuzhiyun 	c->copp_idx = idx;
229*4882a593Smuzhiyun 	c->afe_port = port_idx;
230*4882a593Smuzhiyun 	c->adm = adm;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	init_waitqueue_head(&c->wait);
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	return c;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun 
q6adm_apr_send_copp_pkt(struct q6adm * adm,struct q6copp * copp,struct apr_pkt * pkt,uint32_t rsp_opcode)237*4882a593Smuzhiyun static int q6adm_apr_send_copp_pkt(struct q6adm *adm, struct q6copp *copp,
238*4882a593Smuzhiyun 				   struct apr_pkt *pkt, uint32_t rsp_opcode)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	struct device *dev = adm->dev;
241*4882a593Smuzhiyun 	uint32_t opcode = pkt->hdr.opcode;
242*4882a593Smuzhiyun 	int ret;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	mutex_lock(&adm->lock);
245*4882a593Smuzhiyun 	copp->result.opcode = 0;
246*4882a593Smuzhiyun 	copp->result.status = 0;
247*4882a593Smuzhiyun 	ret = apr_send_pkt(adm->apr, pkt);
248*4882a593Smuzhiyun 	if (ret < 0) {
249*4882a593Smuzhiyun 		dev_err(dev, "Failed to send APR packet\n");
250*4882a593Smuzhiyun 		ret = -EINVAL;
251*4882a593Smuzhiyun 		goto err;
252*4882a593Smuzhiyun 	}
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	/* Wait for the callback with copp id */
255*4882a593Smuzhiyun 	if (rsp_opcode)
256*4882a593Smuzhiyun 		ret = wait_event_timeout(copp->wait,
257*4882a593Smuzhiyun 					 (copp->result.opcode == opcode) ||
258*4882a593Smuzhiyun 					 (copp->result.opcode == rsp_opcode),
259*4882a593Smuzhiyun 					 msecs_to_jiffies(TIMEOUT_MS));
260*4882a593Smuzhiyun 	else
261*4882a593Smuzhiyun 		ret = wait_event_timeout(copp->wait,
262*4882a593Smuzhiyun 					 (copp->result.opcode == opcode),
263*4882a593Smuzhiyun 					 msecs_to_jiffies(TIMEOUT_MS));
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	if (!ret) {
266*4882a593Smuzhiyun 		dev_err(dev, "ADM copp cmd timedout\n");
267*4882a593Smuzhiyun 		ret = -ETIMEDOUT;
268*4882a593Smuzhiyun 	} else if (copp->result.status > 0) {
269*4882a593Smuzhiyun 		dev_err(dev, "DSP returned error[%d]\n",
270*4882a593Smuzhiyun 			copp->result.status);
271*4882a593Smuzhiyun 		ret = -EINVAL;
272*4882a593Smuzhiyun 	}
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun err:
275*4882a593Smuzhiyun 	mutex_unlock(&adm->lock);
276*4882a593Smuzhiyun 	return ret;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
q6adm_device_close(struct q6adm * adm,struct q6copp * copp,int port_id,int copp_idx)279*4882a593Smuzhiyun static int q6adm_device_close(struct q6adm *adm, struct q6copp *copp,
280*4882a593Smuzhiyun 			      int port_id, int copp_idx)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun 	struct apr_pkt close;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	close.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
285*4882a593Smuzhiyun 					APR_HDR_LEN(APR_HDR_SIZE),
286*4882a593Smuzhiyun 					APR_PKT_VER);
287*4882a593Smuzhiyun 	close.hdr.pkt_size = sizeof(close);
288*4882a593Smuzhiyun 	close.hdr.src_port = port_id;
289*4882a593Smuzhiyun 	close.hdr.dest_port = copp->id;
290*4882a593Smuzhiyun 	close.hdr.token = port_id << 16 | copp_idx;
291*4882a593Smuzhiyun 	close.hdr.opcode = ADM_CMD_DEVICE_CLOSE_V5;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	return q6adm_apr_send_copp_pkt(adm, copp, &close, 0);
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun 
q6adm_find_matching_copp(struct q6adm * adm,int port_id,int topology,int mode,int rate,int channel_mode,int bit_width,int app_type)296*4882a593Smuzhiyun static struct q6copp *q6adm_find_matching_copp(struct q6adm *adm,
297*4882a593Smuzhiyun 					       int port_id, int topology,
298*4882a593Smuzhiyun 					       int mode, int rate,
299*4882a593Smuzhiyun 					       int channel_mode, int bit_width,
300*4882a593Smuzhiyun 					       int app_type)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	struct q6copp *c = NULL;
303*4882a593Smuzhiyun 	struct q6copp *ret = NULL;
304*4882a593Smuzhiyun 	unsigned long flags;
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	spin_lock_irqsave(&adm->copps_list_lock, flags);
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	list_for_each_entry(c, &adm->copps_list, node) {
309*4882a593Smuzhiyun 		if ((port_id == c->afe_port) && (topology == c->topology) &&
310*4882a593Smuzhiyun 		    (mode == c->mode) && (rate == c->rate) &&
311*4882a593Smuzhiyun 		    (bit_width == c->bit_width) && (app_type == c->app_type)) {
312*4882a593Smuzhiyun 			ret = c;
313*4882a593Smuzhiyun 			kref_get(&c->refcount);
314*4882a593Smuzhiyun 		}
315*4882a593Smuzhiyun 	}
316*4882a593Smuzhiyun 	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	return ret;
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun 
q6adm_device_open(struct q6adm * adm,struct q6copp * copp,int port_id,int path,int topology,int channel_mode,int bit_width,int rate)321*4882a593Smuzhiyun static int q6adm_device_open(struct q6adm *adm, struct q6copp *copp,
322*4882a593Smuzhiyun 			     int port_id, int path, int topology,
323*4882a593Smuzhiyun 			     int channel_mode, int bit_width, int rate)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun 	struct q6adm_cmd_device_open_v5 *open;
326*4882a593Smuzhiyun 	int afe_port = q6afe_get_port_id(port_id);
327*4882a593Smuzhiyun 	struct apr_pkt *pkt;
328*4882a593Smuzhiyun 	void *p;
329*4882a593Smuzhiyun 	int ret, pkt_size;
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	pkt_size = APR_HDR_SIZE + sizeof(*open);
332*4882a593Smuzhiyun 	p = kzalloc(pkt_size, GFP_KERNEL);
333*4882a593Smuzhiyun 	if (!p)
334*4882a593Smuzhiyun 		return -ENOMEM;
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	pkt = p;
337*4882a593Smuzhiyun 	open = p + APR_HDR_SIZE;
338*4882a593Smuzhiyun 	pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
339*4882a593Smuzhiyun 					   APR_HDR_LEN(APR_HDR_SIZE),
340*4882a593Smuzhiyun 					   APR_PKT_VER);
341*4882a593Smuzhiyun 	pkt->hdr.pkt_size = pkt_size;
342*4882a593Smuzhiyun 	pkt->hdr.src_port = afe_port;
343*4882a593Smuzhiyun 	pkt->hdr.dest_port = afe_port;
344*4882a593Smuzhiyun 	pkt->hdr.token = port_id << 16 | copp->copp_idx;
345*4882a593Smuzhiyun 	pkt->hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
346*4882a593Smuzhiyun 	open->flags = ADM_LEGACY_DEVICE_SESSION;
347*4882a593Smuzhiyun 	open->mode_of_operation = path;
348*4882a593Smuzhiyun 	open->endpoint_id_1 = afe_port;
349*4882a593Smuzhiyun 	open->topology_id = topology;
350*4882a593Smuzhiyun 	open->dev_num_channel = channel_mode & 0x00FF;
351*4882a593Smuzhiyun 	open->bit_width = bit_width;
352*4882a593Smuzhiyun 	open->sample_rate = rate;
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	ret = q6dsp_map_channels(&open->dev_channel_mapping[0],
355*4882a593Smuzhiyun 				 channel_mode);
356*4882a593Smuzhiyun 	if (ret)
357*4882a593Smuzhiyun 		goto err;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	ret = q6adm_apr_send_copp_pkt(adm, copp, pkt,
360*4882a593Smuzhiyun 				      ADM_CMDRSP_DEVICE_OPEN_V5);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun err:
363*4882a593Smuzhiyun 	kfree(pkt);
364*4882a593Smuzhiyun 	return ret;
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun /**
368*4882a593Smuzhiyun  * q6adm_open() - open adm and grab a free copp
369*4882a593Smuzhiyun  *
370*4882a593Smuzhiyun  * @dev: Pointer to adm child device.
371*4882a593Smuzhiyun  * @port_id: port id
372*4882a593Smuzhiyun  * @path: playback or capture path.
373*4882a593Smuzhiyun  * @rate: rate at which copp is required.
374*4882a593Smuzhiyun  * @channel_mode: channel mode
375*4882a593Smuzhiyun  * @topology: adm topology id
376*4882a593Smuzhiyun  * @perf_mode: performace mode.
377*4882a593Smuzhiyun  * @bit_width: audio sample bit width
378*4882a593Smuzhiyun  * @app_type: Application type.
379*4882a593Smuzhiyun  * @acdb_id: ACDB id
380*4882a593Smuzhiyun  *
381*4882a593Smuzhiyun  * Return: Will be an negative on error or a valid copp pointer on success.
382*4882a593Smuzhiyun  */
q6adm_open(struct device * dev,int port_id,int path,int rate,int channel_mode,int topology,int perf_mode,uint16_t bit_width,int app_type,int acdb_id)383*4882a593Smuzhiyun struct q6copp *q6adm_open(struct device *dev, int port_id, int path, int rate,
384*4882a593Smuzhiyun 	       int channel_mode, int topology, int perf_mode,
385*4882a593Smuzhiyun 	       uint16_t bit_width, int app_type, int acdb_id)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun 	struct q6adm *adm = dev_get_drvdata(dev->parent);
388*4882a593Smuzhiyun 	struct q6copp *copp;
389*4882a593Smuzhiyun 	unsigned long flags;
390*4882a593Smuzhiyun 	int ret = 0;
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	if (port_id < 0) {
393*4882a593Smuzhiyun 		dev_err(dev, "Invalid port_id 0x%x\n", port_id);
394*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
395*4882a593Smuzhiyun 	}
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	copp = q6adm_find_matching_copp(adm, port_id, topology, perf_mode,
398*4882a593Smuzhiyun 				      rate, channel_mode, bit_width, app_type);
399*4882a593Smuzhiyun 	if (copp) {
400*4882a593Smuzhiyun 		dev_err(dev, "Found Matching Copp 0x%x\n", copp->copp_idx);
401*4882a593Smuzhiyun 		return copp;
402*4882a593Smuzhiyun 	}
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	spin_lock_irqsave(&adm->copps_list_lock, flags);
405*4882a593Smuzhiyun 	copp = q6adm_alloc_copp(adm, port_id);
406*4882a593Smuzhiyun 	if (IS_ERR(copp)) {
407*4882a593Smuzhiyun 		spin_unlock_irqrestore(&adm->copps_list_lock, flags);
408*4882a593Smuzhiyun 		return ERR_CAST(copp);
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	list_add_tail(&copp->node, &adm->copps_list);
412*4882a593Smuzhiyun 	spin_unlock_irqrestore(&adm->copps_list_lock, flags);
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	kref_init(&copp->refcount);
415*4882a593Smuzhiyun 	copp->topology = topology;
416*4882a593Smuzhiyun 	copp->mode = perf_mode;
417*4882a593Smuzhiyun 	copp->rate = rate;
418*4882a593Smuzhiyun 	copp->channels = channel_mode;
419*4882a593Smuzhiyun 	copp->bit_width = bit_width;
420*4882a593Smuzhiyun 	copp->app_type = app_type;
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 	ret = q6adm_device_open(adm, copp, port_id, path, topology,
423*4882a593Smuzhiyun 				channel_mode, bit_width, rate);
424*4882a593Smuzhiyun 	if (ret < 0) {
425*4882a593Smuzhiyun 		kref_put(&copp->refcount, q6adm_free_copp);
426*4882a593Smuzhiyun 		return ERR_PTR(ret);
427*4882a593Smuzhiyun 	}
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	return copp;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(q6adm_open);
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun /**
434*4882a593Smuzhiyun  * q6adm_get_copp_id() - get copp index
435*4882a593Smuzhiyun  *
436*4882a593Smuzhiyun  * @copp: Pointer to valid copp
437*4882a593Smuzhiyun  *
438*4882a593Smuzhiyun  * Return: Will be an negative on error or a valid copp index on success.
439*4882a593Smuzhiyun  **/
q6adm_get_copp_id(struct q6copp * copp)440*4882a593Smuzhiyun int q6adm_get_copp_id(struct q6copp *copp)
441*4882a593Smuzhiyun {
442*4882a593Smuzhiyun 	if (!copp)
443*4882a593Smuzhiyun 		return -EINVAL;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	return copp->copp_idx;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(q6adm_get_copp_id);
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun /**
450*4882a593Smuzhiyun  * q6adm_matrix_map() - Map asm streams and afe ports using payload
451*4882a593Smuzhiyun  *
452*4882a593Smuzhiyun  * @dev: Pointer to adm child device.
453*4882a593Smuzhiyun  * @path: playback or capture path.
454*4882a593Smuzhiyun  * @payload_map: map between session id and afe ports.
455*4882a593Smuzhiyun  * @perf_mode: Performace mode.
456*4882a593Smuzhiyun  *
457*4882a593Smuzhiyun  * Return: Will be an negative on error or a zero on success.
458*4882a593Smuzhiyun  */
q6adm_matrix_map(struct device * dev,int path,struct route_payload payload_map,int perf_mode)459*4882a593Smuzhiyun int q6adm_matrix_map(struct device *dev, int path,
460*4882a593Smuzhiyun 		     struct route_payload payload_map, int perf_mode)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun 	struct q6adm *adm = dev_get_drvdata(dev->parent);
463*4882a593Smuzhiyun 	struct q6adm_cmd_matrix_map_routings_v5 *route;
464*4882a593Smuzhiyun 	struct q6adm_session_map_node_v5 *node;
465*4882a593Smuzhiyun 	struct apr_pkt *pkt;
466*4882a593Smuzhiyun 	uint16_t *copps_list;
467*4882a593Smuzhiyun 	int pkt_size, ret, i, copp_idx;
468*4882a593Smuzhiyun 	void *matrix_map = NULL;
469*4882a593Smuzhiyun 	struct q6copp *copp;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	/* Assumes port_ids have already been validated during adm_open */
472*4882a593Smuzhiyun 	pkt_size = (APR_HDR_SIZE + sizeof(*route) +  sizeof(*node) +
473*4882a593Smuzhiyun 		    (sizeof(uint32_t) * payload_map.num_copps));
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	matrix_map = kzalloc(pkt_size, GFP_KERNEL);
476*4882a593Smuzhiyun 	if (!matrix_map)
477*4882a593Smuzhiyun 		return -ENOMEM;
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	pkt = matrix_map;
480*4882a593Smuzhiyun 	route = matrix_map + APR_HDR_SIZE;
481*4882a593Smuzhiyun 	node = matrix_map + APR_HDR_SIZE + sizeof(*route);
482*4882a593Smuzhiyun 	copps_list = matrix_map + APR_HDR_SIZE + sizeof(*route) + sizeof(*node);
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
485*4882a593Smuzhiyun 					   APR_HDR_LEN(APR_HDR_SIZE),
486*4882a593Smuzhiyun 					   APR_PKT_VER);
487*4882a593Smuzhiyun 	pkt->hdr.pkt_size = pkt_size;
488*4882a593Smuzhiyun 	pkt->hdr.token = 0;
489*4882a593Smuzhiyun 	pkt->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
490*4882a593Smuzhiyun 	route->num_sessions = 1;
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	switch (path) {
493*4882a593Smuzhiyun 	case ADM_PATH_PLAYBACK:
494*4882a593Smuzhiyun 		route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
495*4882a593Smuzhiyun 		break;
496*4882a593Smuzhiyun 	case ADM_PATH_LIVE_REC:
497*4882a593Smuzhiyun 		route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
498*4882a593Smuzhiyun 		break;
499*4882a593Smuzhiyun 	default:
500*4882a593Smuzhiyun 		dev_err(dev, "Wrong path set[%d]\n", path);
501*4882a593Smuzhiyun 		break;
502*4882a593Smuzhiyun 	}
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	node->session_id = payload_map.session_id;
505*4882a593Smuzhiyun 	node->num_copps = payload_map.num_copps;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	for (i = 0; i < payload_map.num_copps; i++) {
508*4882a593Smuzhiyun 		int port_idx = payload_map.port_id[i];
509*4882a593Smuzhiyun 
510*4882a593Smuzhiyun 		if (port_idx < 0) {
511*4882a593Smuzhiyun 			dev_err(dev, "Invalid port_id 0x%x\n",
512*4882a593Smuzhiyun 				payload_map.port_id[i]);
513*4882a593Smuzhiyun 			kfree(pkt);
514*4882a593Smuzhiyun 			return -EINVAL;
515*4882a593Smuzhiyun 		}
516*4882a593Smuzhiyun 		copp_idx = payload_map.copp_idx[i];
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 		copp = q6adm_find_copp(adm, port_idx, copp_idx);
519*4882a593Smuzhiyun 		if (!copp) {
520*4882a593Smuzhiyun 			kfree(pkt);
521*4882a593Smuzhiyun 			return -EINVAL;
522*4882a593Smuzhiyun 		}
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 		copps_list[i] = copp->id;
525*4882a593Smuzhiyun 		kref_put(&copp->refcount, q6adm_free_copp);
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	mutex_lock(&adm->lock);
529*4882a593Smuzhiyun 	adm->result.status = 0;
530*4882a593Smuzhiyun 	adm->result.opcode = 0;
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	ret = apr_send_pkt(adm->apr, pkt);
533*4882a593Smuzhiyun 	if (ret < 0) {
534*4882a593Smuzhiyun 		dev_err(dev, "routing for stream %d failed ret %d\n",
535*4882a593Smuzhiyun 		       payload_map.session_id, ret);
536*4882a593Smuzhiyun 		goto fail_cmd;
537*4882a593Smuzhiyun 	}
538*4882a593Smuzhiyun 	ret = wait_event_timeout(adm->matrix_map_wait,
539*4882a593Smuzhiyun 				 adm->result.opcode == pkt->hdr.opcode,
540*4882a593Smuzhiyun 				 msecs_to_jiffies(TIMEOUT_MS));
541*4882a593Smuzhiyun 	if (!ret) {
542*4882a593Smuzhiyun 		dev_err(dev, "routing for stream %d failed\n",
543*4882a593Smuzhiyun 		       payload_map.session_id);
544*4882a593Smuzhiyun 		ret = -ETIMEDOUT;
545*4882a593Smuzhiyun 		goto fail_cmd;
546*4882a593Smuzhiyun 	} else if (adm->result.status > 0) {
547*4882a593Smuzhiyun 		dev_err(dev, "DSP returned error[%d]\n",
548*4882a593Smuzhiyun 			adm->result.status);
549*4882a593Smuzhiyun 		ret = -EINVAL;
550*4882a593Smuzhiyun 		goto fail_cmd;
551*4882a593Smuzhiyun 	}
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun fail_cmd:
554*4882a593Smuzhiyun 	mutex_unlock(&adm->lock);
555*4882a593Smuzhiyun 	kfree(pkt);
556*4882a593Smuzhiyun 	return ret;
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(q6adm_matrix_map);
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun /**
561*4882a593Smuzhiyun  * q6adm_close() - Close adm copp
562*4882a593Smuzhiyun  *
563*4882a593Smuzhiyun  * @dev: Pointer to adm child device.
564*4882a593Smuzhiyun  * @copp: pointer to previously opened copp
565*4882a593Smuzhiyun  *
566*4882a593Smuzhiyun  * Return: Will be an negative on error or a zero on success.
567*4882a593Smuzhiyun  */
q6adm_close(struct device * dev,struct q6copp * copp)568*4882a593Smuzhiyun int q6adm_close(struct device *dev, struct q6copp *copp)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun 	struct q6adm *adm = dev_get_drvdata(dev->parent);
571*4882a593Smuzhiyun 	int ret = 0;
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	ret = q6adm_device_close(adm, copp, copp->afe_port, copp->copp_idx);
574*4882a593Smuzhiyun 	if (ret < 0) {
575*4882a593Smuzhiyun 		dev_err(adm->dev, "Failed to close copp %d\n", ret);
576*4882a593Smuzhiyun 		return ret;
577*4882a593Smuzhiyun 	}
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	kref_put(&copp->refcount, q6adm_free_copp);
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	return 0;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(q6adm_close);
584*4882a593Smuzhiyun 
q6adm_probe(struct apr_device * adev)585*4882a593Smuzhiyun static int q6adm_probe(struct apr_device *adev)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun 	struct device *dev = &adev->dev;
588*4882a593Smuzhiyun 	struct q6adm *adm;
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun 	adm = devm_kzalloc(dev, sizeof(*adm), GFP_KERNEL);
591*4882a593Smuzhiyun 	if (!adm)
592*4882a593Smuzhiyun 		return -ENOMEM;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 	adm->apr = adev;
595*4882a593Smuzhiyun 	dev_set_drvdata(dev, adm);
596*4882a593Smuzhiyun 	adm->dev = dev;
597*4882a593Smuzhiyun 	q6core_get_svc_api_info(adev->svc_id, &adm->ainfo);
598*4882a593Smuzhiyun 	mutex_init(&adm->lock);
599*4882a593Smuzhiyun 	init_waitqueue_head(&adm->matrix_map_wait);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	INIT_LIST_HEAD(&adm->copps_list);
602*4882a593Smuzhiyun 	spin_lock_init(&adm->copps_list_lock);
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	return of_platform_populate(dev->of_node, NULL, NULL, dev);
605*4882a593Smuzhiyun }
606*4882a593Smuzhiyun 
q6adm_remove(struct apr_device * adev)607*4882a593Smuzhiyun static int q6adm_remove(struct apr_device *adev)
608*4882a593Smuzhiyun {
609*4882a593Smuzhiyun 	of_platform_depopulate(&adev->dev);
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	return 0;
612*4882a593Smuzhiyun }
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun #ifdef CONFIG_OF
615*4882a593Smuzhiyun static const struct of_device_id q6adm_device_id[]  = {
616*4882a593Smuzhiyun 	{ .compatible = "qcom,q6adm" },
617*4882a593Smuzhiyun 	{},
618*4882a593Smuzhiyun };
619*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, q6adm_device_id);
620*4882a593Smuzhiyun #endif
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun static struct apr_driver qcom_q6adm_driver = {
623*4882a593Smuzhiyun 	.probe = q6adm_probe,
624*4882a593Smuzhiyun 	.remove = q6adm_remove,
625*4882a593Smuzhiyun 	.callback = q6adm_callback,
626*4882a593Smuzhiyun 	.driver = {
627*4882a593Smuzhiyun 		.name = "qcom-q6adm",
628*4882a593Smuzhiyun 		.of_match_table = of_match_ptr(q6adm_device_id),
629*4882a593Smuzhiyun 	},
630*4882a593Smuzhiyun };
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun module_apr_driver(qcom_q6adm_driver);
633*4882a593Smuzhiyun MODULE_DESCRIPTION("Q6 Audio Device Manager");
634*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
635