xref: /openbmc/linux/drivers/soundwire/stream.c (revision 40abc387)
189e59053SSanyog Kale // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
289e59053SSanyog Kale // Copyright(c) 2015-18 Intel Corporation.
389e59053SSanyog Kale 
489e59053SSanyog Kale /*
589e59053SSanyog Kale  *  stream.c - SoundWire Bus stream operations.
689e59053SSanyog Kale  */
789e59053SSanyog Kale 
889e59053SSanyog Kale #include <linux/delay.h>
989e59053SSanyog Kale #include <linux/device.h>
1089e59053SSanyog Kale #include <linux/init.h>
1189e59053SSanyog Kale #include <linux/module.h>
1289e59053SSanyog Kale #include <linux/mod_devicetable.h>
1389e59053SSanyog Kale #include <linux/slab.h>
14f8101c74SSanyog Kale #include <linux/soundwire/sdw_registers.h>
1589e59053SSanyog Kale #include <linux/soundwire/sdw.h>
16bd29c00eSPierre-Louis Bossart #include <linux/soundwire/sdw_type.h>
174550569bSPierre-Louis Bossart #include <sound/soc.h>
1889e59053SSanyog Kale #include "bus.h"
1989e59053SSanyog Kale 
2099b8a5d6SSanyog Kale /*
2199b8a5d6SSanyog Kale  * Array of supported rows and columns as per MIPI SoundWire Specification 1.1
2299b8a5d6SSanyog Kale  *
2399b8a5d6SSanyog Kale  * The rows are arranged as per the array index value programmed
2499b8a5d6SSanyog Kale  * in register. The index 15 has dummy value 0 in order to fill hole.
2599b8a5d6SSanyog Kale  */
26fe4b70f2SPierre-Louis Bossart int sdw_rows[SDW_FRAME_ROWS] = {48, 50, 60, 64, 75, 80, 125, 147,
2799b8a5d6SSanyog Kale 			96, 100, 120, 128, 150, 160, 250, 0,
2899b8a5d6SSanyog Kale 			192, 200, 240, 256, 72, 144, 90, 180};
299026118fSBard Liao EXPORT_SYMBOL(sdw_rows);
3099b8a5d6SSanyog Kale 
31fe4b70f2SPierre-Louis Bossart int sdw_cols[SDW_FRAME_COLS] = {2, 4, 6, 8, 10, 12, 14, 16};
329026118fSBard Liao EXPORT_SYMBOL(sdw_cols);
3399b8a5d6SSanyog Kale 
sdw_find_col_index(int col)34fe4b70f2SPierre-Louis Bossart int sdw_find_col_index(int col)
3599b8a5d6SSanyog Kale {
3699b8a5d6SSanyog Kale 	int i;
3799b8a5d6SSanyog Kale 
3899b8a5d6SSanyog Kale 	for (i = 0; i < SDW_FRAME_COLS; i++) {
39fe4b70f2SPierre-Louis Bossart 		if (sdw_cols[i] == col)
4099b8a5d6SSanyog Kale 			return i;
4199b8a5d6SSanyog Kale 	}
4299b8a5d6SSanyog Kale 
4399b8a5d6SSanyog Kale 	pr_warn("Requested column not found, selecting lowest column no: 2\n");
4499b8a5d6SSanyog Kale 	return 0;
4599b8a5d6SSanyog Kale }
46fe4b70f2SPierre-Louis Bossart EXPORT_SYMBOL(sdw_find_col_index);
4799b8a5d6SSanyog Kale 
sdw_find_row_index(int row)48fe4b70f2SPierre-Louis Bossart int sdw_find_row_index(int row)
4999b8a5d6SSanyog Kale {
5099b8a5d6SSanyog Kale 	int i;
5199b8a5d6SSanyog Kale 
5299b8a5d6SSanyog Kale 	for (i = 0; i < SDW_FRAME_ROWS; i++) {
53fe4b70f2SPierre-Louis Bossart 		if (sdw_rows[i] == row)
5499b8a5d6SSanyog Kale 			return i;
5599b8a5d6SSanyog Kale 	}
5699b8a5d6SSanyog Kale 
5799b8a5d6SSanyog Kale 	pr_warn("Requested row not found, selecting lowest row no: 48\n");
5899b8a5d6SSanyog Kale 	return 0;
5999b8a5d6SSanyog Kale }
60fe4b70f2SPierre-Louis Bossart EXPORT_SYMBOL(sdw_find_row_index);
61897fe40eSVinod Koul 
_sdw_program_slave_port_params(struct sdw_bus * bus,struct sdw_slave * slave,struct sdw_transport_params * t_params,enum sdw_dpn_type type)62f8101c74SSanyog Kale static int _sdw_program_slave_port_params(struct sdw_bus *bus,
63f8101c74SSanyog Kale 					  struct sdw_slave *slave,
64f8101c74SSanyog Kale 					  struct sdw_transport_params *t_params,
65f8101c74SSanyog Kale 					  enum sdw_dpn_type type)
66f8101c74SSanyog Kale {
67f8101c74SSanyog Kale 	u32 addr1, addr2, addr3, addr4;
68f8101c74SSanyog Kale 	int ret;
69f8101c74SSanyog Kale 	u16 wbuf;
70f8101c74SSanyog Kale 
71f8101c74SSanyog Kale 	if (bus->params.next_bank) {
72f8101c74SSanyog Kale 		addr1 = SDW_DPN_OFFSETCTRL2_B1(t_params->port_num);
73f8101c74SSanyog Kale 		addr2 = SDW_DPN_BLOCKCTRL3_B1(t_params->port_num);
74f8101c74SSanyog Kale 		addr3 = SDW_DPN_SAMPLECTRL2_B1(t_params->port_num);
75f8101c74SSanyog Kale 		addr4 = SDW_DPN_HCTRL_B1(t_params->port_num);
76f8101c74SSanyog Kale 	} else {
77f8101c74SSanyog Kale 		addr1 = SDW_DPN_OFFSETCTRL2_B0(t_params->port_num);
78f8101c74SSanyog Kale 		addr2 = SDW_DPN_BLOCKCTRL3_B0(t_params->port_num);
79f8101c74SSanyog Kale 		addr3 = SDW_DPN_SAMPLECTRL2_B0(t_params->port_num);
80f8101c74SSanyog Kale 		addr4 = SDW_DPN_HCTRL_B0(t_params->port_num);
81f8101c74SSanyog Kale 	}
82f8101c74SSanyog Kale 
83f8101c74SSanyog Kale 	/* Program DPN_OffsetCtrl2 registers */
84545c3651SCharles Keepax 	ret = sdw_write_no_pm(slave, addr1, t_params->offset2);
85f8101c74SSanyog Kale 	if (ret < 0) {
8617ed5befSPierre-Louis Bossart 		dev_err(bus->dev, "DPN_OffsetCtrl2 register write failed\n");
87f8101c74SSanyog Kale 		return ret;
88f8101c74SSanyog Kale 	}
89f8101c74SSanyog Kale 
90f8101c74SSanyog Kale 	/* Program DPN_BlockCtrl3 register */
91545c3651SCharles Keepax 	ret = sdw_write_no_pm(slave, addr2, t_params->blk_pkg_mode);
92f8101c74SSanyog Kale 	if (ret < 0) {
9317ed5befSPierre-Louis Bossart 		dev_err(bus->dev, "DPN_BlockCtrl3 register write failed\n");
94f8101c74SSanyog Kale 		return ret;
95f8101c74SSanyog Kale 	}
96f8101c74SSanyog Kale 
97f8101c74SSanyog Kale 	/*
98f8101c74SSanyog Kale 	 * Data ports are FULL, SIMPLE and REDUCED. This function handles
997d3b3cdfSVinod Koul 	 * FULL and REDUCED only and beyond this point only FULL is
100f8101c74SSanyog Kale 	 * handled, so bail out if we are not FULL data port type
101f8101c74SSanyog Kale 	 */
102f8101c74SSanyog Kale 	if (type != SDW_DPN_FULL)
103f8101c74SSanyog Kale 		return ret;
104f8101c74SSanyog Kale 
105f8101c74SSanyog Kale 	/* Program DPN_SampleCtrl2 register */
10641ff9174SVinod Koul 	wbuf = FIELD_GET(SDW_DPN_SAMPLECTRL_HIGH, t_params->sample_interval - 1);
107f8101c74SSanyog Kale 
108545c3651SCharles Keepax 	ret = sdw_write_no_pm(slave, addr3, wbuf);
109f8101c74SSanyog Kale 	if (ret < 0) {
11017ed5befSPierre-Louis Bossart 		dev_err(bus->dev, "DPN_SampleCtrl2 register write failed\n");
111f8101c74SSanyog Kale 		return ret;
112f8101c74SSanyog Kale 	}
113f8101c74SSanyog Kale 
114f8101c74SSanyog Kale 	/* Program DPN_HCtrl register */
11541ff9174SVinod Koul 	wbuf = FIELD_PREP(SDW_DPN_HCTRL_HSTART, t_params->hstart);
11641ff9174SVinod Koul 	wbuf |= FIELD_PREP(SDW_DPN_HCTRL_HSTOP, t_params->hstop);
117f8101c74SSanyog Kale 
118545c3651SCharles Keepax 	ret = sdw_write_no_pm(slave, addr4, wbuf);
119f8101c74SSanyog Kale 	if (ret < 0)
12017ed5befSPierre-Louis Bossart 		dev_err(bus->dev, "DPN_HCtrl register write failed\n");
121f8101c74SSanyog Kale 
122f8101c74SSanyog Kale 	return ret;
123f8101c74SSanyog Kale }
124f8101c74SSanyog Kale 
sdw_program_slave_port_params(struct sdw_bus * bus,struct sdw_slave_runtime * s_rt,struct sdw_port_runtime * p_rt)125f8101c74SSanyog Kale static int sdw_program_slave_port_params(struct sdw_bus *bus,
126f8101c74SSanyog Kale 					 struct sdw_slave_runtime *s_rt,
127f8101c74SSanyog Kale 					 struct sdw_port_runtime *p_rt)
128f8101c74SSanyog Kale {
129f8101c74SSanyog Kale 	struct sdw_transport_params *t_params = &p_rt->transport_params;
130f8101c74SSanyog Kale 	struct sdw_port_params *p_params = &p_rt->port_params;
131f8101c74SSanyog Kale 	struct sdw_slave_prop *slave_prop = &s_rt->slave->prop;
132f8101c74SSanyog Kale 	u32 addr1, addr2, addr3, addr4, addr5, addr6;
133f8101c74SSanyog Kale 	struct sdw_dpn_prop *dpn_prop;
134f8101c74SSanyog Kale 	int ret;
135f8101c74SSanyog Kale 	u8 wbuf;
136f8101c74SSanyog Kale 
13724f08b3aSBard Liao 	if (s_rt->slave->is_mockup_device)
13824f08b3aSBard Liao 		return 0;
13924f08b3aSBard Liao 
140f8101c74SSanyog Kale 	dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
141f8101c74SSanyog Kale 					  s_rt->direction,
142f8101c74SSanyog Kale 					  t_params->port_num);
143f8101c74SSanyog Kale 	if (!dpn_prop)
144f8101c74SSanyog Kale 		return -EINVAL;
145f8101c74SSanyog Kale 
146f8101c74SSanyog Kale 	addr1 = SDW_DPN_PORTCTRL(t_params->port_num);
147f8101c74SSanyog Kale 	addr2 = SDW_DPN_BLOCKCTRL1(t_params->port_num);
148f8101c74SSanyog Kale 
149f8101c74SSanyog Kale 	if (bus->params.next_bank) {
150f8101c74SSanyog Kale 		addr3 = SDW_DPN_SAMPLECTRL1_B1(t_params->port_num);
151f8101c74SSanyog Kale 		addr4 = SDW_DPN_OFFSETCTRL1_B1(t_params->port_num);
152f8101c74SSanyog Kale 		addr5 = SDW_DPN_BLOCKCTRL2_B1(t_params->port_num);
153f8101c74SSanyog Kale 		addr6 = SDW_DPN_LANECTRL_B1(t_params->port_num);
154f8101c74SSanyog Kale 
155f8101c74SSanyog Kale 	} else {
156f8101c74SSanyog Kale 		addr3 = SDW_DPN_SAMPLECTRL1_B0(t_params->port_num);
157f8101c74SSanyog Kale 		addr4 = SDW_DPN_OFFSETCTRL1_B0(t_params->port_num);
158f8101c74SSanyog Kale 		addr5 = SDW_DPN_BLOCKCTRL2_B0(t_params->port_num);
159f8101c74SSanyog Kale 		addr6 = SDW_DPN_LANECTRL_B0(t_params->port_num);
160f8101c74SSanyog Kale 	}
161f8101c74SSanyog Kale 
162f8101c74SSanyog Kale 	/* Program DPN_PortCtrl register */
16341ff9174SVinod Koul 	wbuf = FIELD_PREP(SDW_DPN_PORTCTRL_DATAMODE, p_params->data_mode);
16441ff9174SVinod Koul 	wbuf |= FIELD_PREP(SDW_DPN_PORTCTRL_FLOWMODE, p_params->flow_mode);
165f8101c74SSanyog Kale 
166545c3651SCharles Keepax 	ret = sdw_update_no_pm(s_rt->slave, addr1, 0xF, wbuf);
167f8101c74SSanyog Kale 	if (ret < 0) {
168f8101c74SSanyog Kale 		dev_err(&s_rt->slave->dev,
16917ed5befSPierre-Louis Bossart 			"DPN_PortCtrl register write failed for port %d\n",
170f8101c74SSanyog Kale 			t_params->port_num);
171f8101c74SSanyog Kale 		return ret;
172f8101c74SSanyog Kale 	}
173f8101c74SSanyog Kale 
174a9107de4SSrinivas Kandagatla 	if (!dpn_prop->read_only_wordlength) {
175f8101c74SSanyog Kale 		/* Program DPN_BlockCtrl1 register */
176545c3651SCharles Keepax 		ret = sdw_write_no_pm(s_rt->slave, addr2, (p_params->bps - 1));
177f8101c74SSanyog Kale 		if (ret < 0) {
178f8101c74SSanyog Kale 			dev_err(&s_rt->slave->dev,
17917ed5befSPierre-Louis Bossart 				"DPN_BlockCtrl1 register write failed for port %d\n",
180f8101c74SSanyog Kale 				t_params->port_num);
181f8101c74SSanyog Kale 			return ret;
182f8101c74SSanyog Kale 		}
183a9107de4SSrinivas Kandagatla 	}
184f8101c74SSanyog Kale 
185f8101c74SSanyog Kale 	/* Program DPN_SampleCtrl1 register */
186f8101c74SSanyog Kale 	wbuf = (t_params->sample_interval - 1) & SDW_DPN_SAMPLECTRL_LOW;
187545c3651SCharles Keepax 	ret = sdw_write_no_pm(s_rt->slave, addr3, wbuf);
188f8101c74SSanyog Kale 	if (ret < 0) {
189f8101c74SSanyog Kale 		dev_err(&s_rt->slave->dev,
19017ed5befSPierre-Louis Bossart 			"DPN_SampleCtrl1 register write failed for port %d\n",
191f8101c74SSanyog Kale 			t_params->port_num);
192f8101c74SSanyog Kale 		return ret;
193f8101c74SSanyog Kale 	}
194f8101c74SSanyog Kale 
195f8101c74SSanyog Kale 	/* Program DPN_OffsetCtrl1 registers */
196545c3651SCharles Keepax 	ret = sdw_write_no_pm(s_rt->slave, addr4, t_params->offset1);
197f8101c74SSanyog Kale 	if (ret < 0) {
198f8101c74SSanyog Kale 		dev_err(&s_rt->slave->dev,
19917ed5befSPierre-Louis Bossart 			"DPN_OffsetCtrl1 register write failed for port %d\n",
200f8101c74SSanyog Kale 			t_params->port_num);
201f8101c74SSanyog Kale 		return ret;
202f8101c74SSanyog Kale 	}
203f8101c74SSanyog Kale 
204f8101c74SSanyog Kale 	/* Program DPN_BlockCtrl2 register*/
205f8101c74SSanyog Kale 	if (t_params->blk_grp_ctrl_valid) {
206545c3651SCharles Keepax 		ret = sdw_write_no_pm(s_rt->slave, addr5, t_params->blk_grp_ctrl);
207f8101c74SSanyog Kale 		if (ret < 0) {
208f8101c74SSanyog Kale 			dev_err(&s_rt->slave->dev,
20917ed5befSPierre-Louis Bossart 				"DPN_BlockCtrl2 reg write failed for port %d\n",
210f8101c74SSanyog Kale 				t_params->port_num);
211f8101c74SSanyog Kale 			return ret;
212f8101c74SSanyog Kale 		}
213f8101c74SSanyog Kale 	}
214f8101c74SSanyog Kale 
215f8101c74SSanyog Kale 	/* program DPN_LaneCtrl register */
216f8101c74SSanyog Kale 	if (slave_prop->lane_control_support) {
217545c3651SCharles Keepax 		ret = sdw_write_no_pm(s_rt->slave, addr6, t_params->lane_ctrl);
218f8101c74SSanyog Kale 		if (ret < 0) {
219f8101c74SSanyog Kale 			dev_err(&s_rt->slave->dev,
22017ed5befSPierre-Louis Bossart 				"DPN_LaneCtrl register write failed for port %d\n",
221f8101c74SSanyog Kale 				t_params->port_num);
222f8101c74SSanyog Kale 			return ret;
223f8101c74SSanyog Kale 		}
224f8101c74SSanyog Kale 	}
225f8101c74SSanyog Kale 
226f8101c74SSanyog Kale 	if (dpn_prop->type != SDW_DPN_SIMPLE) {
227f8101c74SSanyog Kale 		ret = _sdw_program_slave_port_params(bus, s_rt->slave,
228f8101c74SSanyog Kale 						     t_params, dpn_prop->type);
229f8101c74SSanyog Kale 		if (ret < 0)
230f8101c74SSanyog Kale 			dev_err(&s_rt->slave->dev,
23117ed5befSPierre-Louis Bossart 				"Transport reg write failed for port: %d\n",
232f8101c74SSanyog Kale 				t_params->port_num);
233f8101c74SSanyog Kale 	}
234f8101c74SSanyog Kale 
235f8101c74SSanyog Kale 	return ret;
236f8101c74SSanyog Kale }
237f8101c74SSanyog Kale 
sdw_program_master_port_params(struct sdw_bus * bus,struct sdw_port_runtime * p_rt)238f8101c74SSanyog Kale static int sdw_program_master_port_params(struct sdw_bus *bus,
239f8101c74SSanyog Kale 					  struct sdw_port_runtime *p_rt)
240f8101c74SSanyog Kale {
241f8101c74SSanyog Kale 	int ret;
242f8101c74SSanyog Kale 
243f8101c74SSanyog Kale 	/*
244f8101c74SSanyog Kale 	 * we need to set transport and port parameters for the port.
2457d3b3cdfSVinod Koul 	 * Transport parameters refers to the sample interval, offsets and
246f8101c74SSanyog Kale 	 * hstart/stop etc of the data. Port parameters refers to word
247f8101c74SSanyog Kale 	 * length, flow mode etc of the port
248f8101c74SSanyog Kale 	 */
249f8101c74SSanyog Kale 	ret = bus->port_ops->dpn_set_port_transport_params(bus,
250f8101c74SSanyog Kale 					&p_rt->transport_params,
251f8101c74SSanyog Kale 					bus->params.next_bank);
252f8101c74SSanyog Kale 	if (ret < 0)
253f8101c74SSanyog Kale 		return ret;
254f8101c74SSanyog Kale 
255f8101c74SSanyog Kale 	return bus->port_ops->dpn_set_port_params(bus,
256f8101c74SSanyog Kale 						  &p_rt->port_params,
257f8101c74SSanyog Kale 						  bus->params.next_bank);
258f8101c74SSanyog Kale }
259f8101c74SSanyog Kale 
260f8101c74SSanyog Kale /**
261f8101c74SSanyog Kale  * sdw_program_port_params() - Programs transport parameters of Master(s)
262f8101c74SSanyog Kale  * and Slave(s)
263f8101c74SSanyog Kale  *
264f8101c74SSanyog Kale  * @m_rt: Master stream runtime
265f8101c74SSanyog Kale  */
sdw_program_port_params(struct sdw_master_runtime * m_rt)266f8101c74SSanyog Kale static int sdw_program_port_params(struct sdw_master_runtime *m_rt)
267f8101c74SSanyog Kale {
2685920a29dSPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt;
269f8101c74SSanyog Kale 	struct sdw_bus *bus = m_rt->bus;
270f8101c74SSanyog Kale 	struct sdw_port_runtime *p_rt;
271f8101c74SSanyog Kale 	int ret = 0;
272f8101c74SSanyog Kale 
273f8101c74SSanyog Kale 	/* Program transport & port parameters for Slave(s) */
274f8101c74SSanyog Kale 	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
275f8101c74SSanyog Kale 		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
276f8101c74SSanyog Kale 			ret = sdw_program_slave_port_params(bus, s_rt, p_rt);
277f8101c74SSanyog Kale 			if (ret < 0)
278f8101c74SSanyog Kale 				return ret;
279f8101c74SSanyog Kale 		}
280f8101c74SSanyog Kale 	}
281f8101c74SSanyog Kale 
282f8101c74SSanyog Kale 	/* Program transport & port parameters for Master(s) */
283f8101c74SSanyog Kale 	list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
284f8101c74SSanyog Kale 		ret = sdw_program_master_port_params(bus, p_rt);
285f8101c74SSanyog Kale 		if (ret < 0)
286f8101c74SSanyog Kale 			return ret;
287f8101c74SSanyog Kale 	}
288f8101c74SSanyog Kale 
289f8101c74SSanyog Kale 	return 0;
290f8101c74SSanyog Kale }
291f8101c74SSanyog Kale 
29289e59053SSanyog Kale /**
29379df15b7SSanyog Kale  * sdw_enable_disable_slave_ports: Enable/disable slave data port
29479df15b7SSanyog Kale  *
29579df15b7SSanyog Kale  * @bus: bus instance
29679df15b7SSanyog Kale  * @s_rt: slave runtime
29779df15b7SSanyog Kale  * @p_rt: port runtime
29879df15b7SSanyog Kale  * @en: enable or disable operation
29979df15b7SSanyog Kale  *
30079df15b7SSanyog Kale  * This function only sets the enable/disable bits in the relevant bank, the
30179df15b7SSanyog Kale  * actual enable/disable is done with a bank switch
30279df15b7SSanyog Kale  */
sdw_enable_disable_slave_ports(struct sdw_bus * bus,struct sdw_slave_runtime * s_rt,struct sdw_port_runtime * p_rt,bool en)30379df15b7SSanyog Kale static int sdw_enable_disable_slave_ports(struct sdw_bus *bus,
30479df15b7SSanyog Kale 					  struct sdw_slave_runtime *s_rt,
3051fe74a5eSPierre-Louis Bossart 					  struct sdw_port_runtime *p_rt,
3061fe74a5eSPierre-Louis Bossart 					  bool en)
30779df15b7SSanyog Kale {
30879df15b7SSanyog Kale 	struct sdw_transport_params *t_params = &p_rt->transport_params;
30979df15b7SSanyog Kale 	u32 addr;
31079df15b7SSanyog Kale 	int ret;
31179df15b7SSanyog Kale 
31279df15b7SSanyog Kale 	if (bus->params.next_bank)
31379df15b7SSanyog Kale 		addr = SDW_DPN_CHANNELEN_B1(p_rt->num);
31479df15b7SSanyog Kale 	else
31579df15b7SSanyog Kale 		addr = SDW_DPN_CHANNELEN_B0(p_rt->num);
31679df15b7SSanyog Kale 
31779df15b7SSanyog Kale 	/*
31879df15b7SSanyog Kale 	 * Since bus doesn't support sharing a port across two streams,
31979df15b7SSanyog Kale 	 * it is safe to reset this register
32079df15b7SSanyog Kale 	 */
32179df15b7SSanyog Kale 	if (en)
322545c3651SCharles Keepax 		ret = sdw_write_no_pm(s_rt->slave, addr, p_rt->ch_mask);
32379df15b7SSanyog Kale 	else
324545c3651SCharles Keepax 		ret = sdw_write_no_pm(s_rt->slave, addr, 0x0);
32579df15b7SSanyog Kale 
32679df15b7SSanyog Kale 	if (ret < 0)
32779df15b7SSanyog Kale 		dev_err(&s_rt->slave->dev,
32817ed5befSPierre-Louis Bossart 			"Slave chn_en reg write failed:%d port:%d\n",
32979df15b7SSanyog Kale 			ret, t_params->port_num);
33079df15b7SSanyog Kale 
33179df15b7SSanyog Kale 	return ret;
33279df15b7SSanyog Kale }
33379df15b7SSanyog Kale 
sdw_enable_disable_master_ports(struct sdw_master_runtime * m_rt,struct sdw_port_runtime * p_rt,bool en)33479df15b7SSanyog Kale static int sdw_enable_disable_master_ports(struct sdw_master_runtime *m_rt,
3351fe74a5eSPierre-Louis Bossart 					   struct sdw_port_runtime *p_rt,
3361fe74a5eSPierre-Louis Bossart 					   bool en)
33779df15b7SSanyog Kale {
33879df15b7SSanyog Kale 	struct sdw_transport_params *t_params = &p_rt->transport_params;
33979df15b7SSanyog Kale 	struct sdw_bus *bus = m_rt->bus;
34079df15b7SSanyog Kale 	struct sdw_enable_ch enable_ch;
341a25eab29SPierre-Louis Bossart 	int ret;
34279df15b7SSanyog Kale 
34379df15b7SSanyog Kale 	enable_ch.port_num = p_rt->num;
34479df15b7SSanyog Kale 	enable_ch.ch_mask = p_rt->ch_mask;
34579df15b7SSanyog Kale 	enable_ch.enable = en;
34679df15b7SSanyog Kale 
34779df15b7SSanyog Kale 	/* Perform Master port channel(s) enable/disable */
34879df15b7SSanyog Kale 	if (bus->port_ops->dpn_port_enable_ch) {
34979df15b7SSanyog Kale 		ret = bus->port_ops->dpn_port_enable_ch(bus,
3501fe74a5eSPierre-Louis Bossart 							&enable_ch,
3511fe74a5eSPierre-Louis Bossart 							bus->params.next_bank);
35279df15b7SSanyog Kale 		if (ret < 0) {
35379df15b7SSanyog Kale 			dev_err(bus->dev,
35417ed5befSPierre-Louis Bossart 				"Master chn_en write failed:%d port:%d\n",
35579df15b7SSanyog Kale 				ret, t_params->port_num);
35679df15b7SSanyog Kale 			return ret;
35779df15b7SSanyog Kale 		}
35879df15b7SSanyog Kale 	} else {
35979df15b7SSanyog Kale 		dev_err(bus->dev,
36079df15b7SSanyog Kale 			"dpn_port_enable_ch not supported, %s failed\n",
36179df15b7SSanyog Kale 			en ? "enable" : "disable");
36279df15b7SSanyog Kale 		return -EINVAL;
36379df15b7SSanyog Kale 	}
36479df15b7SSanyog Kale 
36579df15b7SSanyog Kale 	return 0;
36679df15b7SSanyog Kale }
36779df15b7SSanyog Kale 
36879df15b7SSanyog Kale /**
36979df15b7SSanyog Kale  * sdw_enable_disable_ports() - Enable/disable port(s) for Master and
37079df15b7SSanyog Kale  * Slave(s)
37179df15b7SSanyog Kale  *
37279df15b7SSanyog Kale  * @m_rt: Master stream runtime
37379df15b7SSanyog Kale  * @en: mode (enable/disable)
37479df15b7SSanyog Kale  */
sdw_enable_disable_ports(struct sdw_master_runtime * m_rt,bool en)37579df15b7SSanyog Kale static int sdw_enable_disable_ports(struct sdw_master_runtime *m_rt, bool en)
37679df15b7SSanyog Kale {
37779df15b7SSanyog Kale 	struct sdw_port_runtime *s_port, *m_port;
3783a0be1a6SPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt;
37979df15b7SSanyog Kale 	int ret = 0;
38079df15b7SSanyog Kale 
38179df15b7SSanyog Kale 	/* Enable/Disable Slave port(s) */
38279df15b7SSanyog Kale 	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
38379df15b7SSanyog Kale 		list_for_each_entry(s_port, &s_rt->port_list, port_node) {
38479df15b7SSanyog Kale 			ret = sdw_enable_disable_slave_ports(m_rt->bus, s_rt,
38579df15b7SSanyog Kale 							     s_port, en);
38679df15b7SSanyog Kale 			if (ret < 0)
38779df15b7SSanyog Kale 				return ret;
38879df15b7SSanyog Kale 		}
38979df15b7SSanyog Kale 	}
39079df15b7SSanyog Kale 
39179df15b7SSanyog Kale 	/* Enable/Disable Master port(s) */
39279df15b7SSanyog Kale 	list_for_each_entry(m_port, &m_rt->port_list, port_node) {
39379df15b7SSanyog Kale 		ret = sdw_enable_disable_master_ports(m_rt, m_port, en);
39479df15b7SSanyog Kale 		if (ret < 0)
39579df15b7SSanyog Kale 			return ret;
39679df15b7SSanyog Kale 	}
39779df15b7SSanyog Kale 
39879df15b7SSanyog Kale 	return 0;
39979df15b7SSanyog Kale }
40079df15b7SSanyog Kale 
sdw_do_port_prep(struct sdw_slave_runtime * s_rt,struct sdw_prepare_ch prep_ch,enum sdw_port_prep_ops cmd)40179df15b7SSanyog Kale static int sdw_do_port_prep(struct sdw_slave_runtime *s_rt,
4021fe74a5eSPierre-Louis Bossart 			    struct sdw_prepare_ch prep_ch,
4031fe74a5eSPierre-Louis Bossart 			    enum sdw_port_prep_ops cmd)
40479df15b7SSanyog Kale {
405bd29c00eSPierre-Louis Bossart 	int ret = 0;
406bd29c00eSPierre-Louis Bossart 	struct sdw_slave *slave = s_rt->slave;
40779df15b7SSanyog Kale 
408bd29c00eSPierre-Louis Bossart 	mutex_lock(&slave->sdw_dev_lock);
409bd29c00eSPierre-Louis Bossart 
410bd29c00eSPierre-Louis Bossart 	if (slave->probed) {
411bd29c00eSPierre-Louis Bossart 		struct device *dev = &slave->dev;
412bd29c00eSPierre-Louis Bossart 		struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
413bd29c00eSPierre-Louis Bossart 
414bd29c00eSPierre-Louis Bossart 		if (drv->ops && drv->ops->port_prep) {
415bd29c00eSPierre-Louis Bossart 			ret = drv->ops->port_prep(slave, &prep_ch, cmd);
416bd29c00eSPierre-Louis Bossart 			if (ret < 0)
417bd29c00eSPierre-Louis Bossart 				dev_err(dev, "Slave Port Prep cmd %d failed: %d\n",
41862f0cec3SVinod Koul 					cmd, ret);
41979df15b7SSanyog Kale 		}
42079df15b7SSanyog Kale 	}
42179df15b7SSanyog Kale 
422bd29c00eSPierre-Louis Bossart 	mutex_unlock(&slave->sdw_dev_lock);
423bd29c00eSPierre-Louis Bossart 
424bd29c00eSPierre-Louis Bossart 	return ret;
42579df15b7SSanyog Kale }
42679df15b7SSanyog Kale 
sdw_prep_deprep_slave_ports(struct sdw_bus * bus,struct sdw_slave_runtime * s_rt,struct sdw_port_runtime * p_rt,bool prep)42779df15b7SSanyog Kale static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
42879df15b7SSanyog Kale 				       struct sdw_slave_runtime *s_rt,
4291fe74a5eSPierre-Louis Bossart 				       struct sdw_port_runtime *p_rt,
4301fe74a5eSPierre-Louis Bossart 				       bool prep)
43179df15b7SSanyog Kale {
4323a0be1a6SPierre-Louis Bossart 	struct completion *port_ready;
43379df15b7SSanyog Kale 	struct sdw_dpn_prop *dpn_prop;
43479df15b7SSanyog Kale 	struct sdw_prepare_ch prep_ch;
43579df15b7SSanyog Kale 	bool intr = false;
43679df15b7SSanyog Kale 	int ret = 0, val;
43779df15b7SSanyog Kale 	u32 addr;
43879df15b7SSanyog Kale 
43979df15b7SSanyog Kale 	prep_ch.num = p_rt->num;
44079df15b7SSanyog Kale 	prep_ch.ch_mask = p_rt->ch_mask;
44179df15b7SSanyog Kale 
44279df15b7SSanyog Kale 	dpn_prop = sdw_get_slave_dpn_prop(s_rt->slave,
44379df15b7SSanyog Kale 					  s_rt->direction,
44479df15b7SSanyog Kale 					  prep_ch.num);
44579df15b7SSanyog Kale 	if (!dpn_prop) {
44679df15b7SSanyog Kale 		dev_err(bus->dev,
44717ed5befSPierre-Louis Bossart 			"Slave Port:%d properties not found\n", prep_ch.num);
44879df15b7SSanyog Kale 		return -EINVAL;
44979df15b7SSanyog Kale 	}
45079df15b7SSanyog Kale 
45179df15b7SSanyog Kale 	prep_ch.prepare = prep;
45279df15b7SSanyog Kale 
45379df15b7SSanyog Kale 	prep_ch.bank = bus->params.next_bank;
45479df15b7SSanyog Kale 
455dd87a72aSPierre-Louis Bossart 	if (dpn_prop->imp_def_interrupts || !dpn_prop->simple_ch_prep_sm ||
456dd87a72aSPierre-Louis Bossart 	    bus->params.s_data_mode != SDW_PORT_DATA_MODE_NORMAL)
45779df15b7SSanyog Kale 		intr = true;
45879df15b7SSanyog Kale 
45979df15b7SSanyog Kale 	/*
46079df15b7SSanyog Kale 	 * Enable interrupt before Port prepare.
46179df15b7SSanyog Kale 	 * For Port de-prepare, it is assumed that port
46279df15b7SSanyog Kale 	 * was prepared earlier
46379df15b7SSanyog Kale 	 */
46479df15b7SSanyog Kale 	if (prep && intr) {
46579df15b7SSanyog Kale 		ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep,
4668acbbfecSPierre-Louis Bossart 					     dpn_prop->imp_def_interrupts);
46779df15b7SSanyog Kale 		if (ret < 0)
46879df15b7SSanyog Kale 			return ret;
46979df15b7SSanyog Kale 	}
47079df15b7SSanyog Kale 
47179df15b7SSanyog Kale 	/* Inform slave about the impending port prepare */
47243f1a7f9SStefan Binding 	sdw_do_port_prep(s_rt, prep_ch, prep ? SDW_OPS_PORT_PRE_PREP : SDW_OPS_PORT_PRE_DEPREP);
47379df15b7SSanyog Kale 
47479df15b7SSanyog Kale 	/* Prepare Slave port implementing CP_SM */
47579df15b7SSanyog Kale 	if (!dpn_prop->simple_ch_prep_sm) {
47679df15b7SSanyog Kale 		addr = SDW_DPN_PREPARECTRL(p_rt->num);
47779df15b7SSanyog Kale 
47879df15b7SSanyog Kale 		if (prep)
479545c3651SCharles Keepax 			ret = sdw_write_no_pm(s_rt->slave, addr, p_rt->ch_mask);
48079df15b7SSanyog Kale 		else
481545c3651SCharles Keepax 			ret = sdw_write_no_pm(s_rt->slave, addr, 0x0);
48279df15b7SSanyog Kale 
48379df15b7SSanyog Kale 		if (ret < 0) {
48479df15b7SSanyog Kale 			dev_err(&s_rt->slave->dev,
48517ed5befSPierre-Louis Bossart 				"Slave prep_ctrl reg write failed\n");
48679df15b7SSanyog Kale 			return ret;
48779df15b7SSanyog Kale 		}
48879df15b7SSanyog Kale 
48979df15b7SSanyog Kale 		/* Wait for completion on port ready */
49079df15b7SSanyog Kale 		port_ready = &s_rt->slave->port_ready[prep_ch.num];
4913d3e88e3SRichard Fitzgerald 		wait_for_completion_timeout(port_ready,
49279df15b7SSanyog Kale 			msecs_to_jiffies(dpn_prop->ch_prep_timeout));
49379df15b7SSanyog Kale 
494545c3651SCharles Keepax 		val = sdw_read_no_pm(s_rt->slave, SDW_DPN_PREPARESTATUS(p_rt->num));
4953d3e88e3SRichard Fitzgerald 		if ((val < 0) || (val & p_rt->ch_mask)) {
4963d3e88e3SRichard Fitzgerald 			ret = (val < 0) ? val : -ETIMEDOUT;
49779df15b7SSanyog Kale 			dev_err(&s_rt->slave->dev,
4983d3e88e3SRichard Fitzgerald 				"Chn prep failed for port %d: %d\n", prep_ch.num, ret);
4993d3e88e3SRichard Fitzgerald 			return ret;
50079df15b7SSanyog Kale 		}
50179df15b7SSanyog Kale 	}
50279df15b7SSanyog Kale 
50379df15b7SSanyog Kale 	/* Inform slaves about ports prepared */
50443f1a7f9SStefan Binding 	sdw_do_port_prep(s_rt, prep_ch, prep ? SDW_OPS_PORT_POST_PREP : SDW_OPS_PORT_POST_DEPREP);
50579df15b7SSanyog Kale 
50679df15b7SSanyog Kale 	/* Disable interrupt after Port de-prepare */
50779df15b7SSanyog Kale 	if (!prep && intr)
50879df15b7SSanyog Kale 		ret = sdw_configure_dpn_intr(s_rt->slave, p_rt->num, prep,
5098acbbfecSPierre-Louis Bossart 					     dpn_prop->imp_def_interrupts);
51079df15b7SSanyog Kale 
51179df15b7SSanyog Kale 	return ret;
51279df15b7SSanyog Kale }
51379df15b7SSanyog Kale 
sdw_prep_deprep_master_ports(struct sdw_master_runtime * m_rt,struct sdw_port_runtime * p_rt,bool prep)51479df15b7SSanyog Kale static int sdw_prep_deprep_master_ports(struct sdw_master_runtime *m_rt,
5151fe74a5eSPierre-Louis Bossart 					struct sdw_port_runtime *p_rt,
5161fe74a5eSPierre-Louis Bossart 					bool prep)
51779df15b7SSanyog Kale {
51879df15b7SSanyog Kale 	struct sdw_transport_params *t_params = &p_rt->transport_params;
51979df15b7SSanyog Kale 	struct sdw_bus *bus = m_rt->bus;
52079df15b7SSanyog Kale 	const struct sdw_master_port_ops *ops = bus->port_ops;
52179df15b7SSanyog Kale 	struct sdw_prepare_ch prep_ch;
52279df15b7SSanyog Kale 	int ret = 0;
52379df15b7SSanyog Kale 
52479df15b7SSanyog Kale 	prep_ch.num = p_rt->num;
52579df15b7SSanyog Kale 	prep_ch.ch_mask = p_rt->ch_mask;
52679df15b7SSanyog Kale 	prep_ch.prepare = prep; /* Prepare/De-prepare */
52779df15b7SSanyog Kale 	prep_ch.bank = bus->params.next_bank;
52879df15b7SSanyog Kale 
52979df15b7SSanyog Kale 	/* Pre-prepare/Pre-deprepare port(s) */
53079df15b7SSanyog Kale 	if (ops->dpn_port_prep) {
53179df15b7SSanyog Kale 		ret = ops->dpn_port_prep(bus, &prep_ch);
53279df15b7SSanyog Kale 		if (ret < 0) {
53317ed5befSPierre-Louis Bossart 			dev_err(bus->dev, "Port prepare failed for port:%d\n",
53479df15b7SSanyog Kale 				t_params->port_num);
53579df15b7SSanyog Kale 			return ret;
53679df15b7SSanyog Kale 		}
53779df15b7SSanyog Kale 	}
53879df15b7SSanyog Kale 
53979df15b7SSanyog Kale 	return ret;
54079df15b7SSanyog Kale }
54179df15b7SSanyog Kale 
54279df15b7SSanyog Kale /**
54379df15b7SSanyog Kale  * sdw_prep_deprep_ports() - Prepare/De-prepare port(s) for Master(s) and
54479df15b7SSanyog Kale  * Slave(s)
54579df15b7SSanyog Kale  *
54679df15b7SSanyog Kale  * @m_rt: Master runtime handle
54779df15b7SSanyog Kale  * @prep: Prepare or De-prepare
54879df15b7SSanyog Kale  */
sdw_prep_deprep_ports(struct sdw_master_runtime * m_rt,bool prep)54979df15b7SSanyog Kale static int sdw_prep_deprep_ports(struct sdw_master_runtime *m_rt, bool prep)
55079df15b7SSanyog Kale {
5513a0be1a6SPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt;
55279df15b7SSanyog Kale 	struct sdw_port_runtime *p_rt;
55379df15b7SSanyog Kale 	int ret = 0;
55479df15b7SSanyog Kale 
55579df15b7SSanyog Kale 	/* Prepare/De-prepare Slave port(s) */
55679df15b7SSanyog Kale 	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
55779df15b7SSanyog Kale 		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
55879df15b7SSanyog Kale 			ret = sdw_prep_deprep_slave_ports(m_rt->bus, s_rt,
55979df15b7SSanyog Kale 							  p_rt, prep);
56079df15b7SSanyog Kale 			if (ret < 0)
56179df15b7SSanyog Kale 				return ret;
56279df15b7SSanyog Kale 		}
56379df15b7SSanyog Kale 	}
56479df15b7SSanyog Kale 
56579df15b7SSanyog Kale 	/* Prepare/De-prepare Master port(s) */
56679df15b7SSanyog Kale 	list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
56779df15b7SSanyog Kale 		ret = sdw_prep_deprep_master_ports(m_rt, p_rt, prep);
56879df15b7SSanyog Kale 		if (ret < 0)
56979df15b7SSanyog Kale 			return ret;
57079df15b7SSanyog Kale 	}
57179df15b7SSanyog Kale 
57279df15b7SSanyog Kale 	return ret;
57379df15b7SSanyog Kale }
57479df15b7SSanyog Kale 
57579df15b7SSanyog Kale /**
57699b8a5d6SSanyog Kale  * sdw_notify_config() - Notify bus configuration
57799b8a5d6SSanyog Kale  *
57899b8a5d6SSanyog Kale  * @m_rt: Master runtime handle
57999b8a5d6SSanyog Kale  *
58099b8a5d6SSanyog Kale  * This function notifies the Master(s) and Slave(s) of the
58199b8a5d6SSanyog Kale  * new bus configuration.
58299b8a5d6SSanyog Kale  */
sdw_notify_config(struct sdw_master_runtime * m_rt)58399b8a5d6SSanyog Kale static int sdw_notify_config(struct sdw_master_runtime *m_rt)
58499b8a5d6SSanyog Kale {
58599b8a5d6SSanyog Kale 	struct sdw_slave_runtime *s_rt;
58699b8a5d6SSanyog Kale 	struct sdw_bus *bus = m_rt->bus;
58799b8a5d6SSanyog Kale 	struct sdw_slave *slave;
588bd29c00eSPierre-Louis Bossart 	int ret;
58999b8a5d6SSanyog Kale 
59099b8a5d6SSanyog Kale 	if (bus->ops->set_bus_conf) {
59199b8a5d6SSanyog Kale 		ret = bus->ops->set_bus_conf(bus, &bus->params);
59299b8a5d6SSanyog Kale 		if (ret < 0)
59399b8a5d6SSanyog Kale 			return ret;
59499b8a5d6SSanyog Kale 	}
59599b8a5d6SSanyog Kale 
59699b8a5d6SSanyog Kale 	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
59799b8a5d6SSanyog Kale 		slave = s_rt->slave;
59899b8a5d6SSanyog Kale 
599bd29c00eSPierre-Louis Bossart 		mutex_lock(&slave->sdw_dev_lock);
600bd29c00eSPierre-Louis Bossart 
601bd29c00eSPierre-Louis Bossart 		if (slave->probed) {
602bd29c00eSPierre-Louis Bossart 			struct device *dev = &slave->dev;
603bd29c00eSPierre-Louis Bossart 			struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
604bd29c00eSPierre-Louis Bossart 
605bd29c00eSPierre-Louis Bossart 			if (drv->ops && drv->ops->bus_config) {
606bd29c00eSPierre-Louis Bossart 				ret = drv->ops->bus_config(slave, &bus->params);
60760835022SRander Wang 				if (ret < 0) {
608bd29c00eSPierre-Louis Bossart 					dev_err(dev, "Notify Slave: %d failed\n",
60999b8a5d6SSanyog Kale 						slave->dev_num);
610bd29c00eSPierre-Louis Bossart 					mutex_unlock(&slave->sdw_dev_lock);
61199b8a5d6SSanyog Kale 					return ret;
61299b8a5d6SSanyog Kale 				}
61399b8a5d6SSanyog Kale 			}
61460835022SRander Wang 		}
61599b8a5d6SSanyog Kale 
616bd29c00eSPierre-Louis Bossart 		mutex_unlock(&slave->sdw_dev_lock);
617bd29c00eSPierre-Louis Bossart 	}
618bd29c00eSPierre-Louis Bossart 
619bd29c00eSPierre-Louis Bossart 	return 0;
62099b8a5d6SSanyog Kale }
62199b8a5d6SSanyog Kale 
62299b8a5d6SSanyog Kale /**
62399b8a5d6SSanyog Kale  * sdw_program_params() - Program transport and port parameters for Master(s)
62499b8a5d6SSanyog Kale  * and Slave(s)
62599b8a5d6SSanyog Kale  *
62699b8a5d6SSanyog Kale  * @bus: SDW bus instance
627bfaa3549SRander Wang  * @prepare: true if sdw_program_params() is called by _prepare.
62899b8a5d6SSanyog Kale  */
sdw_program_params(struct sdw_bus * bus,bool prepare)629bfaa3549SRander Wang static int sdw_program_params(struct sdw_bus *bus, bool prepare)
63099b8a5d6SSanyog Kale {
6313a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
63299b8a5d6SSanyog Kale 	int ret = 0;
63399b8a5d6SSanyog Kale 
63499b8a5d6SSanyog Kale 	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
635bfaa3549SRander Wang 
636bfaa3549SRander Wang 		/*
637bfaa3549SRander Wang 		 * this loop walks through all master runtimes for a
638bfaa3549SRander Wang 		 * bus, but the ports can only be configured while
639bfaa3549SRander Wang 		 * explicitly preparing a stream or handling an
640bfaa3549SRander Wang 		 * already-prepared stream otherwise.
641bfaa3549SRander Wang 		 */
642bfaa3549SRander Wang 		if (!prepare &&
643bfaa3549SRander Wang 		    m_rt->stream->state == SDW_STREAM_CONFIGURED)
644bfaa3549SRander Wang 			continue;
645bfaa3549SRander Wang 
64699b8a5d6SSanyog Kale 		ret = sdw_program_port_params(m_rt);
64799b8a5d6SSanyog Kale 		if (ret < 0) {
64899b8a5d6SSanyog Kale 			dev_err(bus->dev,
64917ed5befSPierre-Louis Bossart 				"Program transport params failed: %d\n", ret);
65099b8a5d6SSanyog Kale 			return ret;
65199b8a5d6SSanyog Kale 		}
65299b8a5d6SSanyog Kale 
65399b8a5d6SSanyog Kale 		ret = sdw_notify_config(m_rt);
65499b8a5d6SSanyog Kale 		if (ret < 0) {
65562f0cec3SVinod Koul 			dev_err(bus->dev,
65662f0cec3SVinod Koul 				"Notify bus config failed: %d\n", ret);
65799b8a5d6SSanyog Kale 			return ret;
65899b8a5d6SSanyog Kale 		}
65999b8a5d6SSanyog Kale 
66099b8a5d6SSanyog Kale 		/* Enable port(s) on alternate bank for all active streams */
66199b8a5d6SSanyog Kale 		if (m_rt->stream->state != SDW_STREAM_ENABLED)
66299b8a5d6SSanyog Kale 			continue;
66399b8a5d6SSanyog Kale 
66499b8a5d6SSanyog Kale 		ret = sdw_enable_disable_ports(m_rt, true);
66599b8a5d6SSanyog Kale 		if (ret < 0) {
66617ed5befSPierre-Louis Bossart 			dev_err(bus->dev, "Enable channel failed: %d\n", ret);
66799b8a5d6SSanyog Kale 			return ret;
66899b8a5d6SSanyog Kale 		}
66999b8a5d6SSanyog Kale 	}
67099b8a5d6SSanyog Kale 
67199b8a5d6SSanyog Kale 	return ret;
67299b8a5d6SSanyog Kale }
67399b8a5d6SSanyog Kale 
sdw_bank_switch(struct sdw_bus * bus,int m_rt_count)674ce6e74d0SShreyas NC static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count)
67599b8a5d6SSanyog Kale {
67699b8a5d6SSanyog Kale 	int col_index, row_index;
677ce6e74d0SShreyas NC 	bool multi_link;
67899b8a5d6SSanyog Kale 	struct sdw_msg *wr_msg;
6793a0be1a6SPierre-Louis Bossart 	u8 *wbuf;
6803a0be1a6SPierre-Louis Bossart 	int ret;
68199b8a5d6SSanyog Kale 	u16 addr;
68299b8a5d6SSanyog Kale 
68399b8a5d6SSanyog Kale 	wr_msg = kzalloc(sizeof(*wr_msg), GFP_KERNEL);
68499b8a5d6SSanyog Kale 	if (!wr_msg)
68599b8a5d6SSanyog Kale 		return -ENOMEM;
68699b8a5d6SSanyog Kale 
68799b8a5d6SSanyog Kale 	wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL);
68899b8a5d6SSanyog Kale 	if (!wbuf) {
68999b8a5d6SSanyog Kale 		ret = -ENOMEM;
69099b8a5d6SSanyog Kale 		goto error_1;
69199b8a5d6SSanyog Kale 	}
69299b8a5d6SSanyog Kale 
69399b8a5d6SSanyog Kale 	/* Get row and column index to program register */
69499b8a5d6SSanyog Kale 	col_index = sdw_find_col_index(bus->params.col);
69599b8a5d6SSanyog Kale 	row_index = sdw_find_row_index(bus->params.row);
69699b8a5d6SSanyog Kale 	wbuf[0] = col_index | (row_index << 3);
69799b8a5d6SSanyog Kale 
69899b8a5d6SSanyog Kale 	if (bus->params.next_bank)
69999b8a5d6SSanyog Kale 		addr = SDW_SCP_FRAMECTRL_B1;
70099b8a5d6SSanyog Kale 	else
70199b8a5d6SSanyog Kale 		addr = SDW_SCP_FRAMECTRL_B0;
70299b8a5d6SSanyog Kale 
70399b8a5d6SSanyog Kale 	sdw_fill_msg(wr_msg, NULL, addr, 1, SDW_BROADCAST_DEV_NUM,
70499b8a5d6SSanyog Kale 		     SDW_MSG_FLAG_WRITE, wbuf);
70599b8a5d6SSanyog Kale 	wr_msg->ssp_sync = true;
70699b8a5d6SSanyog Kale 
707ce6e74d0SShreyas NC 	/*
708ce6e74d0SShreyas NC 	 * Set the multi_link flag only when both the hardware supports
709063ff4e5SPierre-Louis Bossart 	 * and hardware-based sync is required
710ce6e74d0SShreyas NC 	 */
711063ff4e5SPierre-Louis Bossart 	multi_link = bus->multi_link && (m_rt_count >= bus->hw_sync_min_links);
712ce6e74d0SShreyas NC 
713ce6e74d0SShreyas NC 	if (multi_link)
71445cb70f9SPierre-Louis Bossart 		ret = sdw_transfer_defer(bus, wr_msg);
715ce6e74d0SShreyas NC 	else
71699b8a5d6SSanyog Kale 		ret = sdw_transfer(bus, wr_msg);
717ce6e74d0SShreyas NC 
718e6645314SBard Liao 	if (ret < 0 && ret != -ENODATA) {
71917ed5befSPierre-Louis Bossart 		dev_err(bus->dev, "Slave frame_ctrl reg write failed\n");
72099b8a5d6SSanyog Kale 		goto error;
72199b8a5d6SSanyog Kale 	}
72299b8a5d6SSanyog Kale 
723ce6e74d0SShreyas NC 	if (!multi_link) {
72499b8a5d6SSanyog Kale 		kfree(wbuf);
7255ec0c872SPierre-Louis Bossart 		kfree(wr_msg);
72699b8a5d6SSanyog Kale 		bus->defer_msg.msg = NULL;
72799b8a5d6SSanyog Kale 		bus->params.curr_bank = !bus->params.curr_bank;
72899b8a5d6SSanyog Kale 		bus->params.next_bank = !bus->params.next_bank;
729ce6e74d0SShreyas NC 	}
73099b8a5d6SSanyog Kale 
73199b8a5d6SSanyog Kale 	return 0;
73299b8a5d6SSanyog Kale 
73399b8a5d6SSanyog Kale error:
73499b8a5d6SSanyog Kale 	kfree(wbuf);
73599b8a5d6SSanyog Kale error_1:
73699b8a5d6SSanyog Kale 	kfree(wr_msg);
7373fbbf214STom Rix 	bus->defer_msg.msg = NULL;
73899b8a5d6SSanyog Kale 	return ret;
73999b8a5d6SSanyog Kale }
74099b8a5d6SSanyog Kale 
741ce6e74d0SShreyas NC /**
742ce6e74d0SShreyas NC  * sdw_ml_sync_bank_switch: Multilink register bank switch
743ce6e74d0SShreyas NC  *
744ce6e74d0SShreyas NC  * @bus: SDW bus instance
745*40abc387SKrzysztof Kozlowski  * @multi_link: whether this is a multi-link stream with hardware-based sync
746ce6e74d0SShreyas NC  *
747ce6e74d0SShreyas NC  * Caller function should free the buffers on error
748ce6e74d0SShreyas NC  */
sdw_ml_sync_bank_switch(struct sdw_bus * bus,bool multi_link)749*40abc387SKrzysztof Kozlowski static int sdw_ml_sync_bank_switch(struct sdw_bus *bus, bool multi_link)
750ce6e74d0SShreyas NC {
751ce6e74d0SShreyas NC 	unsigned long time_left;
752ce6e74d0SShreyas NC 
753*40abc387SKrzysztof Kozlowski 	if (!multi_link)
754ce6e74d0SShreyas NC 		return 0;
755ce6e74d0SShreyas NC 
756ce6e74d0SShreyas NC 	/* Wait for completion of transfer */
757ce6e74d0SShreyas NC 	time_left = wait_for_completion_timeout(&bus->defer_msg.complete,
758ce6e74d0SShreyas NC 						bus->bank_switch_timeout);
759ce6e74d0SShreyas NC 
760ce6e74d0SShreyas NC 	if (!time_left) {
76117ed5befSPierre-Louis Bossart 		dev_err(bus->dev, "Controller Timed out on bank switch\n");
762ce6e74d0SShreyas NC 		return -ETIMEDOUT;
763ce6e74d0SShreyas NC 	}
764ce6e74d0SShreyas NC 
765ce6e74d0SShreyas NC 	bus->params.curr_bank = !bus->params.curr_bank;
766ce6e74d0SShreyas NC 	bus->params.next_bank = !bus->params.next_bank;
767ce6e74d0SShreyas NC 
768ce6e74d0SShreyas NC 	if (bus->defer_msg.msg) {
769ce6e74d0SShreyas NC 		kfree(bus->defer_msg.msg->buf);
770ce6e74d0SShreyas NC 		kfree(bus->defer_msg.msg);
7715ec0c872SPierre-Louis Bossart 		bus->defer_msg.msg = NULL;
772ce6e74d0SShreyas NC 	}
773ce6e74d0SShreyas NC 
774ce6e74d0SShreyas NC 	return 0;
775ce6e74d0SShreyas NC }
776ce6e74d0SShreyas NC 
do_bank_switch(struct sdw_stream_runtime * stream)77799b8a5d6SSanyog Kale static int do_bank_switch(struct sdw_stream_runtime *stream)
77899b8a5d6SSanyog Kale {
7793a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
78099b8a5d6SSanyog Kale 	const struct sdw_master_ops *ops;
7813a0be1a6SPierre-Louis Bossart 	struct sdw_bus *bus;
782ce6e74d0SShreyas NC 	bool multi_link = false;
783063ff4e5SPierre-Louis Bossart 	int m_rt_count;
78499b8a5d6SSanyog Kale 	int ret = 0;
78599b8a5d6SSanyog Kale 
786063ff4e5SPierre-Louis Bossart 	m_rt_count = stream->m_rt_count;
787063ff4e5SPierre-Louis Bossart 
78848949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
78948949722SVinod Koul 		bus = m_rt->bus;
79099b8a5d6SSanyog Kale 		ops = bus->ops;
79199b8a5d6SSanyog Kale 
792063ff4e5SPierre-Louis Bossart 		if (bus->multi_link && m_rt_count >= bus->hw_sync_min_links) {
793ce6e74d0SShreyas NC 			multi_link = true;
794ce6e74d0SShreyas NC 			mutex_lock(&bus->msg_lock);
795ce6e74d0SShreyas NC 		}
796ce6e74d0SShreyas NC 
79799b8a5d6SSanyog Kale 		/* Pre-bank switch */
79899b8a5d6SSanyog Kale 		if (ops->pre_bank_switch) {
79999b8a5d6SSanyog Kale 			ret = ops->pre_bank_switch(bus);
80099b8a5d6SSanyog Kale 			if (ret < 0) {
80148949722SVinod Koul 				dev_err(bus->dev,
80217ed5befSPierre-Louis Bossart 					"Pre bank switch op failed: %d\n", ret);
803ce6e74d0SShreyas NC 				goto msg_unlock;
80499b8a5d6SSanyog Kale 			}
80599b8a5d6SSanyog Kale 		}
80699b8a5d6SSanyog Kale 
807ce6e74d0SShreyas NC 		/*
808ce6e74d0SShreyas NC 		 * Perform Bank switch operation.
809ce6e74d0SShreyas NC 		 * For multi link cases, the actual bank switch is
810ce6e74d0SShreyas NC 		 * synchronized across all Masters and happens later as a
811ce6e74d0SShreyas NC 		 * part of post_bank_switch ops.
812ce6e74d0SShreyas NC 		 */
813063ff4e5SPierre-Louis Bossart 		ret = sdw_bank_switch(bus, m_rt_count);
81499b8a5d6SSanyog Kale 		if (ret < 0) {
81517ed5befSPierre-Louis Bossart 			dev_err(bus->dev, "Bank switch failed: %d\n", ret);
816ce6e74d0SShreyas NC 			goto error;
81799b8a5d6SSanyog Kale 		}
81848949722SVinod Koul 	}
81948949722SVinod Koul 
820ce6e74d0SShreyas NC 	/*
821ce6e74d0SShreyas NC 	 * For multi link cases, it is expected that the bank switch is
822ce6e74d0SShreyas NC 	 * triggered by the post_bank_switch for the first Master in the list
823ce6e74d0SShreyas NC 	 * and for the other Masters the post_bank_switch() should return doing
824ce6e74d0SShreyas NC 	 * nothing.
825ce6e74d0SShreyas NC 	 */
82648949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
82748949722SVinod Koul 		bus = m_rt->bus;
82848949722SVinod Koul 		ops = bus->ops;
82999b8a5d6SSanyog Kale 
83099b8a5d6SSanyog Kale 		/* Post-bank switch */
83199b8a5d6SSanyog Kale 		if (ops->post_bank_switch) {
83299b8a5d6SSanyog Kale 			ret = ops->post_bank_switch(bus);
83399b8a5d6SSanyog Kale 			if (ret < 0) {
83499b8a5d6SSanyog Kale 				dev_err(bus->dev,
83562f0cec3SVinod Koul 					"Post bank switch op failed: %d\n",
83662f0cec3SVinod Koul 					ret);
837ce6e74d0SShreyas NC 				goto error;
83899b8a5d6SSanyog Kale 			}
839063ff4e5SPierre-Louis Bossart 		} else if (multi_link) {
840ce6e74d0SShreyas NC 			dev_err(bus->dev,
84117ed5befSPierre-Louis Bossart 				"Post bank switch ops not implemented\n");
842a7ad7ce4SWang Wensheng 			ret = -EINVAL;
843ce6e74d0SShreyas NC 			goto error;
844ce6e74d0SShreyas NC 		}
845ce6e74d0SShreyas NC 
846ce6e74d0SShreyas NC 		/* Set the bank switch timeout to default, if not set */
847ce6e74d0SShreyas NC 		if (!bus->bank_switch_timeout)
848ce6e74d0SShreyas NC 			bus->bank_switch_timeout = DEFAULT_BANK_SWITCH_TIMEOUT;
849ce6e74d0SShreyas NC 
850ce6e74d0SShreyas NC 		/* Check if bank switch was successful */
851*40abc387SKrzysztof Kozlowski 		ret = sdw_ml_sync_bank_switch(bus, multi_link);
852ce6e74d0SShreyas NC 		if (ret < 0) {
853ce6e74d0SShreyas NC 			dev_err(bus->dev,
85417ed5befSPierre-Louis Bossart 				"multi link bank switch failed: %d\n", ret);
855ce6e74d0SShreyas NC 			goto error;
856ce6e74d0SShreyas NC 		}
857ce6e74d0SShreyas NC 
858063ff4e5SPierre-Louis Bossart 		if (multi_link)
859ce6e74d0SShreyas NC 			mutex_unlock(&bus->msg_lock);
860ce6e74d0SShreyas NC 	}
861ce6e74d0SShreyas NC 
862ce6e74d0SShreyas NC 	return ret;
863ce6e74d0SShreyas NC 
864ce6e74d0SShreyas NC error:
865ce6e74d0SShreyas NC 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
866ce6e74d0SShreyas NC 		bus = m_rt->bus;
8673fbbf214STom Rix 		if (bus->defer_msg.msg) {
868ce6e74d0SShreyas NC 			kfree(bus->defer_msg.msg->buf);
869ce6e74d0SShreyas NC 			kfree(bus->defer_msg.msg);
8705ec0c872SPierre-Louis Bossart 			bus->defer_msg.msg = NULL;
871ce6e74d0SShreyas NC 		}
8723fbbf214STom Rix 	}
873ce6e74d0SShreyas NC 
874ce6e74d0SShreyas NC msg_unlock:
875ce6e74d0SShreyas NC 
876ce6e74d0SShreyas NC 	if (multi_link) {
877ce6e74d0SShreyas NC 		list_for_each_entry(m_rt, &stream->master_list, stream_node) {
878ce6e74d0SShreyas NC 			bus = m_rt->bus;
879ce6e74d0SShreyas NC 			if (mutex_is_locked(&bus->msg_lock))
880ce6e74d0SShreyas NC 				mutex_unlock(&bus->msg_lock);
88199b8a5d6SSanyog Kale 		}
88248949722SVinod Koul 	}
88399b8a5d6SSanyog Kale 
88499b8a5d6SSanyog Kale 	return ret;
88599b8a5d6SSanyog Kale }
88699b8a5d6SSanyog Kale 
sdw_port_alloc(struct list_head * port_list)8876ccf3292SPierre-Louis Bossart static struct sdw_port_runtime *sdw_port_alloc(struct list_head *port_list)
8886ccf3292SPierre-Louis Bossart {
8896ccf3292SPierre-Louis Bossart 	struct sdw_port_runtime *p_rt;
8906ccf3292SPierre-Louis Bossart 
8916ccf3292SPierre-Louis Bossart 	p_rt = kzalloc(sizeof(*p_rt), GFP_KERNEL);
8926ccf3292SPierre-Louis Bossart 	if (!p_rt)
8936ccf3292SPierre-Louis Bossart 		return NULL;
8946ccf3292SPierre-Louis Bossart 
8956ccf3292SPierre-Louis Bossart 	list_add_tail(&p_rt->port_node, port_list);
8966ccf3292SPierre-Louis Bossart 
8976ccf3292SPierre-Louis Bossart 	return p_rt;
8986ccf3292SPierre-Louis Bossart }
8996ccf3292SPierre-Louis Bossart 
sdw_port_config(struct sdw_port_runtime * p_rt,struct sdw_port_config * port_config,int port_index)9006ccf3292SPierre-Louis Bossart static int sdw_port_config(struct sdw_port_runtime *p_rt,
9016ccf3292SPierre-Louis Bossart 			   struct sdw_port_config *port_config,
9026ccf3292SPierre-Louis Bossart 			   int port_index)
9036ccf3292SPierre-Louis Bossart {
9046ccf3292SPierre-Louis Bossart 	p_rt->ch_mask = port_config[port_index].ch_mask;
9056ccf3292SPierre-Louis Bossart 	p_rt->num = port_config[port_index].num;
9066ccf3292SPierre-Louis Bossart 
9076ccf3292SPierre-Louis Bossart 	/*
9086ccf3292SPierre-Louis Bossart 	 * TODO: Check port capabilities for requested configuration
9096ccf3292SPierre-Louis Bossart 	 */
9106ccf3292SPierre-Louis Bossart 
9116ccf3292SPierre-Louis Bossart 	return 0;
9126ccf3292SPierre-Louis Bossart }
9136ccf3292SPierre-Louis Bossart 
sdw_port_free(struct sdw_port_runtime * p_rt)9146ccf3292SPierre-Louis Bossart static void sdw_port_free(struct sdw_port_runtime *p_rt)
9156ccf3292SPierre-Louis Bossart {
9166ccf3292SPierre-Louis Bossart 	list_del(&p_rt->port_node);
9176ccf3292SPierre-Louis Bossart 	kfree(p_rt);
9186ccf3292SPierre-Louis Bossart }
9196ccf3292SPierre-Louis Bossart 
sdw_slave_port_allocated(struct sdw_slave_runtime * s_rt)920f3016b89SPierre-Louis Bossart static bool sdw_slave_port_allocated(struct sdw_slave_runtime *s_rt)
921f3016b89SPierre-Louis Bossart {
922f3016b89SPierre-Louis Bossart 	return !list_empty(&s_rt->port_list);
923f3016b89SPierre-Louis Bossart }
924f3016b89SPierre-Louis Bossart 
sdw_slave_port_free(struct sdw_slave * slave,struct sdw_stream_runtime * stream)925c7aa9d77SPierre-Louis Bossart static void sdw_slave_port_free(struct sdw_slave *slave,
926c7aa9d77SPierre-Louis Bossart 				struct sdw_stream_runtime *stream)
927c7aa9d77SPierre-Louis Bossart {
928c7aa9d77SPierre-Louis Bossart 	struct sdw_port_runtime *p_rt, *_p_rt;
929c7aa9d77SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
930c7aa9d77SPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt;
931c7aa9d77SPierre-Louis Bossart 
932c7aa9d77SPierre-Louis Bossart 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
933c7aa9d77SPierre-Louis Bossart 		list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
934c7aa9d77SPierre-Louis Bossart 			if (s_rt->slave != slave)
935c7aa9d77SPierre-Louis Bossart 				continue;
936c7aa9d77SPierre-Louis Bossart 
937c7aa9d77SPierre-Louis Bossart 			list_for_each_entry_safe(p_rt, _p_rt,
938c7aa9d77SPierre-Louis Bossart 						 &s_rt->port_list, port_node) {
939c7aa9d77SPierre-Louis Bossart 				sdw_port_free(p_rt);
940c7aa9d77SPierre-Louis Bossart 			}
941c7aa9d77SPierre-Louis Bossart 		}
942c7aa9d77SPierre-Louis Bossart 	}
943c7aa9d77SPierre-Louis Bossart }
944c7aa9d77SPierre-Louis Bossart 
sdw_slave_port_alloc(struct sdw_slave * slave,struct sdw_slave_runtime * s_rt,unsigned int num_config)945c7aa9d77SPierre-Louis Bossart static int sdw_slave_port_alloc(struct sdw_slave *slave,
946c7aa9d77SPierre-Louis Bossart 				struct sdw_slave_runtime *s_rt,
947c7aa9d77SPierre-Louis Bossart 				unsigned int num_config)
948c7aa9d77SPierre-Louis Bossart {
949c7aa9d77SPierre-Louis Bossart 	struct sdw_port_runtime *p_rt;
950c7aa9d77SPierre-Louis Bossart 	int i;
951c7aa9d77SPierre-Louis Bossart 
952c7aa9d77SPierre-Louis Bossart 	/* Iterate for number of ports to perform initialization */
953c7aa9d77SPierre-Louis Bossart 	for (i = 0; i < num_config; i++) {
954c7aa9d77SPierre-Louis Bossart 		p_rt = sdw_port_alloc(&s_rt->port_list);
955c7aa9d77SPierre-Louis Bossart 		if (!p_rt)
956c7aa9d77SPierre-Louis Bossart 			return -ENOMEM;
957c7aa9d77SPierre-Louis Bossart 	}
958c7aa9d77SPierre-Louis Bossart 
959c7aa9d77SPierre-Louis Bossart 	return 0;
960c7aa9d77SPierre-Louis Bossart }
961c7aa9d77SPierre-Louis Bossart 
sdw_slave_port_is_valid_range(struct device * dev,int num)962c7aa9d77SPierre-Louis Bossart static int sdw_slave_port_is_valid_range(struct device *dev, int num)
963c7aa9d77SPierre-Louis Bossart {
964c7aa9d77SPierre-Louis Bossart 	if (!SDW_VALID_PORT_RANGE(num)) {
965c7aa9d77SPierre-Louis Bossart 		dev_err(dev, "SoundWire: Invalid port number :%d\n", num);
966c7aa9d77SPierre-Louis Bossart 		return -EINVAL;
967c7aa9d77SPierre-Louis Bossart 	}
968c7aa9d77SPierre-Louis Bossart 
969c7aa9d77SPierre-Louis Bossart 	return 0;
970c7aa9d77SPierre-Louis Bossart }
971c7aa9d77SPierre-Louis Bossart 
sdw_slave_port_config(struct sdw_slave * slave,struct sdw_slave_runtime * s_rt,struct sdw_port_config * port_config)972c7aa9d77SPierre-Louis Bossart static int sdw_slave_port_config(struct sdw_slave *slave,
973c7aa9d77SPierre-Louis Bossart 				 struct sdw_slave_runtime *s_rt,
974c7aa9d77SPierre-Louis Bossart 				 struct sdw_port_config *port_config)
975c7aa9d77SPierre-Louis Bossart {
976c7aa9d77SPierre-Louis Bossart 	struct sdw_port_runtime *p_rt;
977c7aa9d77SPierre-Louis Bossart 	int ret;
978c7aa9d77SPierre-Louis Bossart 	int i;
979c7aa9d77SPierre-Louis Bossart 
980c7aa9d77SPierre-Louis Bossart 	i = 0;
981c7aa9d77SPierre-Louis Bossart 	list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
982c7aa9d77SPierre-Louis Bossart 		/*
983c7aa9d77SPierre-Louis Bossart 		 * TODO: Check valid port range as defined by DisCo/
984c7aa9d77SPierre-Louis Bossart 		 * slave
985c7aa9d77SPierre-Louis Bossart 		 */
986c7aa9d77SPierre-Louis Bossart 		ret = sdw_slave_port_is_valid_range(&slave->dev, port_config[i].num);
987c7aa9d77SPierre-Louis Bossart 		if (ret < 0)
988c7aa9d77SPierre-Louis Bossart 			return ret;
989c7aa9d77SPierre-Louis Bossart 
990c7aa9d77SPierre-Louis Bossart 		ret = sdw_port_config(p_rt, port_config, i);
991c7aa9d77SPierre-Louis Bossart 		if (ret < 0)
992c7aa9d77SPierre-Louis Bossart 			return ret;
993c7aa9d77SPierre-Louis Bossart 		i++;
994c7aa9d77SPierre-Louis Bossart 	}
995c7aa9d77SPierre-Louis Bossart 
996c7aa9d77SPierre-Louis Bossart 	return 0;
997c7aa9d77SPierre-Louis Bossart }
998c7aa9d77SPierre-Louis Bossart 
sdw_master_port_allocated(struct sdw_master_runtime * m_rt)999f3016b89SPierre-Louis Bossart static bool sdw_master_port_allocated(struct sdw_master_runtime *m_rt)
1000f3016b89SPierre-Louis Bossart {
1001f3016b89SPierre-Louis Bossart 	return !list_empty(&m_rt->port_list);
1002f3016b89SPierre-Louis Bossart }
1003f3016b89SPierre-Louis Bossart 
sdw_master_port_free(struct sdw_master_runtime * m_rt)1004c7aa9d77SPierre-Louis Bossart static void sdw_master_port_free(struct sdw_master_runtime *m_rt)
1005c7aa9d77SPierre-Louis Bossart {
1006c7aa9d77SPierre-Louis Bossart 	struct sdw_port_runtime *p_rt, *_p_rt;
1007c7aa9d77SPierre-Louis Bossart 
1008c7aa9d77SPierre-Louis Bossart 	list_for_each_entry_safe(p_rt, _p_rt, &m_rt->port_list, port_node) {
1009c7aa9d77SPierre-Louis Bossart 		sdw_port_free(p_rt);
1010c7aa9d77SPierre-Louis Bossart 	}
1011c7aa9d77SPierre-Louis Bossart }
1012c7aa9d77SPierre-Louis Bossart 
sdw_master_port_alloc(struct sdw_master_runtime * m_rt,unsigned int num_ports)1013c7aa9d77SPierre-Louis Bossart static int sdw_master_port_alloc(struct sdw_master_runtime *m_rt,
1014c7aa9d77SPierre-Louis Bossart 				 unsigned int num_ports)
1015c7aa9d77SPierre-Louis Bossart {
1016c7aa9d77SPierre-Louis Bossart 	struct sdw_port_runtime *p_rt;
1017c7aa9d77SPierre-Louis Bossart 	int i;
1018c7aa9d77SPierre-Louis Bossart 
1019c7aa9d77SPierre-Louis Bossart 	/* Iterate for number of ports to perform initialization */
1020c7aa9d77SPierre-Louis Bossart 	for (i = 0; i < num_ports; i++) {
1021c7aa9d77SPierre-Louis Bossart 		p_rt = sdw_port_alloc(&m_rt->port_list);
1022c7aa9d77SPierre-Louis Bossart 		if (!p_rt)
1023c7aa9d77SPierre-Louis Bossart 			return -ENOMEM;
1024c7aa9d77SPierre-Louis Bossart 	}
1025c7aa9d77SPierre-Louis Bossart 
1026c7aa9d77SPierre-Louis Bossart 	return 0;
1027c7aa9d77SPierre-Louis Bossart }
1028c7aa9d77SPierre-Louis Bossart 
sdw_master_port_config(struct sdw_master_runtime * m_rt,struct sdw_port_config * port_config)1029c7aa9d77SPierre-Louis Bossart static int sdw_master_port_config(struct sdw_master_runtime *m_rt,
1030c7aa9d77SPierre-Louis Bossart 				  struct sdw_port_config *port_config)
1031c7aa9d77SPierre-Louis Bossart {
1032c7aa9d77SPierre-Louis Bossart 	struct sdw_port_runtime *p_rt;
1033c7aa9d77SPierre-Louis Bossart 	int ret;
1034c7aa9d77SPierre-Louis Bossart 	int i;
1035c7aa9d77SPierre-Louis Bossart 
1036c7aa9d77SPierre-Louis Bossart 	i = 0;
1037c7aa9d77SPierre-Louis Bossart 	list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
1038c7aa9d77SPierre-Louis Bossart 		ret = sdw_port_config(p_rt, port_config, i);
1039c7aa9d77SPierre-Louis Bossart 		if (ret < 0)
1040c7aa9d77SPierre-Louis Bossart 			return ret;
1041c7aa9d77SPierre-Louis Bossart 		i++;
1042c7aa9d77SPierre-Louis Bossart 	}
1043c7aa9d77SPierre-Louis Bossart 
1044c7aa9d77SPierre-Louis Bossart 	return 0;
1045c7aa9d77SPierre-Louis Bossart }
1046c7aa9d77SPierre-Louis Bossart 
104799b8a5d6SSanyog Kale /**
1048edd5cf99SPierre-Louis Bossart  * sdw_slave_rt_alloc() - Allocate a Slave runtime handle.
1049bf75ba4bSPierre-Louis Bossart  *
1050bf75ba4bSPierre-Louis Bossart  * @slave: Slave handle
105142aad41eSPierre-Louis Bossart  * @m_rt: Master runtime handle
1052bf75ba4bSPierre-Louis Bossart  *
1053bf75ba4bSPierre-Louis Bossart  * This function is to be called with bus_lock held.
1054bf75ba4bSPierre-Louis Bossart  */
1055bf75ba4bSPierre-Louis Bossart static struct sdw_slave_runtime
sdw_slave_rt_alloc(struct sdw_slave * slave,struct sdw_master_runtime * m_rt)105642aad41eSPierre-Louis Bossart *sdw_slave_rt_alloc(struct sdw_slave *slave,
105742aad41eSPierre-Louis Bossart 		    struct sdw_master_runtime *m_rt)
1058bf75ba4bSPierre-Louis Bossart {
1059bf75ba4bSPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt;
1060bf75ba4bSPierre-Louis Bossart 
1061bf75ba4bSPierre-Louis Bossart 	s_rt = kzalloc(sizeof(*s_rt), GFP_KERNEL);
1062bf75ba4bSPierre-Louis Bossart 	if (!s_rt)
1063bf75ba4bSPierre-Louis Bossart 		return NULL;
1064bf75ba4bSPierre-Louis Bossart 
1065bf75ba4bSPierre-Louis Bossart 	INIT_LIST_HEAD(&s_rt->port_list);
1066bf75ba4bSPierre-Louis Bossart 	s_rt->slave = slave;
1067bf75ba4bSPierre-Louis Bossart 
106842aad41eSPierre-Louis Bossart 	list_add_tail(&s_rt->m_rt_node, &m_rt->slave_rt_list);
106942aad41eSPierre-Louis Bossart 
1070bf75ba4bSPierre-Louis Bossart 	return s_rt;
1071bf75ba4bSPierre-Louis Bossart }
1072bf75ba4bSPierre-Louis Bossart 
1073edd5cf99SPierre-Louis Bossart /**
1074edd5cf99SPierre-Louis Bossart  * sdw_slave_rt_config() - Configure a Slave runtime handle.
1075edd5cf99SPierre-Louis Bossart  *
1076edd5cf99SPierre-Louis Bossart  * @s_rt: Slave runtime handle
1077edd5cf99SPierre-Louis Bossart  * @stream_config: Stream configuration
1078edd5cf99SPierre-Louis Bossart  *
1079edd5cf99SPierre-Louis Bossart  * This function is to be called with bus_lock held.
1080edd5cf99SPierre-Louis Bossart  */
sdw_slave_rt_config(struct sdw_slave_runtime * s_rt,struct sdw_stream_config * stream_config)1081edd5cf99SPierre-Louis Bossart static int sdw_slave_rt_config(struct sdw_slave_runtime *s_rt,
1082edd5cf99SPierre-Louis Bossart 			       struct sdw_stream_config *stream_config)
1083edd5cf99SPierre-Louis Bossart {
1084edd5cf99SPierre-Louis Bossart 	s_rt->ch_count = stream_config->ch_count;
1085edd5cf99SPierre-Louis Bossart 	s_rt->direction = stream_config->direction;
1086edd5cf99SPierre-Louis Bossart 
1087edd5cf99SPierre-Louis Bossart 	return 0;
1088edd5cf99SPierre-Louis Bossart }
1089edd5cf99SPierre-Louis Bossart 
sdw_slave_rt_find(struct sdw_slave * slave,struct sdw_stream_runtime * stream)10905e1df543SPierre-Louis Bossart static struct sdw_slave_runtime *sdw_slave_rt_find(struct sdw_slave *slave,
10915e1df543SPierre-Louis Bossart 						   struct sdw_stream_runtime *stream)
10925e1df543SPierre-Louis Bossart {
10935e1df543SPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt, *_s_rt;
10945e1df543SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
10955e1df543SPierre-Louis Bossart 
10965e1df543SPierre-Louis Bossart 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
10975e1df543SPierre-Louis Bossart 		/* Retrieve Slave runtime handle */
10985e1df543SPierre-Louis Bossart 		list_for_each_entry_safe(s_rt, _s_rt,
10995e1df543SPierre-Louis Bossart 					 &m_rt->slave_rt_list, m_rt_node) {
11005e1df543SPierre-Louis Bossart 			if (s_rt->slave == slave)
11015e1df543SPierre-Louis Bossart 				return s_rt;
11025e1df543SPierre-Louis Bossart 		}
11035e1df543SPierre-Louis Bossart 	}
11045e1df543SPierre-Louis Bossart 	return NULL;
11055e1df543SPierre-Louis Bossart }
11065e1df543SPierre-Louis Bossart 
110700ce0d2aSPierre-Louis Bossart /**
110800ce0d2aSPierre-Louis Bossart  * sdw_slave_rt_free() - Free Slave(s) runtime handle
110900ce0d2aSPierre-Louis Bossart  *
111000ce0d2aSPierre-Louis Bossart  * @slave: Slave handle.
111100ce0d2aSPierre-Louis Bossart  * @stream: Stream runtime handle.
111200ce0d2aSPierre-Louis Bossart  *
111300ce0d2aSPierre-Louis Bossart  * This function is to be called with bus_lock held.
111400ce0d2aSPierre-Louis Bossart  */
sdw_slave_rt_free(struct sdw_slave * slave,struct sdw_stream_runtime * stream)111500ce0d2aSPierre-Louis Bossart static void sdw_slave_rt_free(struct sdw_slave *slave,
111600ce0d2aSPierre-Louis Bossart 			      struct sdw_stream_runtime *stream)
111700ce0d2aSPierre-Louis Bossart {
11185e1df543SPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt;
111900ce0d2aSPierre-Louis Bossart 
11205e1df543SPierre-Louis Bossart 	s_rt = sdw_slave_rt_find(slave, stream);
11215e1df543SPierre-Louis Bossart 	if (s_rt) {
112200ce0d2aSPierre-Louis Bossart 		list_del(&s_rt->m_rt_node);
112300ce0d2aSPierre-Louis Bossart 		kfree(s_rt);
112400ce0d2aSPierre-Louis Bossart 	}
112500ce0d2aSPierre-Louis Bossart }
112600ce0d2aSPierre-Louis Bossart 
112748949722SVinod Koul static struct sdw_master_runtime
sdw_master_rt_find(struct sdw_bus * bus,struct sdw_stream_runtime * stream)1128bb10659aSPierre-Louis Bossart *sdw_master_rt_find(struct sdw_bus *bus,
112948949722SVinod Koul 		    struct sdw_stream_runtime *stream)
113048949722SVinod Koul {
11313a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
113248949722SVinod Koul 
113348949722SVinod Koul 	/* Retrieve Bus handle if already available */
113448949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
113548949722SVinod Koul 		if (m_rt->bus == bus)
113648949722SVinod Koul 			return m_rt;
113748949722SVinod Koul 	}
113848949722SVinod Koul 
113948949722SVinod Koul 	return NULL;
114048949722SVinod Koul }
114148949722SVinod Koul 
114289e59053SSanyog Kale /**
1143bb10659aSPierre-Louis Bossart  * sdw_master_rt_alloc() - Allocates a Master runtime handle
114489e59053SSanyog Kale  *
114589e59053SSanyog Kale  * @bus: SDW bus instance
114689e59053SSanyog Kale  * @stream: Stream runtime handle.
114789e59053SSanyog Kale  *
114889e59053SSanyog Kale  * This function is to be called with bus_lock held.
114989e59053SSanyog Kale  */
115089e59053SSanyog Kale static struct sdw_master_runtime
sdw_master_rt_alloc(struct sdw_bus * bus,struct sdw_stream_runtime * stream)1151bb10659aSPierre-Louis Bossart *sdw_master_rt_alloc(struct sdw_bus *bus,
115289e59053SSanyog Kale 		     struct sdw_stream_runtime *stream)
115389e59053SSanyog Kale {
1154a4857d1aSRichard Fitzgerald 	struct sdw_master_runtime *m_rt, *walk_m_rt;
1155a4857d1aSRichard Fitzgerald 	struct list_head *insert_after;
115689e59053SSanyog Kale 
115789e59053SSanyog Kale 	m_rt = kzalloc(sizeof(*m_rt), GFP_KERNEL);
115889e59053SSanyog Kale 	if (!m_rt)
115989e59053SSanyog Kale 		return NULL;
116089e59053SSanyog Kale 
116189e59053SSanyog Kale 	/* Initialization of Master runtime handle */
1162bbe7379dSSanyog Kale 	INIT_LIST_HEAD(&m_rt->port_list);
116389e59053SSanyog Kale 	INIT_LIST_HEAD(&m_rt->slave_rt_list);
1164a4857d1aSRichard Fitzgerald 
1165a4857d1aSRichard Fitzgerald 	/*
1166a4857d1aSRichard Fitzgerald 	 * Add in order of bus id so that when taking the bus_lock
1167a4857d1aSRichard Fitzgerald 	 * of multiple buses they will always be taken in the same
1168a4857d1aSRichard Fitzgerald 	 * order to prevent a mutex deadlock.
1169a4857d1aSRichard Fitzgerald 	 */
1170a4857d1aSRichard Fitzgerald 	insert_after = &stream->master_list;
1171a4857d1aSRichard Fitzgerald 	list_for_each_entry_reverse(walk_m_rt, &stream->master_list, stream_node) {
1172a4857d1aSRichard Fitzgerald 		if (walk_m_rt->bus->id < bus->id) {
1173a4857d1aSRichard Fitzgerald 			insert_after = &walk_m_rt->stream_node;
1174a4857d1aSRichard Fitzgerald 			break;
1175a4857d1aSRichard Fitzgerald 		}
1176a4857d1aSRichard Fitzgerald 	}
1177a4857d1aSRichard Fitzgerald 	list_add(&m_rt->stream_node, insert_after);
117889e59053SSanyog Kale 
117989e59053SSanyog Kale 	list_add_tail(&m_rt->bus_node, &bus->m_rt_list);
118089e59053SSanyog Kale 
118189e59053SSanyog Kale 	m_rt->bus = bus;
118289e59053SSanyog Kale 	m_rt->stream = stream;
118389e59053SSanyog Kale 
118489e59053SSanyog Kale 	return m_rt;
118589e59053SSanyog Kale }
118689e59053SSanyog Kale 
118789e59053SSanyog Kale /**
1188bb10659aSPierre-Louis Bossart  * sdw_master_rt_config() - Configure Master runtime handle
1189bb10659aSPierre-Louis Bossart  *
1190bb10659aSPierre-Louis Bossart  * @m_rt: Master runtime handle
1191bb10659aSPierre-Louis Bossart  * @stream_config: Stream configuration
1192bb10659aSPierre-Louis Bossart  *
1193bb10659aSPierre-Louis Bossart  * This function is to be called with bus_lock held.
1194bb10659aSPierre-Louis Bossart  */
1195bb10659aSPierre-Louis Bossart 
sdw_master_rt_config(struct sdw_master_runtime * m_rt,struct sdw_stream_config * stream_config)1196bb10659aSPierre-Louis Bossart static int sdw_master_rt_config(struct sdw_master_runtime *m_rt,
1197bb10659aSPierre-Louis Bossart 				struct sdw_stream_config *stream_config)
1198bb10659aSPierre-Louis Bossart {
1199bb10659aSPierre-Louis Bossart 	m_rt->ch_count = stream_config->ch_count;
1200bb10659aSPierre-Louis Bossart 	m_rt->direction = stream_config->direction;
1201bb10659aSPierre-Louis Bossart 
1202bb10659aSPierre-Louis Bossart 	return 0;
1203bb10659aSPierre-Louis Bossart }
1204bb10659aSPierre-Louis Bossart 
1205bb10659aSPierre-Louis Bossart /**
120600ce0d2aSPierre-Louis Bossart  * sdw_master_rt_free() - Free Master runtime handle
120789e59053SSanyog Kale  *
120848949722SVinod Koul  * @m_rt: Master runtime node
120989e59053SSanyog Kale  * @stream: Stream runtime handle.
121089e59053SSanyog Kale  *
121189e59053SSanyog Kale  * This function is to be called with bus_lock held
121289e59053SSanyog Kale  * It frees the Master runtime handle and associated Slave(s) runtime
121300ce0d2aSPierre-Louis Bossart  * handle. If this is called first then sdw_slave_rt_free() will have
121489e59053SSanyog Kale  * no effect as Slave(s) runtime handle would already be freed up.
121589e59053SSanyog Kale  */
sdw_master_rt_free(struct sdw_master_runtime * m_rt,struct sdw_stream_runtime * stream)121600ce0d2aSPierre-Louis Bossart static void sdw_master_rt_free(struct sdw_master_runtime *m_rt,
121748949722SVinod Koul 			       struct sdw_stream_runtime *stream)
121889e59053SSanyog Kale {
121989e59053SSanyog Kale 	struct sdw_slave_runtime *s_rt, *_s_rt;
122089e59053SSanyog Kale 
12218d6ccf5cSSanyog Kale 	list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) {
1222c7aa9d77SPierre-Louis Bossart 		sdw_slave_port_free(s_rt->slave, stream);
122300ce0d2aSPierre-Louis Bossart 		sdw_slave_rt_free(s_rt->slave, stream);
12248d6ccf5cSSanyog Kale 	}
122589e59053SSanyog Kale 
122648949722SVinod Koul 	list_del(&m_rt->stream_node);
122789e59053SSanyog Kale 	list_del(&m_rt->bus_node);
122848949722SVinod Koul 	kfree(m_rt);
122989e59053SSanyog Kale }
123089e59053SSanyog Kale 
123189e59053SSanyog Kale /**
123289e59053SSanyog Kale  * sdw_config_stream() - Configure the allocated stream
123389e59053SSanyog Kale  *
123489e59053SSanyog Kale  * @dev: SDW device
123589e59053SSanyog Kale  * @stream: SoundWire stream
123689e59053SSanyog Kale  * @stream_config: Stream configuration for audio stream
123789e59053SSanyog Kale  * @is_slave: is API called from Slave or Master
123889e59053SSanyog Kale  *
123989e59053SSanyog Kale  * This function is to be called with bus_lock held.
124089e59053SSanyog Kale  */
sdw_config_stream(struct device * dev,struct sdw_stream_runtime * stream,struct sdw_stream_config * stream_config,bool is_slave)124189e59053SSanyog Kale static int sdw_config_stream(struct device *dev,
124289e59053SSanyog Kale 			     struct sdw_stream_runtime *stream,
12431fe74a5eSPierre-Louis Bossart 			     struct sdw_stream_config *stream_config,
12441fe74a5eSPierre-Louis Bossart 			     bool is_slave)
124589e59053SSanyog Kale {
124689e59053SSanyog Kale 	/*
124789e59053SSanyog Kale 	 * Update the stream rate, channel and bps based on data
124889e59053SSanyog Kale 	 * source. For more than one data source (multilink),
124989e59053SSanyog Kale 	 * match the rate, bps, stream type and increment number of channels.
125089e59053SSanyog Kale 	 *
125189e59053SSanyog Kale 	 * If rate/bps is zero, it means the values are not set, so skip
125289e59053SSanyog Kale 	 * comparison and allow the value to be set and stored in stream
125389e59053SSanyog Kale 	 */
125489e59053SSanyog Kale 	if (stream->params.rate &&
125589e59053SSanyog Kale 	    stream->params.rate != stream_config->frame_rate) {
125617ed5befSPierre-Louis Bossart 		dev_err(dev, "rate not matching, stream:%s\n", stream->name);
125789e59053SSanyog Kale 		return -EINVAL;
125889e59053SSanyog Kale 	}
125989e59053SSanyog Kale 
126089e59053SSanyog Kale 	if (stream->params.bps &&
126189e59053SSanyog Kale 	    stream->params.bps != stream_config->bps) {
126217ed5befSPierre-Louis Bossart 		dev_err(dev, "bps not matching, stream:%s\n", stream->name);
126389e59053SSanyog Kale 		return -EINVAL;
126489e59053SSanyog Kale 	}
126589e59053SSanyog Kale 
126689e59053SSanyog Kale 	stream->type = stream_config->type;
126789e59053SSanyog Kale 	stream->params.rate = stream_config->frame_rate;
126889e59053SSanyog Kale 	stream->params.bps = stream_config->bps;
126989e59053SSanyog Kale 
127089e59053SSanyog Kale 	/* TODO: Update this check during Device-device support */
127189e59053SSanyog Kale 	if (is_slave)
127289e59053SSanyog Kale 		stream->params.ch_count += stream_config->ch_count;
127389e59053SSanyog Kale 
127489e59053SSanyog Kale 	return 0;
127589e59053SSanyog Kale }
127689e59053SSanyog Kale 
127789e59053SSanyog Kale /**
1278f8101c74SSanyog Kale  * sdw_get_slave_dpn_prop() - Get Slave port capabilities
1279f8101c74SSanyog Kale  *
1280f8101c74SSanyog Kale  * @slave: Slave handle
1281f8101c74SSanyog Kale  * @direction: Data direction.
1282f8101c74SSanyog Kale  * @port_num: Port number
1283f8101c74SSanyog Kale  */
sdw_get_slave_dpn_prop(struct sdw_slave * slave,enum sdw_data_direction direction,unsigned int port_num)1284f8101c74SSanyog Kale struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave,
1285f8101c74SSanyog Kale 					    enum sdw_data_direction direction,
1286f8101c74SSanyog Kale 					    unsigned int port_num)
1287f8101c74SSanyog Kale {
1288f8101c74SSanyog Kale 	struct sdw_dpn_prop *dpn_prop;
1289f8101c74SSanyog Kale 	u8 num_ports;
1290f8101c74SSanyog Kale 	int i;
1291f8101c74SSanyog Kale 
1292f8101c74SSanyog Kale 	if (direction == SDW_DATA_DIR_TX) {
1293f8101c74SSanyog Kale 		num_ports = hweight32(slave->prop.source_ports);
1294f8101c74SSanyog Kale 		dpn_prop = slave->prop.src_dpn_prop;
1295f8101c74SSanyog Kale 	} else {
1296f8101c74SSanyog Kale 		num_ports = hweight32(slave->prop.sink_ports);
1297f8101c74SSanyog Kale 		dpn_prop = slave->prop.sink_dpn_prop;
1298f8101c74SSanyog Kale 	}
1299f8101c74SSanyog Kale 
1300f8101c74SSanyog Kale 	for (i = 0; i < num_ports; i++) {
130103ecad90SSrinivas Kandagatla 		if (dpn_prop[i].num == port_num)
1302f8101c74SSanyog Kale 			return &dpn_prop[i];
1303f8101c74SSanyog Kale 	}
1304f8101c74SSanyog Kale 
1305f8101c74SSanyog Kale 	return NULL;
1306f8101c74SSanyog Kale }
13075c3eb9f7SSanyog Kale 
13080c4a1049SSanyog Kale /**
13090c4a1049SSanyog Kale  * sdw_acquire_bus_lock: Acquire bus lock for all Master runtime(s)
13100c4a1049SSanyog Kale  *
13110c4a1049SSanyog Kale  * @stream: SoundWire stream
13120c4a1049SSanyog Kale  *
13130c4a1049SSanyog Kale  * Acquire bus_lock for each of the master runtime(m_rt) part of this
13140c4a1049SSanyog Kale  * stream to reconfigure the bus.
13150c4a1049SSanyog Kale  * NOTE: This function is called from SoundWire stream ops and is
13160c4a1049SSanyog Kale  * expected that a global lock is held before acquiring bus_lock.
13170c4a1049SSanyog Kale  */
sdw_acquire_bus_lock(struct sdw_stream_runtime * stream)13180c4a1049SSanyog Kale static void sdw_acquire_bus_lock(struct sdw_stream_runtime *stream)
13190c4a1049SSanyog Kale {
13203a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
132153e0a304SPierre-Louis Bossart 	struct sdw_bus *bus;
13220c4a1049SSanyog Kale 
13230c4a1049SSanyog Kale 	/* Iterate for all Master(s) in Master list */
13240c4a1049SSanyog Kale 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
13250c4a1049SSanyog Kale 		bus = m_rt->bus;
13260c4a1049SSanyog Kale 
13270c4a1049SSanyog Kale 		mutex_lock(&bus->bus_lock);
13280c4a1049SSanyog Kale 	}
13290c4a1049SSanyog Kale }
13300c4a1049SSanyog Kale 
13310c4a1049SSanyog Kale /**
13320c4a1049SSanyog Kale  * sdw_release_bus_lock: Release bus lock for all Master runtime(s)
13330c4a1049SSanyog Kale  *
13340c4a1049SSanyog Kale  * @stream: SoundWire stream
13350c4a1049SSanyog Kale  *
13360c4a1049SSanyog Kale  * Release the previously held bus_lock after reconfiguring the bus.
133748949722SVinod Koul  * NOTE: This function is called from SoundWire stream ops and is
133848949722SVinod Koul  * expected that a global lock is held before releasing bus_lock.
13390c4a1049SSanyog Kale  */
sdw_release_bus_lock(struct sdw_stream_runtime * stream)13400c4a1049SSanyog Kale static void sdw_release_bus_lock(struct sdw_stream_runtime *stream)
13410c4a1049SSanyog Kale {
13425920a29dSPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
134353e0a304SPierre-Louis Bossart 	struct sdw_bus *bus;
13440c4a1049SSanyog Kale 
13450c4a1049SSanyog Kale 	/* Iterate for all Master(s) in Master list */
13460c4a1049SSanyog Kale 	list_for_each_entry_reverse(m_rt, &stream->master_list, stream_node) {
13470c4a1049SSanyog Kale 		bus = m_rt->bus;
13480c4a1049SSanyog Kale 		mutex_unlock(&bus->bus_lock);
13490c4a1049SSanyog Kale 	}
13500c4a1049SSanyog Kale }
13510c4a1049SSanyog Kale 
_sdw_prepare_stream(struct sdw_stream_runtime * stream,bool update_params)1352c7a8f049SPierre-Louis Bossart static int _sdw_prepare_stream(struct sdw_stream_runtime *stream,
1353c7a8f049SPierre-Louis Bossart 			       bool update_params)
13545c3eb9f7SSanyog Kale {
13553a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
13562b2da409SCharles Keepax 	struct sdw_bus *bus;
13573a0be1a6SPierre-Louis Bossart 	struct sdw_master_prop *prop;
13585c3eb9f7SSanyog Kale 	struct sdw_bus_params params;
13595c3eb9f7SSanyog Kale 	int ret;
13605c3eb9f7SSanyog Kale 
136148949722SVinod Koul 	/* Prepare  Master(s) and Slave(s) port(s) associated with stream */
136248949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
136348949722SVinod Koul 		bus = m_rt->bus;
13645c3eb9f7SSanyog Kale 		prop = &bus->prop;
13655c3eb9f7SSanyog Kale 		memcpy(&params, &bus->params, sizeof(params));
13665c3eb9f7SSanyog Kale 
13675c3eb9f7SSanyog Kale 		/* TODO: Support Asynchronous mode */
13683424305bSPierre-Louis Bossart 		if ((prop->max_clk_freq % stream->params.rate) != 0) {
136917ed5befSPierre-Louis Bossart 			dev_err(bus->dev, "Async mode not supported\n");
13705c3eb9f7SSanyog Kale 			return -EINVAL;
13715c3eb9f7SSanyog Kale 		}
13725c3eb9f7SSanyog Kale 
13730cbcced9SCharles Keepax 		if (update_params) {
13745c3eb9f7SSanyog Kale 			/* Increment cumulative bus bandwidth */
13755c3eb9f7SSanyog Kale 			/* TODO: Update this during Device-Device support */
13765c3eb9f7SSanyog Kale 			bus->params.bandwidth += m_rt->stream->params.rate *
13775c3eb9f7SSanyog Kale 				m_rt->ch_count * m_rt->stream->params.bps;
13785c3eb9f7SSanyog Kale 
1379c7578c1dSVinod Koul 			/* Compute params */
1380c7578c1dSVinod Koul 			if (bus->compute_params) {
1381c7578c1dSVinod Koul 				ret = bus->compute_params(bus);
1382c7578c1dSVinod Koul 				if (ret < 0) {
13836122d3beSPierre-Louis Bossart 					dev_err(bus->dev, "Compute params failed: %d\n",
1384c7578c1dSVinod Koul 						ret);
1385acdae463SShuming Fan 					goto restore_params;
1386c7578c1dSVinod Koul 				}
1387c7578c1dSVinod Koul 			}
13880cbcced9SCharles Keepax 		}
1389c7578c1dSVinod Koul 
13905c3eb9f7SSanyog Kale 		/* Program params */
1391bfaa3549SRander Wang 		ret = sdw_program_params(bus, true);
13925c3eb9f7SSanyog Kale 		if (ret < 0) {
139317ed5befSPierre-Louis Bossart 			dev_err(bus->dev, "Program params failed: %d\n", ret);
13945c3eb9f7SSanyog Kale 			goto restore_params;
13955c3eb9f7SSanyog Kale 		}
139648949722SVinod Koul 	}
139748949722SVinod Koul 
13985c3eb9f7SSanyog Kale 	ret = do_bank_switch(stream);
13995c3eb9f7SSanyog Kale 	if (ret < 0) {
140068d9bfb6SPierre-Louis Bossart 		pr_err("%s: do_bank_switch failed: %d\n", __func__, ret);
14015c3eb9f7SSanyog Kale 		goto restore_params;
14025c3eb9f7SSanyog Kale 	}
14035c3eb9f7SSanyog Kale 
140448949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
140548949722SVinod Koul 		bus = m_rt->bus;
140648949722SVinod Koul 
14075c3eb9f7SSanyog Kale 		/* Prepare port(s) on the new clock configuration */
14085c3eb9f7SSanyog Kale 		ret = sdw_prep_deprep_ports(m_rt, true);
14095c3eb9f7SSanyog Kale 		if (ret < 0) {
141017ed5befSPierre-Louis Bossart 			dev_err(bus->dev, "Prepare port(s) failed ret = %d\n",
14115c3eb9f7SSanyog Kale 				ret);
14125c3eb9f7SSanyog Kale 			return ret;
14135c3eb9f7SSanyog Kale 		}
141448949722SVinod Koul 	}
14155c3eb9f7SSanyog Kale 
14165c3eb9f7SSanyog Kale 	stream->state = SDW_STREAM_PREPARED;
14175c3eb9f7SSanyog Kale 
14185c3eb9f7SSanyog Kale 	return ret;
14195c3eb9f7SSanyog Kale 
14205c3eb9f7SSanyog Kale restore_params:
14215c3eb9f7SSanyog Kale 	memcpy(&bus->params, &params, sizeof(params));
14225c3eb9f7SSanyog Kale 	return ret;
14235c3eb9f7SSanyog Kale }
14245c3eb9f7SSanyog Kale 
14255c3eb9f7SSanyog Kale /**
14265c3eb9f7SSanyog Kale  * sdw_prepare_stream() - Prepare SoundWire stream
14275c3eb9f7SSanyog Kale  *
14285c3eb9f7SSanyog Kale  * @stream: Soundwire stream
14295c3eb9f7SSanyog Kale  *
143034962fb8SMauro Carvalho Chehab  * Documentation/driver-api/soundwire/stream.rst explains this API in detail
14315c3eb9f7SSanyog Kale  */
sdw_prepare_stream(struct sdw_stream_runtime * stream)14325c3eb9f7SSanyog Kale int sdw_prepare_stream(struct sdw_stream_runtime *stream)
14335c3eb9f7SSanyog Kale {
1434c7a8f049SPierre-Louis Bossart 	bool update_params = true;
1435c32464c9SBard Liao 	int ret;
14365c3eb9f7SSanyog Kale 
14375c3eb9f7SSanyog Kale 	if (!stream) {
143817ed5befSPierre-Louis Bossart 		pr_err("SoundWire: Handle not found for stream\n");
14395c3eb9f7SSanyog Kale 		return -EINVAL;
14405c3eb9f7SSanyog Kale 	}
14415c3eb9f7SSanyog Kale 
144248949722SVinod Koul 	sdw_acquire_bus_lock(stream);
14435c3eb9f7SSanyog Kale 
1444c32464c9SBard Liao 	if (stream->state == SDW_STREAM_PREPARED) {
1445c32464c9SBard Liao 		ret = 0;
1446c32464c9SBard Liao 		goto state_err;
1447c32464c9SBard Liao 	}
1448c32464c9SBard Liao 
144959528807SPierre-Louis Bossart 	if (stream->state != SDW_STREAM_CONFIGURED &&
145059528807SPierre-Louis Bossart 	    stream->state != SDW_STREAM_DEPREPARED &&
145159528807SPierre-Louis Bossart 	    stream->state != SDW_STREAM_DISABLED) {
145259528807SPierre-Louis Bossart 		pr_err("%s: %s: inconsistent state state %d\n",
145359528807SPierre-Louis Bossart 		       __func__, stream->name, stream->state);
145459528807SPierre-Louis Bossart 		ret = -EINVAL;
145559528807SPierre-Louis Bossart 		goto state_err;
145659528807SPierre-Louis Bossart 	}
145759528807SPierre-Louis Bossart 
1458c7a8f049SPierre-Louis Bossart 	/*
1459c7a8f049SPierre-Louis Bossart 	 * when the stream is DISABLED, this means sdw_prepare_stream()
1460c7a8f049SPierre-Louis Bossart 	 * is called as a result of an underflow or a resume operation.
1461c7a8f049SPierre-Louis Bossart 	 * In this case, the bus parameters shall not be recomputed, but
1462c7a8f049SPierre-Louis Bossart 	 * still need to be re-applied
1463c7a8f049SPierre-Louis Bossart 	 */
1464c7a8f049SPierre-Louis Bossart 	if (stream->state == SDW_STREAM_DISABLED)
1465c7a8f049SPierre-Louis Bossart 		update_params = false;
1466c7a8f049SPierre-Louis Bossart 
1467c7a8f049SPierre-Louis Bossart 	ret = _sdw_prepare_stream(stream, update_params);
14685c3eb9f7SSanyog Kale 
146959528807SPierre-Louis Bossart state_err:
147048949722SVinod Koul 	sdw_release_bus_lock(stream);
14715c3eb9f7SSanyog Kale 	return ret;
14725c3eb9f7SSanyog Kale }
14735c3eb9f7SSanyog Kale EXPORT_SYMBOL(sdw_prepare_stream);
14745c3eb9f7SSanyog Kale 
_sdw_enable_stream(struct sdw_stream_runtime * stream)14755c3eb9f7SSanyog Kale static int _sdw_enable_stream(struct sdw_stream_runtime *stream)
14765c3eb9f7SSanyog Kale {
14773a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
14782b2da409SCharles Keepax 	struct sdw_bus *bus;
14795c3eb9f7SSanyog Kale 	int ret;
14805c3eb9f7SSanyog Kale 
148148949722SVinod Koul 	/* Enable Master(s) and Slave(s) port(s) associated with stream */
148248949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
148348949722SVinod Koul 		bus = m_rt->bus;
148448949722SVinod Koul 
14855c3eb9f7SSanyog Kale 		/* Program params */
1486bfaa3549SRander Wang 		ret = sdw_program_params(bus, false);
14875c3eb9f7SSanyog Kale 		if (ret < 0) {
148868d9bfb6SPierre-Louis Bossart 			dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret);
14895c3eb9f7SSanyog Kale 			return ret;
14905c3eb9f7SSanyog Kale 		}
14915c3eb9f7SSanyog Kale 
14925c3eb9f7SSanyog Kale 		/* Enable port(s) */
14935c3eb9f7SSanyog Kale 		ret = sdw_enable_disable_ports(m_rt, true);
14945c3eb9f7SSanyog Kale 		if (ret < 0) {
149562f0cec3SVinod Koul 			dev_err(bus->dev,
149662f0cec3SVinod Koul 				"Enable port(s) failed ret: %d\n", ret);
14975c3eb9f7SSanyog Kale 			return ret;
14985c3eb9f7SSanyog Kale 		}
149948949722SVinod Koul 	}
15005c3eb9f7SSanyog Kale 
15015c3eb9f7SSanyog Kale 	ret = do_bank_switch(stream);
15025c3eb9f7SSanyog Kale 	if (ret < 0) {
150368d9bfb6SPierre-Louis Bossart 		pr_err("%s: do_bank_switch failed: %d\n", __func__, ret);
15045c3eb9f7SSanyog Kale 		return ret;
15055c3eb9f7SSanyog Kale 	}
15065c3eb9f7SSanyog Kale 
15075c3eb9f7SSanyog Kale 	stream->state = SDW_STREAM_ENABLED;
15085c3eb9f7SSanyog Kale 	return 0;
15095c3eb9f7SSanyog Kale }
15105c3eb9f7SSanyog Kale 
15115c3eb9f7SSanyog Kale /**
15125c3eb9f7SSanyog Kale  * sdw_enable_stream() - Enable SoundWire stream
15135c3eb9f7SSanyog Kale  *
15145c3eb9f7SSanyog Kale  * @stream: Soundwire stream
15155c3eb9f7SSanyog Kale  *
151634962fb8SMauro Carvalho Chehab  * Documentation/driver-api/soundwire/stream.rst explains this API in detail
15175c3eb9f7SSanyog Kale  */
sdw_enable_stream(struct sdw_stream_runtime * stream)15185c3eb9f7SSanyog Kale int sdw_enable_stream(struct sdw_stream_runtime *stream)
15195c3eb9f7SSanyog Kale {
15203a0be1a6SPierre-Louis Bossart 	int ret;
15215c3eb9f7SSanyog Kale 
15225c3eb9f7SSanyog Kale 	if (!stream) {
152317ed5befSPierre-Louis Bossart 		pr_err("SoundWire: Handle not found for stream\n");
15245c3eb9f7SSanyog Kale 		return -EINVAL;
15255c3eb9f7SSanyog Kale 	}
15265c3eb9f7SSanyog Kale 
152748949722SVinod Koul 	sdw_acquire_bus_lock(stream);
15285c3eb9f7SSanyog Kale 
152963fadaa2SPierre-Louis Bossart 	if (stream->state == SDW_STREAM_ENABLED) {
153063fadaa2SPierre-Louis Bossart 		ret = 0;
153163fadaa2SPierre-Louis Bossart 		goto state_err;
153263fadaa2SPierre-Louis Bossart 	}
153363fadaa2SPierre-Louis Bossart 
153459528807SPierre-Louis Bossart 	if (stream->state != SDW_STREAM_PREPARED &&
153559528807SPierre-Louis Bossart 	    stream->state != SDW_STREAM_DISABLED) {
153659528807SPierre-Louis Bossart 		pr_err("%s: %s: inconsistent state state %d\n",
153759528807SPierre-Louis Bossart 		       __func__, stream->name, stream->state);
153859528807SPierre-Louis Bossart 		ret = -EINVAL;
153959528807SPierre-Louis Bossart 		goto state_err;
154059528807SPierre-Louis Bossart 	}
154159528807SPierre-Louis Bossart 
15425c3eb9f7SSanyog Kale 	ret = _sdw_enable_stream(stream);
15435c3eb9f7SSanyog Kale 
154459528807SPierre-Louis Bossart state_err:
154548949722SVinod Koul 	sdw_release_bus_lock(stream);
15465c3eb9f7SSanyog Kale 	return ret;
15475c3eb9f7SSanyog Kale }
15485c3eb9f7SSanyog Kale EXPORT_SYMBOL(sdw_enable_stream);
15495c3eb9f7SSanyog Kale 
_sdw_disable_stream(struct sdw_stream_runtime * stream)15505c3eb9f7SSanyog Kale static int _sdw_disable_stream(struct sdw_stream_runtime *stream)
15515c3eb9f7SSanyog Kale {
15523a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
15535c3eb9f7SSanyog Kale 	int ret;
15545c3eb9f7SSanyog Kale 
155548949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
15563a0be1a6SPierre-Louis Bossart 		struct sdw_bus *bus = m_rt->bus;
15573a0be1a6SPierre-Louis Bossart 
15585c3eb9f7SSanyog Kale 		/* Disable port(s) */
15595c3eb9f7SSanyog Kale 		ret = sdw_enable_disable_ports(m_rt, false);
15605c3eb9f7SSanyog Kale 		if (ret < 0) {
156117ed5befSPierre-Louis Bossart 			dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
15625c3eb9f7SSanyog Kale 			return ret;
15635c3eb9f7SSanyog Kale 		}
156448949722SVinod Koul 	}
15655c3eb9f7SSanyog Kale 	stream->state = SDW_STREAM_DISABLED;
15665c3eb9f7SSanyog Kale 
156748949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
15683a0be1a6SPierre-Louis Bossart 		struct sdw_bus *bus = m_rt->bus;
15693a0be1a6SPierre-Louis Bossart 
15705c3eb9f7SSanyog Kale 		/* Program params */
1571bfaa3549SRander Wang 		ret = sdw_program_params(bus, false);
15725c3eb9f7SSanyog Kale 		if (ret < 0) {
157368d9bfb6SPierre-Louis Bossart 			dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret);
15745c3eb9f7SSanyog Kale 			return ret;
15755c3eb9f7SSanyog Kale 		}
157648949722SVinod Koul 	}
15775c3eb9f7SSanyog Kale 
1578e0279b6bSPierre-Louis Bossart 	ret = do_bank_switch(stream);
1579e0279b6bSPierre-Louis Bossart 	if (ret < 0) {
158068d9bfb6SPierre-Louis Bossart 		pr_err("%s: do_bank_switch failed: %d\n", __func__, ret);
1581e0279b6bSPierre-Louis Bossart 		return ret;
1582e0279b6bSPierre-Louis Bossart 	}
1583e0279b6bSPierre-Louis Bossart 
1584e0279b6bSPierre-Louis Bossart 	/* make sure alternate bank (previous current) is also disabled */
1585e0279b6bSPierre-Louis Bossart 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
15863a0be1a6SPierre-Louis Bossart 		struct sdw_bus *bus = m_rt->bus;
15873a0be1a6SPierre-Louis Bossart 
1588e0279b6bSPierre-Louis Bossart 		/* Disable port(s) */
1589e0279b6bSPierre-Louis Bossart 		ret = sdw_enable_disable_ports(m_rt, false);
1590e0279b6bSPierre-Louis Bossart 		if (ret < 0) {
1591e0279b6bSPierre-Louis Bossart 			dev_err(bus->dev, "Disable port(s) failed: %d\n", ret);
1592e0279b6bSPierre-Louis Bossart 			return ret;
1593e0279b6bSPierre-Louis Bossart 		}
1594e0279b6bSPierre-Louis Bossart 	}
1595e0279b6bSPierre-Louis Bossart 
1596e0279b6bSPierre-Louis Bossart 	return 0;
15975c3eb9f7SSanyog Kale }
15985c3eb9f7SSanyog Kale 
15995c3eb9f7SSanyog Kale /**
16005c3eb9f7SSanyog Kale  * sdw_disable_stream() - Disable SoundWire stream
16015c3eb9f7SSanyog Kale  *
16025c3eb9f7SSanyog Kale  * @stream: Soundwire stream
16035c3eb9f7SSanyog Kale  *
160434962fb8SMauro Carvalho Chehab  * Documentation/driver-api/soundwire/stream.rst explains this API in detail
16055c3eb9f7SSanyog Kale  */
sdw_disable_stream(struct sdw_stream_runtime * stream)16065c3eb9f7SSanyog Kale int sdw_disable_stream(struct sdw_stream_runtime *stream)
16075c3eb9f7SSanyog Kale {
16083a0be1a6SPierre-Louis Bossart 	int ret;
16095c3eb9f7SSanyog Kale 
16105c3eb9f7SSanyog Kale 	if (!stream) {
161117ed5befSPierre-Louis Bossart 		pr_err("SoundWire: Handle not found for stream\n");
16125c3eb9f7SSanyog Kale 		return -EINVAL;
16135c3eb9f7SSanyog Kale 	}
16145c3eb9f7SSanyog Kale 
161548949722SVinod Koul 	sdw_acquire_bus_lock(stream);
16165c3eb9f7SSanyog Kale 
161763fadaa2SPierre-Louis Bossart 	if (stream->state == SDW_STREAM_DISABLED) {
161863fadaa2SPierre-Louis Bossart 		ret = 0;
161963fadaa2SPierre-Louis Bossart 		goto state_err;
162063fadaa2SPierre-Louis Bossart 	}
162163fadaa2SPierre-Louis Bossart 
162259528807SPierre-Louis Bossart 	if (stream->state != SDW_STREAM_ENABLED) {
162359528807SPierre-Louis Bossart 		pr_err("%s: %s: inconsistent state state %d\n",
162459528807SPierre-Louis Bossart 		       __func__, stream->name, stream->state);
162559528807SPierre-Louis Bossart 		ret = -EINVAL;
162659528807SPierre-Louis Bossart 		goto state_err;
162759528807SPierre-Louis Bossart 	}
162859528807SPierre-Louis Bossart 
16295c3eb9f7SSanyog Kale 	ret = _sdw_disable_stream(stream);
16305c3eb9f7SSanyog Kale 
163159528807SPierre-Louis Bossart state_err:
163248949722SVinod Koul 	sdw_release_bus_lock(stream);
16335c3eb9f7SSanyog Kale 	return ret;
16345c3eb9f7SSanyog Kale }
16355c3eb9f7SSanyog Kale EXPORT_SYMBOL(sdw_disable_stream);
16365c3eb9f7SSanyog Kale 
_sdw_deprepare_stream(struct sdw_stream_runtime * stream)16375c3eb9f7SSanyog Kale static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream)
16385c3eb9f7SSanyog Kale {
16393a0be1a6SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
16403a0be1a6SPierre-Louis Bossart 	struct sdw_bus *bus;
16415c3eb9f7SSanyog Kale 	int ret = 0;
16425c3eb9f7SSanyog Kale 
164348949722SVinod Koul 	list_for_each_entry(m_rt, &stream->master_list, stream_node) {
164448949722SVinod Koul 		bus = m_rt->bus;
16455c3eb9f7SSanyog Kale 		/* De-prepare port(s) */
16465c3eb9f7SSanyog Kale 		ret = sdw_prep_deprep_ports(m_rt, false);
16475c3eb9f7SSanyog Kale 		if (ret < 0) {
164862f0cec3SVinod Koul 			dev_err(bus->dev,
164962f0cec3SVinod Koul 				"De-prepare port(s) failed: %d\n", ret);
16505c3eb9f7SSanyog Kale 			return ret;
16515c3eb9f7SSanyog Kale 		}
16525c3eb9f7SSanyog Kale 
16535c3eb9f7SSanyog Kale 		/* TODO: Update this during Device-Device support */
16545c3eb9f7SSanyog Kale 		bus->params.bandwidth -= m_rt->stream->params.rate *
16555c3eb9f7SSanyog Kale 			m_rt->ch_count * m_rt->stream->params.bps;
16565c3eb9f7SSanyog Kale 
16579026118fSBard Liao 		/* Compute params */
16589026118fSBard Liao 		if (bus->compute_params) {
16599026118fSBard Liao 			ret = bus->compute_params(bus);
16609026118fSBard Liao 			if (ret < 0) {
16616122d3beSPierre-Louis Bossart 				dev_err(bus->dev, "Compute params failed: %d\n",
16629026118fSBard Liao 					ret);
16639026118fSBard Liao 				return ret;
16649026118fSBard Liao 			}
16659026118fSBard Liao 		}
16669026118fSBard Liao 
16675c3eb9f7SSanyog Kale 		/* Program params */
1668bfaa3549SRander Wang 		ret = sdw_program_params(bus, false);
16695c3eb9f7SSanyog Kale 		if (ret < 0) {
167068d9bfb6SPierre-Louis Bossart 			dev_err(bus->dev, "%s: Program params failed: %d\n", __func__, ret);
16715c3eb9f7SSanyog Kale 			return ret;
16725c3eb9f7SSanyog Kale 		}
167348949722SVinod Koul 	}
167448949722SVinod Koul 
167548949722SVinod Koul 	stream->state = SDW_STREAM_DEPREPARED;
16765c3eb9f7SSanyog Kale 	return do_bank_switch(stream);
16775c3eb9f7SSanyog Kale }
16785c3eb9f7SSanyog Kale 
16795c3eb9f7SSanyog Kale /**
16805c3eb9f7SSanyog Kale  * sdw_deprepare_stream() - Deprepare SoundWire stream
16815c3eb9f7SSanyog Kale  *
16825c3eb9f7SSanyog Kale  * @stream: Soundwire stream
16835c3eb9f7SSanyog Kale  *
168434962fb8SMauro Carvalho Chehab  * Documentation/driver-api/soundwire/stream.rst explains this API in detail
16855c3eb9f7SSanyog Kale  */
sdw_deprepare_stream(struct sdw_stream_runtime * stream)16865c3eb9f7SSanyog Kale int sdw_deprepare_stream(struct sdw_stream_runtime *stream)
16875c3eb9f7SSanyog Kale {
16883a0be1a6SPierre-Louis Bossart 	int ret;
16895c3eb9f7SSanyog Kale 
16905c3eb9f7SSanyog Kale 	if (!stream) {
169117ed5befSPierre-Louis Bossart 		pr_err("SoundWire: Handle not found for stream\n");
16925c3eb9f7SSanyog Kale 		return -EINVAL;
16935c3eb9f7SSanyog Kale 	}
16945c3eb9f7SSanyog Kale 
169548949722SVinod Koul 	sdw_acquire_bus_lock(stream);
169659528807SPierre-Louis Bossart 
169763fadaa2SPierre-Louis Bossart 	if (stream->state == SDW_STREAM_DEPREPARED) {
169863fadaa2SPierre-Louis Bossart 		ret = 0;
169963fadaa2SPierre-Louis Bossart 		goto state_err;
170063fadaa2SPierre-Louis Bossart 	}
170163fadaa2SPierre-Louis Bossart 
170259528807SPierre-Louis Bossart 	if (stream->state != SDW_STREAM_PREPARED &&
170359528807SPierre-Louis Bossart 	    stream->state != SDW_STREAM_DISABLED) {
170459528807SPierre-Louis Bossart 		pr_err("%s: %s: inconsistent state state %d\n",
170559528807SPierre-Louis Bossart 		       __func__, stream->name, stream->state);
170659528807SPierre-Louis Bossart 		ret = -EINVAL;
170759528807SPierre-Louis Bossart 		goto state_err;
170859528807SPierre-Louis Bossart 	}
170959528807SPierre-Louis Bossart 
17105c3eb9f7SSanyog Kale 	ret = _sdw_deprepare_stream(stream);
17115c3eb9f7SSanyog Kale 
171259528807SPierre-Louis Bossart state_err:
171348949722SVinod Koul 	sdw_release_bus_lock(stream);
17145c3eb9f7SSanyog Kale 	return ret;
17155c3eb9f7SSanyog Kale }
17165c3eb9f7SSanyog Kale EXPORT_SYMBOL(sdw_deprepare_stream);
17174550569bSPierre-Louis Bossart 
set_stream(struct snd_pcm_substream * substream,struct sdw_stream_runtime * sdw_stream)17184550569bSPierre-Louis Bossart static int set_stream(struct snd_pcm_substream *substream,
17194550569bSPierre-Louis Bossart 		      struct sdw_stream_runtime *sdw_stream)
17204550569bSPierre-Louis Bossart {
17214550569bSPierre-Louis Bossart 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
17224550569bSPierre-Louis Bossart 	struct snd_soc_dai *dai;
17234550569bSPierre-Louis Bossart 	int ret = 0;
17244550569bSPierre-Louis Bossart 	int i;
17254550569bSPierre-Louis Bossart 
17264550569bSPierre-Louis Bossart 	/* Set stream pointer on all DAIs */
17274550569bSPierre-Louis Bossart 	for_each_rtd_dais(rtd, i, dai) {
1728e8444560SPierre-Louis Bossart 		ret = snd_soc_dai_set_stream(dai, sdw_stream, substream->stream);
17294550569bSPierre-Louis Bossart 		if (ret < 0) {
17306122d3beSPierre-Louis Bossart 			dev_err(rtd->dev, "failed to set stream pointer on dai %s\n", dai->name);
17314550569bSPierre-Louis Bossart 			break;
17324550569bSPierre-Louis Bossart 		}
17334550569bSPierre-Louis Bossart 	}
17344550569bSPierre-Louis Bossart 
17354550569bSPierre-Louis Bossart 	return ret;
17364550569bSPierre-Louis Bossart }
17374550569bSPierre-Louis Bossart 
17384550569bSPierre-Louis Bossart /**
17397a908906SPierre-Louis Bossart  * sdw_alloc_stream() - Allocate and return stream runtime
17407a908906SPierre-Louis Bossart  *
17417a908906SPierre-Louis Bossart  * @stream_name: SoundWire stream name
17427a908906SPierre-Louis Bossart  *
17437a908906SPierre-Louis Bossart  * Allocates a SoundWire stream runtime instance.
17447a908906SPierre-Louis Bossart  * sdw_alloc_stream should be called only once per stream. Typically
17457a908906SPierre-Louis Bossart  * invoked from ALSA/ASoC machine/platform driver.
17467a908906SPierre-Louis Bossart  */
sdw_alloc_stream(const char * stream_name)17477a908906SPierre-Louis Bossart struct sdw_stream_runtime *sdw_alloc_stream(const char *stream_name)
17487a908906SPierre-Louis Bossart {
17497a908906SPierre-Louis Bossart 	struct sdw_stream_runtime *stream;
17507a908906SPierre-Louis Bossart 
17517a908906SPierre-Louis Bossart 	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
17527a908906SPierre-Louis Bossart 	if (!stream)
17537a908906SPierre-Louis Bossart 		return NULL;
17547a908906SPierre-Louis Bossart 
17557a908906SPierre-Louis Bossart 	stream->name = stream_name;
17567a908906SPierre-Louis Bossart 	INIT_LIST_HEAD(&stream->master_list);
17577a908906SPierre-Louis Bossart 	stream->state = SDW_STREAM_ALLOCATED;
17587a908906SPierre-Louis Bossart 	stream->m_rt_count = 0;
17597a908906SPierre-Louis Bossart 
17607a908906SPierre-Louis Bossart 	return stream;
17617a908906SPierre-Louis Bossart }
17627a908906SPierre-Louis Bossart EXPORT_SYMBOL(sdw_alloc_stream);
17637a908906SPierre-Louis Bossart 
17647a908906SPierre-Louis Bossart /**
17654550569bSPierre-Louis Bossart  * sdw_startup_stream() - Startup SoundWire stream
17664550569bSPierre-Louis Bossart  *
17673b71c690SVinod Koul  * @sdw_substream: Soundwire stream
17684550569bSPierre-Louis Bossart  *
17694550569bSPierre-Louis Bossart  * Documentation/driver-api/soundwire/stream.rst explains this API in detail
17704550569bSPierre-Louis Bossart  */
sdw_startup_stream(void * sdw_substream)17714550569bSPierre-Louis Bossart int sdw_startup_stream(void *sdw_substream)
17724550569bSPierre-Louis Bossart {
17734550569bSPierre-Louis Bossart 	struct snd_pcm_substream *substream = sdw_substream;
17744550569bSPierre-Louis Bossart 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
17754550569bSPierre-Louis Bossart 	struct sdw_stream_runtime *sdw_stream;
17764550569bSPierre-Louis Bossart 	char *name;
17774550569bSPierre-Louis Bossart 	int ret;
17784550569bSPierre-Louis Bossart 
17794550569bSPierre-Louis Bossart 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
17804550569bSPierre-Louis Bossart 		name = kasprintf(GFP_KERNEL, "%s-Playback", substream->name);
17814550569bSPierre-Louis Bossart 	else
17824550569bSPierre-Louis Bossart 		name = kasprintf(GFP_KERNEL, "%s-Capture", substream->name);
17834550569bSPierre-Louis Bossart 
17844550569bSPierre-Louis Bossart 	if (!name)
17854550569bSPierre-Louis Bossart 		return -ENOMEM;
17864550569bSPierre-Louis Bossart 
17874550569bSPierre-Louis Bossart 	sdw_stream = sdw_alloc_stream(name);
17884550569bSPierre-Louis Bossart 	if (!sdw_stream) {
17896122d3beSPierre-Louis Bossart 		dev_err(rtd->dev, "alloc stream failed for substream DAI %s\n", substream->name);
17904550569bSPierre-Louis Bossart 		ret = -ENOMEM;
17914550569bSPierre-Louis Bossart 		goto error;
17924550569bSPierre-Louis Bossart 	}
17934550569bSPierre-Louis Bossart 
17944550569bSPierre-Louis Bossart 	ret = set_stream(substream, sdw_stream);
17954550569bSPierre-Louis Bossart 	if (ret < 0)
17964550569bSPierre-Louis Bossart 		goto release_stream;
17974550569bSPierre-Louis Bossart 	return 0;
17984550569bSPierre-Louis Bossart 
17994550569bSPierre-Louis Bossart release_stream:
18004550569bSPierre-Louis Bossart 	sdw_release_stream(sdw_stream);
18014550569bSPierre-Louis Bossart 	set_stream(substream, NULL);
18024550569bSPierre-Louis Bossart error:
18034550569bSPierre-Louis Bossart 	kfree(name);
18044550569bSPierre-Louis Bossart 	return ret;
18054550569bSPierre-Louis Bossart }
18064550569bSPierre-Louis Bossart EXPORT_SYMBOL(sdw_startup_stream);
18074550569bSPierre-Louis Bossart 
18084550569bSPierre-Louis Bossart /**
18094550569bSPierre-Louis Bossart  * sdw_shutdown_stream() - Shutdown SoundWire stream
18104550569bSPierre-Louis Bossart  *
18113b71c690SVinod Koul  * @sdw_substream: Soundwire stream
18124550569bSPierre-Louis Bossart  *
18134550569bSPierre-Louis Bossart  * Documentation/driver-api/soundwire/stream.rst explains this API in detail
18144550569bSPierre-Louis Bossart  */
sdw_shutdown_stream(void * sdw_substream)18154550569bSPierre-Louis Bossart void sdw_shutdown_stream(void *sdw_substream)
18164550569bSPierre-Louis Bossart {
18174550569bSPierre-Louis Bossart 	struct snd_pcm_substream *substream = sdw_substream;
18184550569bSPierre-Louis Bossart 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
18194550569bSPierre-Louis Bossart 	struct sdw_stream_runtime *sdw_stream;
18204550569bSPierre-Louis Bossart 	struct snd_soc_dai *dai;
18214550569bSPierre-Louis Bossart 
18224550569bSPierre-Louis Bossart 	/* Find stream from first CPU DAI */
18234550569bSPierre-Louis Bossart 	dai = asoc_rtd_to_cpu(rtd, 0);
18244550569bSPierre-Louis Bossart 
1825e8444560SPierre-Louis Bossart 	sdw_stream = snd_soc_dai_get_stream(dai, substream->stream);
18264550569bSPierre-Louis Bossart 
18273471d2a1SPierre-Louis Bossart 	if (IS_ERR(sdw_stream)) {
18286122d3beSPierre-Louis Bossart 		dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name);
18294550569bSPierre-Louis Bossart 		return;
18304550569bSPierre-Louis Bossart 	}
18314550569bSPierre-Louis Bossart 
18324550569bSPierre-Louis Bossart 	/* release memory */
18334550569bSPierre-Louis Bossart 	kfree(sdw_stream->name);
18344550569bSPierre-Louis Bossart 	sdw_release_stream(sdw_stream);
18354550569bSPierre-Louis Bossart 
18364550569bSPierre-Louis Bossart 	/* clear DAI data */
18374550569bSPierre-Louis Bossart 	set_stream(substream, NULL);
18384550569bSPierre-Louis Bossart }
18394550569bSPierre-Louis Bossart EXPORT_SYMBOL(sdw_shutdown_stream);
18407a908906SPierre-Louis Bossart 
18417a908906SPierre-Louis Bossart /**
18427a908906SPierre-Louis Bossart  * sdw_release_stream() - Free the assigned stream runtime
18437a908906SPierre-Louis Bossart  *
18447a908906SPierre-Louis Bossart  * @stream: SoundWire stream runtime
18457a908906SPierre-Louis Bossart  *
18467a908906SPierre-Louis Bossart  * sdw_release_stream should be called only once per stream
18477a908906SPierre-Louis Bossart  */
sdw_release_stream(struct sdw_stream_runtime * stream)18487a908906SPierre-Louis Bossart void sdw_release_stream(struct sdw_stream_runtime *stream)
18497a908906SPierre-Louis Bossart {
18507a908906SPierre-Louis Bossart 	kfree(stream);
18517a908906SPierre-Louis Bossart }
18527a908906SPierre-Louis Bossart EXPORT_SYMBOL(sdw_release_stream);
18537a908906SPierre-Louis Bossart 
18547a908906SPierre-Louis Bossart /**
18557a908906SPierre-Louis Bossart  * sdw_stream_add_master() - Allocate and add master runtime to a stream
18567a908906SPierre-Louis Bossart  *
18577a908906SPierre-Louis Bossart  * @bus: SDW Bus instance
18587a908906SPierre-Louis Bossart  * @stream_config: Stream configuration for audio stream
18597a908906SPierre-Louis Bossart  * @port_config: Port configuration for audio stream
18607a908906SPierre-Louis Bossart  * @num_ports: Number of ports
18617a908906SPierre-Louis Bossart  * @stream: SoundWire stream
18627a908906SPierre-Louis Bossart  */
sdw_stream_add_master(struct sdw_bus * bus,struct sdw_stream_config * stream_config,struct sdw_port_config * port_config,unsigned int num_ports,struct sdw_stream_runtime * stream)18637a908906SPierre-Louis Bossart int sdw_stream_add_master(struct sdw_bus *bus,
18647a908906SPierre-Louis Bossart 			  struct sdw_stream_config *stream_config,
18657a908906SPierre-Louis Bossart 			  struct sdw_port_config *port_config,
18667a908906SPierre-Louis Bossart 			  unsigned int num_ports,
18677a908906SPierre-Louis Bossart 			  struct sdw_stream_runtime *stream)
18687a908906SPierre-Louis Bossart {
18697a908906SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
1870e0240644SCharles Keepax 	bool alloc_master_rt = false;
18717a908906SPierre-Louis Bossart 	int ret;
18727a908906SPierre-Louis Bossart 
18737a908906SPierre-Louis Bossart 	mutex_lock(&bus->bus_lock);
18747a908906SPierre-Louis Bossart 
18757a908906SPierre-Louis Bossart 	/*
18767a908906SPierre-Louis Bossart 	 * For multi link streams, add the second master only if
18777a908906SPierre-Louis Bossart 	 * the bus supports it.
18787a908906SPierre-Louis Bossart 	 * Check if bus->multi_link is set
18797a908906SPierre-Louis Bossart 	 */
18807a908906SPierre-Louis Bossart 	if (!bus->multi_link && stream->m_rt_count > 0) {
18817a908906SPierre-Louis Bossart 		dev_err(bus->dev,
18827a908906SPierre-Louis Bossart 			"Multilink not supported, link %d\n", bus->link_id);
18837a908906SPierre-Louis Bossart 		ret = -EINVAL;
18847a908906SPierre-Louis Bossart 		goto unlock;
18857a908906SPierre-Louis Bossart 	}
18867a908906SPierre-Louis Bossart 
18877a908906SPierre-Louis Bossart 	/*
18887a908906SPierre-Louis Bossart 	 * check if Master is already allocated (e.g. as a result of Slave adding
18897a908906SPierre-Louis Bossart 	 * it first), if so skip allocation and go to configuration
18907a908906SPierre-Louis Bossart 	 */
18917a908906SPierre-Louis Bossart 	m_rt = sdw_master_rt_find(bus, stream);
18920cbcced9SCharles Keepax 	if (!m_rt) {
18937a908906SPierre-Louis Bossart 		m_rt = sdw_master_rt_alloc(bus, stream);
18947a908906SPierre-Louis Bossart 		if (!m_rt) {
189568d9bfb6SPierre-Louis Bossart 			dev_err(bus->dev, "%s: Master runtime alloc failed for stream:%s\n",
189668d9bfb6SPierre-Louis Bossart 				__func__, stream->name);
18977a908906SPierre-Louis Bossart 			ret = -ENOMEM;
18987a908906SPierre-Louis Bossart 			goto unlock;
18997a908906SPierre-Louis Bossart 		}
1900e0240644SCharles Keepax 
1901e0240644SCharles Keepax 		alloc_master_rt = true;
19020cbcced9SCharles Keepax 	}
1903ac3bc88cSPierre-Louis Bossart 
19040cbcced9SCharles Keepax 	if (!sdw_master_port_allocated(m_rt)) {
1905ac3bc88cSPierre-Louis Bossart 		ret = sdw_master_port_alloc(m_rt, num_ports);
1906ac3bc88cSPierre-Louis Bossart 		if (ret)
1907ac3bc88cSPierre-Louis Bossart 			goto alloc_error;
1908ac3bc88cSPierre-Louis Bossart 
1909ac3bc88cSPierre-Louis Bossart 		stream->m_rt_count++;
19100cbcced9SCharles Keepax 	}
1911f3016b89SPierre-Louis Bossart 
19127a908906SPierre-Louis Bossart 	ret = sdw_master_rt_config(m_rt, stream_config);
19137a908906SPierre-Louis Bossart 	if (ret < 0)
19147a908906SPierre-Louis Bossart 		goto unlock;
19157a908906SPierre-Louis Bossart 
19167a908906SPierre-Louis Bossart 	ret = sdw_config_stream(bus->dev, stream, stream_config, false);
19177a908906SPierre-Louis Bossart 	if (ret)
1918ac3bc88cSPierre-Louis Bossart 		goto unlock;
19197a908906SPierre-Louis Bossart 
19207a908906SPierre-Louis Bossart 	ret = sdw_master_port_config(m_rt, port_config);
19217a908906SPierre-Louis Bossart 
19227a908906SPierre-Louis Bossart 	goto unlock;
19237a908906SPierre-Louis Bossart 
1924ac3bc88cSPierre-Louis Bossart alloc_error:
1925ac3bc88cSPierre-Louis Bossart 	/*
1926ac3bc88cSPierre-Louis Bossart 	 * we only cleanup what was allocated in this routine
1927ac3bc88cSPierre-Louis Bossart 	 */
1928ac3bc88cSPierre-Louis Bossart 	if (alloc_master_rt)
192900ce0d2aSPierre-Louis Bossart 		sdw_master_rt_free(m_rt, stream);
19307a908906SPierre-Louis Bossart unlock:
19317a908906SPierre-Louis Bossart 	mutex_unlock(&bus->bus_lock);
19327a908906SPierre-Louis Bossart 	return ret;
19337a908906SPierre-Louis Bossart }
19347a908906SPierre-Louis Bossart EXPORT_SYMBOL(sdw_stream_add_master);
19357a908906SPierre-Louis Bossart 
19367a908906SPierre-Louis Bossart /**
19377a908906SPierre-Louis Bossart  * sdw_stream_remove_master() - Remove master from sdw_stream
19387a908906SPierre-Louis Bossart  *
19397a908906SPierre-Louis Bossart  * @bus: SDW Bus instance
19407a908906SPierre-Louis Bossart  * @stream: SoundWire stream
19417a908906SPierre-Louis Bossart  *
19427a908906SPierre-Louis Bossart  * This removes and frees port_rt and master_rt from a stream
19437a908906SPierre-Louis Bossart  */
sdw_stream_remove_master(struct sdw_bus * bus,struct sdw_stream_runtime * stream)19447a908906SPierre-Louis Bossart int sdw_stream_remove_master(struct sdw_bus *bus,
19457a908906SPierre-Louis Bossart 			     struct sdw_stream_runtime *stream)
19467a908906SPierre-Louis Bossart {
19477a908906SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt, *_m_rt;
19487a908906SPierre-Louis Bossart 
19497a908906SPierre-Louis Bossart 	mutex_lock(&bus->bus_lock);
19507a908906SPierre-Louis Bossart 
19517a908906SPierre-Louis Bossart 	list_for_each_entry_safe(m_rt, _m_rt,
19527a908906SPierre-Louis Bossart 				 &stream->master_list, stream_node) {
19537a908906SPierre-Louis Bossart 		if (m_rt->bus != bus)
19547a908906SPierre-Louis Bossart 			continue;
19557a908906SPierre-Louis Bossart 
19567a908906SPierre-Louis Bossart 		sdw_master_port_free(m_rt);
195700ce0d2aSPierre-Louis Bossart 		sdw_master_rt_free(m_rt, stream);
19587a908906SPierre-Louis Bossart 		stream->m_rt_count--;
19597a908906SPierre-Louis Bossart 	}
19607a908906SPierre-Louis Bossart 
19617a908906SPierre-Louis Bossart 	if (list_empty(&stream->master_list))
19627a908906SPierre-Louis Bossart 		stream->state = SDW_STREAM_RELEASED;
19637a908906SPierre-Louis Bossart 
19647a908906SPierre-Louis Bossart 	mutex_unlock(&bus->bus_lock);
19657a908906SPierre-Louis Bossart 
19667a908906SPierre-Louis Bossart 	return 0;
19677a908906SPierre-Louis Bossart }
19687a908906SPierre-Louis Bossart EXPORT_SYMBOL(sdw_stream_remove_master);
19697a908906SPierre-Louis Bossart 
19707a908906SPierre-Louis Bossart /**
19717a908906SPierre-Louis Bossart  * sdw_stream_add_slave() - Allocate and add master/slave runtime to a stream
19727a908906SPierre-Louis Bossart  *
19737a908906SPierre-Louis Bossart  * @slave: SDW Slave instance
19747a908906SPierre-Louis Bossart  * @stream_config: Stream configuration for audio stream
19757a908906SPierre-Louis Bossart  * @stream: SoundWire stream
19767a908906SPierre-Louis Bossart  * @port_config: Port configuration for audio stream
19777a908906SPierre-Louis Bossart  * @num_ports: Number of ports
19787a908906SPierre-Louis Bossart  *
19797a908906SPierre-Louis Bossart  * It is expected that Slave is added before adding Master
19807a908906SPierre-Louis Bossart  * to the Stream.
19817a908906SPierre-Louis Bossart  *
19827a908906SPierre-Louis Bossart  */
sdw_stream_add_slave(struct sdw_slave * slave,struct sdw_stream_config * stream_config,struct sdw_port_config * port_config,unsigned int num_ports,struct sdw_stream_runtime * stream)19837a908906SPierre-Louis Bossart int sdw_stream_add_slave(struct sdw_slave *slave,
19847a908906SPierre-Louis Bossart 			 struct sdw_stream_config *stream_config,
19857a908906SPierre-Louis Bossart 			 struct sdw_port_config *port_config,
19867a908906SPierre-Louis Bossart 			 unsigned int num_ports,
19877a908906SPierre-Louis Bossart 			 struct sdw_stream_runtime *stream)
19887a908906SPierre-Louis Bossart {
19897a908906SPierre-Louis Bossart 	struct sdw_slave_runtime *s_rt;
19907a908906SPierre-Louis Bossart 	struct sdw_master_runtime *m_rt;
1991e0240644SCharles Keepax 	bool alloc_master_rt = false;
1992e0240644SCharles Keepax 	bool alloc_slave_rt = false;
1993ac3bc88cSPierre-Louis Bossart 
19947a908906SPierre-Louis Bossart 	int ret;
19957a908906SPierre-Louis Bossart 
19967a908906SPierre-Louis Bossart 	mutex_lock(&slave->bus->bus_lock);
19977a908906SPierre-Louis Bossart 
19987a908906SPierre-Louis Bossart 	/*
19997a908906SPierre-Louis Bossart 	 * check if Master is already allocated, if so skip allocation
20007a908906SPierre-Louis Bossart 	 * and go to configuration
20017a908906SPierre-Louis Bossart 	 */
20027a908906SPierre-Louis Bossart 	m_rt = sdw_master_rt_find(slave->bus, stream);
20030cbcced9SCharles Keepax 	if (!m_rt) {
20047a908906SPierre-Louis Bossart 		/*
20057a908906SPierre-Louis Bossart 		 * If this API is invoked by Slave first then m_rt is not valid.
20067a908906SPierre-Louis Bossart 		 * So, allocate m_rt and add Slave to it.
20077a908906SPierre-Louis Bossart 		 */
20087a908906SPierre-Louis Bossart 		m_rt = sdw_master_rt_alloc(slave->bus, stream);
20097a908906SPierre-Louis Bossart 		if (!m_rt) {
201068d9bfb6SPierre-Louis Bossart 			dev_err(&slave->dev, "%s: Master runtime alloc failed for stream:%s\n",
201168d9bfb6SPierre-Louis Bossart 				__func__, stream->name);
20127a908906SPierre-Louis Bossart 			ret = -ENOMEM;
2013ac3bc88cSPierre-Louis Bossart 			goto unlock;
20147a908906SPierre-Louis Bossart 		}
20157a908906SPierre-Louis Bossart 
2016e0240644SCharles Keepax 		alloc_master_rt = true;
20170cbcced9SCharles Keepax 	}
2018e0240644SCharles Keepax 
2019f3016b89SPierre-Louis Bossart 	s_rt = sdw_slave_rt_find(slave, stream);
20200cbcced9SCharles Keepax 	if (!s_rt) {
202142aad41eSPierre-Louis Bossart 		s_rt = sdw_slave_rt_alloc(slave, m_rt);
20227a908906SPierre-Louis Bossart 		if (!s_rt) {
20230cbcced9SCharles Keepax 			dev_err(&slave->dev, "Slave runtime alloc failed for stream:%s\n",
20240cbcced9SCharles Keepax 				stream->name);
20257a908906SPierre-Louis Bossart 			ret = -ENOMEM;
2026ac3bc88cSPierre-Louis Bossart 			goto alloc_error;
20277a908906SPierre-Louis Bossart 		}
20287a908906SPierre-Louis Bossart 
2029e0240644SCharles Keepax 		alloc_slave_rt = true;
20300cbcced9SCharles Keepax 	}
2031e0240644SCharles Keepax 
20320cbcced9SCharles Keepax 	if (!sdw_slave_port_allocated(s_rt)) {
20337a908906SPierre-Louis Bossart 		ret = sdw_slave_port_alloc(slave, s_rt, num_ports);
20347a908906SPierre-Louis Bossart 		if (ret)
2035ac3bc88cSPierre-Louis Bossart 			goto alloc_error;
20360cbcced9SCharles Keepax 	}
2037ac3bc88cSPierre-Louis Bossart 
2038ac3bc88cSPierre-Louis Bossart 	ret =  sdw_master_rt_config(m_rt, stream_config);
2039ac3bc88cSPierre-Louis Bossart 	if (ret)
2040ac3bc88cSPierre-Louis Bossart 		goto unlock;
2041ac3bc88cSPierre-Louis Bossart 
2042ac3bc88cSPierre-Louis Bossart 	ret = sdw_slave_rt_config(s_rt, stream_config);
2043ac3bc88cSPierre-Louis Bossart 	if (ret)
2044ac3bc88cSPierre-Louis Bossart 		goto unlock;
2045ac3bc88cSPierre-Louis Bossart 
2046ac3bc88cSPierre-Louis Bossart 	ret = sdw_config_stream(&slave->dev, stream, stream_config, true);
2047ac3bc88cSPierre-Louis Bossart 	if (ret)
2048ac3bc88cSPierre-Louis Bossart 		goto unlock;
20497a908906SPierre-Louis Bossart 
20507a908906SPierre-Louis Bossart 	ret = sdw_slave_port_config(slave, s_rt, port_config);
20517a908906SPierre-Louis Bossart 	if (ret)
2052ac3bc88cSPierre-Louis Bossart 		goto unlock;
20537a908906SPierre-Louis Bossart 
20547a908906SPierre-Louis Bossart 	/*
20557a908906SPierre-Louis Bossart 	 * Change stream state to CONFIGURED on first Slave add.
20567a908906SPierre-Louis Bossart 	 * Bus is not aware of number of Slave(s) in a stream at this
20577a908906SPierre-Louis Bossart 	 * point so cannot depend on all Slave(s) to be added in order to
20587a908906SPierre-Louis Bossart 	 * change stream state to CONFIGURED.
20597a908906SPierre-Louis Bossart 	 */
20607a908906SPierre-Louis Bossart 	stream->state = SDW_STREAM_CONFIGURED;
2061ac3bc88cSPierre-Louis Bossart 	goto unlock;
20627a908906SPierre-Louis Bossart 
2063ac3bc88cSPierre-Louis Bossart alloc_error:
20647a908906SPierre-Louis Bossart 	/*
2065ac3bc88cSPierre-Louis Bossart 	 * we only cleanup what was allocated in this routine. The 'else if'
2066ac3bc88cSPierre-Louis Bossart 	 * is intentional, the 'master_rt_free' will call sdw_slave_rt_free()
2067ac3bc88cSPierre-Louis Bossart 	 * internally.
20687a908906SPierre-Louis Bossart 	 */
2069ac3bc88cSPierre-Louis Bossart 	if (alloc_master_rt)
207000ce0d2aSPierre-Louis Bossart 		sdw_master_rt_free(m_rt, stream);
2071ac3bc88cSPierre-Louis Bossart 	else if (alloc_slave_rt)
2072ac3bc88cSPierre-Louis Bossart 		sdw_slave_rt_free(slave, stream);
2073ac3bc88cSPierre-Louis Bossart unlock:
20747a908906SPierre-Louis Bossart 	mutex_unlock(&slave->bus->bus_lock);
20757a908906SPierre-Louis Bossart 	return ret;
20767a908906SPierre-Louis Bossart }
20777a908906SPierre-Louis Bossart EXPORT_SYMBOL(sdw_stream_add_slave);
20787a908906SPierre-Louis Bossart 
20797a908906SPierre-Louis Bossart /**
20807a908906SPierre-Louis Bossart  * sdw_stream_remove_slave() - Remove slave from sdw_stream
20817a908906SPierre-Louis Bossart  *
20827a908906SPierre-Louis Bossart  * @slave: SDW Slave instance
20837a908906SPierre-Louis Bossart  * @stream: SoundWire stream
20847a908906SPierre-Louis Bossart  *
20857a908906SPierre-Louis Bossart  * This removes and frees port_rt and slave_rt from a stream
20867a908906SPierre-Louis Bossart  */
sdw_stream_remove_slave(struct sdw_slave * slave,struct sdw_stream_runtime * stream)20877a908906SPierre-Louis Bossart int sdw_stream_remove_slave(struct sdw_slave *slave,
20887a908906SPierre-Louis Bossart 			    struct sdw_stream_runtime *stream)
20897a908906SPierre-Louis Bossart {
20907a908906SPierre-Louis Bossart 	mutex_lock(&slave->bus->bus_lock);
20917a908906SPierre-Louis Bossart 
20927a908906SPierre-Louis Bossart 	sdw_slave_port_free(slave, stream);
209300ce0d2aSPierre-Louis Bossart 	sdw_slave_rt_free(slave, stream);
20947a908906SPierre-Louis Bossart 
20957a908906SPierre-Louis Bossart 	mutex_unlock(&slave->bus->bus_lock);
20967a908906SPierre-Louis Bossart 
20977a908906SPierre-Louis Bossart 	return 0;
20987a908906SPierre-Louis Bossart }
20997a908906SPierre-Louis Bossart EXPORT_SYMBOL(sdw_stream_remove_slave);
2100