xref: /openbmc/linux/sound/firewire/cmp.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
1da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
231ef9134SClemens Ladisch /*
331ef9134SClemens Ladisch  * Connection Management Procedures (IEC 61883-1) helper functions
431ef9134SClemens Ladisch  *
531ef9134SClemens Ladisch  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
631ef9134SClemens Ladisch  */
731ef9134SClemens Ladisch 
831ef9134SClemens Ladisch #include <linux/device.h>
931ef9134SClemens Ladisch #include <linux/firewire.h>
1031ef9134SClemens Ladisch #include <linux/firewire-constants.h>
1131ef9134SClemens Ladisch #include <linux/module.h>
1231ef9134SClemens Ladisch #include <linux/sched.h>
1331ef9134SClemens Ladisch #include "lib.h"
1431ef9134SClemens Ladisch #include "iso-resources.h"
1531ef9134SClemens Ladisch #include "cmp.h"
1631ef9134SClemens Ladisch 
17a7fa0d04STakashi Sakamoto /* MPR common fields */
18a7fa0d04STakashi Sakamoto #define MPR_SPEED_MASK		0xc0000000
19a7fa0d04STakashi Sakamoto #define MPR_SPEED_SHIFT		30
20a7fa0d04STakashi Sakamoto #define MPR_XSPEED_MASK		0x00000060
21a7fa0d04STakashi Sakamoto #define MPR_XSPEED_SHIFT	5
22a7fa0d04STakashi Sakamoto #define MPR_PLUGS_MASK		0x0000001f
2331ef9134SClemens Ladisch 
24a7fa0d04STakashi Sakamoto /* PCR common fields */
25a7fa0d04STakashi Sakamoto #define PCR_ONLINE		0x80000000
26a7fa0d04STakashi Sakamoto #define PCR_BCAST_CONN		0x40000000
27a7fa0d04STakashi Sakamoto #define PCR_P2P_CONN_MASK	0x3f000000
28a7fa0d04STakashi Sakamoto #define PCR_P2P_CONN_SHIFT	24
29a7fa0d04STakashi Sakamoto #define PCR_CHANNEL_MASK	0x003f0000
30a7fa0d04STakashi Sakamoto #define PCR_CHANNEL_SHIFT	16
3131ef9134SClemens Ladisch 
3244aff698STakashi Sakamoto /* oPCR specific fields */
3344aff698STakashi Sakamoto #define OPCR_XSPEED_MASK	0x00C00000
3444aff698STakashi Sakamoto #define OPCR_XSPEED_SHIFT	22
3544aff698STakashi Sakamoto #define OPCR_SPEED_MASK		0x0000C000
3644aff698STakashi Sakamoto #define OPCR_SPEED_SHIFT	14
3744aff698STakashi Sakamoto #define OPCR_OVERHEAD_ID_MASK	0x00003C00
3844aff698STakashi Sakamoto #define OPCR_OVERHEAD_ID_SHIFT	10
3944aff698STakashi Sakamoto 
4031ef9134SClemens Ladisch enum bus_reset_handling {
4131ef9134SClemens Ladisch 	ABORT_ON_BUS_RESET,
4231ef9134SClemens Ladisch 	SUCCEED_ON_BUS_RESET,
4331ef9134SClemens Ladisch };
4431ef9134SClemens Ladisch 
45b9075fa9SJoe Perches static __printf(2, 3)
cmp_error(struct cmp_connection * c,const char * fmt,...)4631ef9134SClemens Ladisch void cmp_error(struct cmp_connection *c, const char *fmt, ...)
4731ef9134SClemens Ladisch {
4831ef9134SClemens Ladisch 	va_list va;
4931ef9134SClemens Ladisch 
5031ef9134SClemens Ladisch 	va_start(va, fmt);
5131ef9134SClemens Ladisch 	dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
5244aff698STakashi Sakamoto 		(c->direction == CMP_INPUT) ? 'i' : 'o',
5344aff698STakashi Sakamoto 		c->pcr_index, &(struct va_format){ fmt, &va });
5431ef9134SClemens Ladisch 	va_end(va);
5531ef9134SClemens Ladisch }
5631ef9134SClemens Ladisch 
mpr_address(struct cmp_connection * c)5744aff698STakashi Sakamoto static u64 mpr_address(struct cmp_connection *c)
5844aff698STakashi Sakamoto {
5944aff698STakashi Sakamoto 	if (c->direction == CMP_INPUT)
6044aff698STakashi Sakamoto 		return CSR_REGISTER_BASE + CSR_IMPR;
6144aff698STakashi Sakamoto 	else
6244aff698STakashi Sakamoto 		return CSR_REGISTER_BASE + CSR_OMPR;
6344aff698STakashi Sakamoto }
6444aff698STakashi Sakamoto 
pcr_address(struct cmp_connection * c)6544aff698STakashi Sakamoto static u64 pcr_address(struct cmp_connection *c)
6644aff698STakashi Sakamoto {
6744aff698STakashi Sakamoto 	if (c->direction == CMP_INPUT)
6844aff698STakashi Sakamoto 		return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
6944aff698STakashi Sakamoto 	else
7044aff698STakashi Sakamoto 		return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
7144aff698STakashi Sakamoto }
7244aff698STakashi Sakamoto 
pcr_modify(struct cmp_connection * c,__be32 (* modify)(struct cmp_connection * c,__be32 old),int (* check)(struct cmp_connection * c,__be32 pcr),enum bus_reset_handling bus_reset_handling)7331ef9134SClemens Ladisch static int pcr_modify(struct cmp_connection *c,
7431ef9134SClemens Ladisch 		      __be32 (*modify)(struct cmp_connection *c, __be32 old),
7531ef9134SClemens Ladisch 		      int (*check)(struct cmp_connection *c, __be32 pcr),
7631ef9134SClemens Ladisch 		      enum bus_reset_handling bus_reset_handling)
7731ef9134SClemens Ladisch {
78f30e6d3eSStefan Richter 	__be32 old_arg, buffer[2];
7931ef9134SClemens Ladisch 	int err;
8031ef9134SClemens Ladisch 
8131ef9134SClemens Ladisch 	buffer[0] = c->last_pcr_value;
8231ef9134SClemens Ladisch 	for (;;) {
8331ef9134SClemens Ladisch 		old_arg = buffer[0];
8431ef9134SClemens Ladisch 		buffer[1] = modify(c, buffer[0]);
8531ef9134SClemens Ladisch 
861b70485fSClemens Ladisch 		err = snd_fw_transaction(
871b70485fSClemens Ladisch 				c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
8844aff698STakashi Sakamoto 				pcr_address(c), buffer, 8,
891b70485fSClemens Ladisch 				FW_FIXED_GENERATION | c->resources.generation);
9031ef9134SClemens Ladisch 
911b70485fSClemens Ladisch 		if (err < 0) {
921b70485fSClemens Ladisch 			if (err == -EAGAIN &&
931b70485fSClemens Ladisch 			    bus_reset_handling == SUCCEED_ON_BUS_RESET)
941b70485fSClemens Ladisch 				err = 0;
951b70485fSClemens Ladisch 			return err;
961b70485fSClemens Ladisch 		}
971b70485fSClemens Ladisch 
9831ef9134SClemens Ladisch 		if (buffer[0] == old_arg) /* success? */
9931ef9134SClemens Ladisch 			break;
10031ef9134SClemens Ladisch 
10131ef9134SClemens Ladisch 		if (check) {
10231ef9134SClemens Ladisch 			err = check(c, buffer[0]);
10331ef9134SClemens Ladisch 			if (err < 0)
10431ef9134SClemens Ladisch 				return err;
10531ef9134SClemens Ladisch 		}
10631ef9134SClemens Ladisch 	}
10731ef9134SClemens Ladisch 	c->last_pcr_value = buffer[1];
10831ef9134SClemens Ladisch 
10931ef9134SClemens Ladisch 	return 0;
11031ef9134SClemens Ladisch }
11131ef9134SClemens Ladisch 
11231ef9134SClemens Ladisch 
11331ef9134SClemens Ladisch /**
11431ef9134SClemens Ladisch  * cmp_connection_init - initializes a connection manager
11531ef9134SClemens Ladisch  * @c: the connection manager to initialize
11631ef9134SClemens Ladisch  * @unit: a unit of the target device
1175f217f90STakashi Sakamoto  * @direction: input or output
118a7fa0d04STakashi Sakamoto  * @pcr_index: the index of the iPCR/oPCR on the target device
11931ef9134SClemens Ladisch  */
cmp_connection_init(struct cmp_connection * c,struct fw_unit * unit,enum cmp_direction direction,unsigned int pcr_index)12031ef9134SClemens Ladisch int cmp_connection_init(struct cmp_connection *c,
12131ef9134SClemens Ladisch 			struct fw_unit *unit,
122c68a1c65STakashi Sakamoto 			enum cmp_direction direction,
123a7fa0d04STakashi Sakamoto 			unsigned int pcr_index)
12431ef9134SClemens Ladisch {
125a7fa0d04STakashi Sakamoto 	__be32 mpr_be;
126a7fa0d04STakashi Sakamoto 	u32 mpr;
12731ef9134SClemens Ladisch 	int err;
12831ef9134SClemens Ladisch 
12944aff698STakashi Sakamoto 	c->direction = direction;
13031ef9134SClemens Ladisch 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
13144aff698STakashi Sakamoto 				 mpr_address(c), &mpr_be, 4, 0);
13231ef9134SClemens Ladisch 	if (err < 0)
13331ef9134SClemens Ladisch 		return err;
134a7fa0d04STakashi Sakamoto 	mpr = be32_to_cpu(mpr_be);
13531ef9134SClemens Ladisch 
136a7fa0d04STakashi Sakamoto 	if (pcr_index >= (mpr & MPR_PLUGS_MASK))
13731ef9134SClemens Ladisch 		return -EINVAL;
13831ef9134SClemens Ladisch 
1395b2599a0SClemens Ladisch 	err = fw_iso_resources_init(&c->resources, unit);
1405b2599a0SClemens Ladisch 	if (err < 0)
1415b2599a0SClemens Ladisch 		return err;
1425b2599a0SClemens Ladisch 
14331ef9134SClemens Ladisch 	c->connected = false;
14431ef9134SClemens Ladisch 	mutex_init(&c->mutex);
14531ef9134SClemens Ladisch 	c->last_pcr_value = cpu_to_be32(0x80000000);
146a7fa0d04STakashi Sakamoto 	c->pcr_index = pcr_index;
147a7fa0d04STakashi Sakamoto 	c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
14831ef9134SClemens Ladisch 	if (c->max_speed == SCODE_BETA)
149a7fa0d04STakashi Sakamoto 		c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
15031ef9134SClemens Ladisch 
15131ef9134SClemens Ladisch 	return 0;
15231ef9134SClemens Ladisch }
15331ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_init);
15431ef9134SClemens Ladisch 
15531ef9134SClemens Ladisch /**
156b04479fbSTakashi Sakamoto  * cmp_connection_check_used - check connection is already esablished or not
157b04479fbSTakashi Sakamoto  * @c: the connection manager to be checked
1585f217f90STakashi Sakamoto  * @used: the pointer to store the result of checking the connection
159b04479fbSTakashi Sakamoto  */
cmp_connection_check_used(struct cmp_connection * c,bool * used)160b04479fbSTakashi Sakamoto int cmp_connection_check_used(struct cmp_connection *c, bool *used)
161b04479fbSTakashi Sakamoto {
162b04479fbSTakashi Sakamoto 	__be32 pcr;
163b04479fbSTakashi Sakamoto 	int err;
164b04479fbSTakashi Sakamoto 
165b04479fbSTakashi Sakamoto 	err = snd_fw_transaction(
166b04479fbSTakashi Sakamoto 			c->resources.unit, TCODE_READ_QUADLET_REQUEST,
167b04479fbSTakashi Sakamoto 			pcr_address(c), &pcr, 4, 0);
168b04479fbSTakashi Sakamoto 	if (err >= 0)
16951212eeaSTakashi Sakamoto 		*used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
17051212eeaSTakashi Sakamoto 					     PCR_P2P_CONN_MASK));
17151212eeaSTakashi Sakamoto 
172b04479fbSTakashi Sakamoto 	return err;
173b04479fbSTakashi Sakamoto }
174b04479fbSTakashi Sakamoto EXPORT_SYMBOL(cmp_connection_check_used);
175b04479fbSTakashi Sakamoto 
176b04479fbSTakashi Sakamoto /**
17731ef9134SClemens Ladisch  * cmp_connection_destroy - free connection manager resources
17831ef9134SClemens Ladisch  * @c: the connection manager
17931ef9134SClemens Ladisch  */
cmp_connection_destroy(struct cmp_connection * c)18031ef9134SClemens Ladisch void cmp_connection_destroy(struct cmp_connection *c)
18131ef9134SClemens Ladisch {
18231ef9134SClemens Ladisch 	WARN_ON(c->connected);
18331ef9134SClemens Ladisch 	mutex_destroy(&c->mutex);
18431ef9134SClemens Ladisch 	fw_iso_resources_destroy(&c->resources);
18531ef9134SClemens Ladisch }
18631ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_destroy);
18731ef9134SClemens Ladisch 
cmp_connection_reserve(struct cmp_connection * c,unsigned int max_payload_bytes)188*7bc93821STakashi Sakamoto int cmp_connection_reserve(struct cmp_connection *c,
189*7bc93821STakashi Sakamoto 			   unsigned int max_payload_bytes)
190*7bc93821STakashi Sakamoto {
191*7bc93821STakashi Sakamoto 	int err;
192*7bc93821STakashi Sakamoto 
193*7bc93821STakashi Sakamoto 	mutex_lock(&c->mutex);
194*7bc93821STakashi Sakamoto 
195*7bc93821STakashi Sakamoto 	if (WARN_ON(c->resources.allocated)) {
196*7bc93821STakashi Sakamoto 		err = -EBUSY;
197*7bc93821STakashi Sakamoto 		goto end;
198*7bc93821STakashi Sakamoto 	}
199*7bc93821STakashi Sakamoto 
200*7bc93821STakashi Sakamoto 	c->speed = min(c->max_speed,
201*7bc93821STakashi Sakamoto 		       fw_parent_device(c->resources.unit)->max_speed);
202*7bc93821STakashi Sakamoto 
203*7bc93821STakashi Sakamoto 	err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
204*7bc93821STakashi Sakamoto 					c->speed);
205*7bc93821STakashi Sakamoto end:
206*7bc93821STakashi Sakamoto 	mutex_unlock(&c->mutex);
207*7bc93821STakashi Sakamoto 
208*7bc93821STakashi Sakamoto 	return err;
209*7bc93821STakashi Sakamoto }
210*7bc93821STakashi Sakamoto EXPORT_SYMBOL(cmp_connection_reserve);
211*7bc93821STakashi Sakamoto 
cmp_connection_release(struct cmp_connection * c)212*7bc93821STakashi Sakamoto void cmp_connection_release(struct cmp_connection *c)
213*7bc93821STakashi Sakamoto {
214*7bc93821STakashi Sakamoto 	mutex_lock(&c->mutex);
215*7bc93821STakashi Sakamoto 	fw_iso_resources_free(&c->resources);
216*7bc93821STakashi Sakamoto 	mutex_unlock(&c->mutex);
217*7bc93821STakashi Sakamoto }
218*7bc93821STakashi Sakamoto EXPORT_SYMBOL(cmp_connection_release);
21931ef9134SClemens Ladisch 
ipcr_set_modify(struct cmp_connection * c,__be32 ipcr)22031ef9134SClemens Ladisch static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
22131ef9134SClemens Ladisch {
222a7fa0d04STakashi Sakamoto 	ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
223a7fa0d04STakashi Sakamoto 			     PCR_P2P_CONN_MASK |
224a7fa0d04STakashi Sakamoto 			     PCR_CHANNEL_MASK);
225a7fa0d04STakashi Sakamoto 	ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
226a7fa0d04STakashi Sakamoto 	ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
22731ef9134SClemens Ladisch 
22831ef9134SClemens Ladisch 	return ipcr;
22931ef9134SClemens Ladisch }
23031ef9134SClemens Ladisch 
get_overhead_id(struct cmp_connection * c)23144aff698STakashi Sakamoto static int get_overhead_id(struct cmp_connection *c)
23244aff698STakashi Sakamoto {
23344aff698STakashi Sakamoto 	int id;
23444aff698STakashi Sakamoto 
23544aff698STakashi Sakamoto 	/*
23644aff698STakashi Sakamoto 	 * apply "oPCR overhead ID encoding"
23744aff698STakashi Sakamoto 	 * the encoding table can convert up to 512.
23844aff698STakashi Sakamoto 	 * here the value over 512 is converted as the same way as 512.
23944aff698STakashi Sakamoto 	 */
24044aff698STakashi Sakamoto 	for (id = 1; id < 16; id++) {
24144aff698STakashi Sakamoto 		if (c->resources.bandwidth_overhead < (id << 5))
24244aff698STakashi Sakamoto 			break;
24344aff698STakashi Sakamoto 	}
24444aff698STakashi Sakamoto 	if (id == 16)
24544aff698STakashi Sakamoto 		id = 0;
24644aff698STakashi Sakamoto 
24744aff698STakashi Sakamoto 	return id;
24844aff698STakashi Sakamoto }
24944aff698STakashi Sakamoto 
opcr_set_modify(struct cmp_connection * c,__be32 opcr)25044aff698STakashi Sakamoto static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
25144aff698STakashi Sakamoto {
25244aff698STakashi Sakamoto 	unsigned int spd, xspd;
25344aff698STakashi Sakamoto 
25444aff698STakashi Sakamoto 	/* generate speed and extended speed field value */
25544aff698STakashi Sakamoto 	if (c->speed > SCODE_400) {
25644aff698STakashi Sakamoto 		spd  = SCODE_800;
25744aff698STakashi Sakamoto 		xspd = c->speed - SCODE_800;
25844aff698STakashi Sakamoto 	} else {
25944aff698STakashi Sakamoto 		spd = c->speed;
26044aff698STakashi Sakamoto 		xspd = 0;
26144aff698STakashi Sakamoto 	}
26244aff698STakashi Sakamoto 
26344aff698STakashi Sakamoto 	opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
26444aff698STakashi Sakamoto 			     PCR_P2P_CONN_MASK |
26544aff698STakashi Sakamoto 			     OPCR_XSPEED_MASK |
26644aff698STakashi Sakamoto 			     PCR_CHANNEL_MASK |
26744aff698STakashi Sakamoto 			     OPCR_SPEED_MASK |
26844aff698STakashi Sakamoto 			     OPCR_OVERHEAD_ID_MASK);
26944aff698STakashi Sakamoto 	opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
27044aff698STakashi Sakamoto 	opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
27144aff698STakashi Sakamoto 	opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
27244aff698STakashi Sakamoto 	opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
27344aff698STakashi Sakamoto 	opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
27444aff698STakashi Sakamoto 
27544aff698STakashi Sakamoto 	return opcr;
27644aff698STakashi Sakamoto }
27744aff698STakashi Sakamoto 
pcr_set_check(struct cmp_connection * c,__be32 pcr)278a7fa0d04STakashi Sakamoto static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
27931ef9134SClemens Ladisch {
280a7fa0d04STakashi Sakamoto 	if (pcr & cpu_to_be32(PCR_BCAST_CONN |
281a7fa0d04STakashi Sakamoto 			      PCR_P2P_CONN_MASK)) {
28231ef9134SClemens Ladisch 		cmp_error(c, "plug is already in use\n");
28331ef9134SClemens Ladisch 		return -EBUSY;
28431ef9134SClemens Ladisch 	}
285a7fa0d04STakashi Sakamoto 	if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
28631ef9134SClemens Ladisch 		cmp_error(c, "plug is not on-line\n");
28731ef9134SClemens Ladisch 		return -ECONNREFUSED;
28831ef9134SClemens Ladisch 	}
28931ef9134SClemens Ladisch 
29031ef9134SClemens Ladisch 	return 0;
29131ef9134SClemens Ladisch }
29231ef9134SClemens Ladisch 
29331ef9134SClemens Ladisch /**
29431ef9134SClemens Ladisch  * cmp_connection_establish - establish a connection to the target
29531ef9134SClemens Ladisch  * @c: the connection manager
29631ef9134SClemens Ladisch  *
29731ef9134SClemens Ladisch  * This function establishes a point-to-point connection from the local
29831ef9134SClemens Ladisch  * computer to the target by allocating isochronous resources (channel and
299a7fa0d04STakashi Sakamoto  * bandwidth) and setting the target's input/output plug control register.
300a7fa0d04STakashi Sakamoto  * When this function succeeds, the caller is responsible for starting
301a7fa0d04STakashi Sakamoto  * transmitting packets.
30231ef9134SClemens Ladisch  */
cmp_connection_establish(struct cmp_connection * c)303*7bc93821STakashi Sakamoto int cmp_connection_establish(struct cmp_connection *c)
30431ef9134SClemens Ladisch {
30531ef9134SClemens Ladisch 	int err;
30631ef9134SClemens Ladisch 
30731ef9134SClemens Ladisch 	mutex_lock(&c->mutex);
30831ef9134SClemens Ladisch 
309*7bc93821STakashi Sakamoto 	if (WARN_ON(c->connected)) {
310*7bc93821STakashi Sakamoto 		mutex_unlock(&c->mutex);
311*7bc93821STakashi Sakamoto 		return -EISCONN;
312*7bc93821STakashi Sakamoto 	}
31331ef9134SClemens Ladisch 
314*7bc93821STakashi Sakamoto retry_after_bus_reset:
31544aff698STakashi Sakamoto 	if (c->direction == CMP_OUTPUT)
31644aff698STakashi Sakamoto 		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
31744aff698STakashi Sakamoto 				 ABORT_ON_BUS_RESET);
31844aff698STakashi Sakamoto 	else
319a7fa0d04STakashi Sakamoto 		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
32031ef9134SClemens Ladisch 				 ABORT_ON_BUS_RESET);
32144aff698STakashi Sakamoto 
32231ef9134SClemens Ladisch 	if (err == -EAGAIN) {
323*7bc93821STakashi Sakamoto 		err = fw_iso_resources_update(&c->resources);
324*7bc93821STakashi Sakamoto 		if (err >= 0)
32531ef9134SClemens Ladisch 			goto retry_after_bus_reset;
32631ef9134SClemens Ladisch 	}
327*7bc93821STakashi Sakamoto 	if (err >= 0)
32831ef9134SClemens Ladisch 		c->connected = true;
32931ef9134SClemens Ladisch 
33031ef9134SClemens Ladisch 	mutex_unlock(&c->mutex);
33131ef9134SClemens Ladisch 
33231ef9134SClemens Ladisch 	return err;
33331ef9134SClemens Ladisch }
33431ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_establish);
33531ef9134SClemens Ladisch 
33631ef9134SClemens Ladisch /**
33731ef9134SClemens Ladisch  * cmp_connection_update - update the connection after a bus reset
33831ef9134SClemens Ladisch  * @c: the connection manager
33931ef9134SClemens Ladisch  *
340a7fa0d04STakashi Sakamoto  * This function must be called from the driver's .update handler to
341a7fa0d04STakashi Sakamoto  * reestablish any connection that might have been active.
34231ef9134SClemens Ladisch  *
34331ef9134SClemens Ladisch  * Returns zero on success, or a negative error code.  On an error, the
34431ef9134SClemens Ladisch  * connection is broken and the caller must stop transmitting iso packets.
34531ef9134SClemens Ladisch  */
cmp_connection_update(struct cmp_connection * c)34631ef9134SClemens Ladisch int cmp_connection_update(struct cmp_connection *c)
34731ef9134SClemens Ladisch {
34831ef9134SClemens Ladisch 	int err;
34931ef9134SClemens Ladisch 
35031ef9134SClemens Ladisch 	mutex_lock(&c->mutex);
35131ef9134SClemens Ladisch 
35231ef9134SClemens Ladisch 	if (!c->connected) {
35331ef9134SClemens Ladisch 		mutex_unlock(&c->mutex);
35431ef9134SClemens Ladisch 		return 0;
35531ef9134SClemens Ladisch 	}
35631ef9134SClemens Ladisch 
35731ef9134SClemens Ladisch 	err = fw_iso_resources_update(&c->resources);
35831ef9134SClemens Ladisch 	if (err < 0)
35931ef9134SClemens Ladisch 		goto err_unconnect;
36031ef9134SClemens Ladisch 
36144aff698STakashi Sakamoto 	if (c->direction == CMP_OUTPUT)
36244aff698STakashi Sakamoto 		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
36344aff698STakashi Sakamoto 				 SUCCEED_ON_BUS_RESET);
36444aff698STakashi Sakamoto 	else
365a7fa0d04STakashi Sakamoto 		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
36631ef9134SClemens Ladisch 				 SUCCEED_ON_BUS_RESET);
36744aff698STakashi Sakamoto 
36831ef9134SClemens Ladisch 	if (err < 0)
369*7bc93821STakashi Sakamoto 		goto err_unconnect;
37031ef9134SClemens Ladisch 
37131ef9134SClemens Ladisch 	mutex_unlock(&c->mutex);
37231ef9134SClemens Ladisch 
37331ef9134SClemens Ladisch 	return 0;
37431ef9134SClemens Ladisch 
37531ef9134SClemens Ladisch err_unconnect:
37631ef9134SClemens Ladisch 	c->connected = false;
37731ef9134SClemens Ladisch 	mutex_unlock(&c->mutex);
37831ef9134SClemens Ladisch 
37931ef9134SClemens Ladisch 	return err;
38031ef9134SClemens Ladisch }
38131ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_update);
38231ef9134SClemens Ladisch 
pcr_break_modify(struct cmp_connection * c,__be32 pcr)383a7fa0d04STakashi Sakamoto static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
38431ef9134SClemens Ladisch {
385a7fa0d04STakashi Sakamoto 	return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
38631ef9134SClemens Ladisch }
38731ef9134SClemens Ladisch 
38831ef9134SClemens Ladisch /**
38931ef9134SClemens Ladisch  * cmp_connection_break - break the connection to the target
39031ef9134SClemens Ladisch  * @c: the connection manager
39131ef9134SClemens Ladisch  *
392a7fa0d04STakashi Sakamoto  * This function deactives the connection in the target's input/output plug
393a7fa0d04STakashi Sakamoto  * control register, and frees the isochronous resources of the connection.
394a7fa0d04STakashi Sakamoto  * Before calling this function, the caller should cease transmitting packets.
39531ef9134SClemens Ladisch  */
cmp_connection_break(struct cmp_connection * c)39631ef9134SClemens Ladisch void cmp_connection_break(struct cmp_connection *c)
39731ef9134SClemens Ladisch {
39831ef9134SClemens Ladisch 	int err;
39931ef9134SClemens Ladisch 
40031ef9134SClemens Ladisch 	mutex_lock(&c->mutex);
40131ef9134SClemens Ladisch 
40231ef9134SClemens Ladisch 	if (!c->connected) {
40331ef9134SClemens Ladisch 		mutex_unlock(&c->mutex);
40431ef9134SClemens Ladisch 		return;
40531ef9134SClemens Ladisch 	}
40631ef9134SClemens Ladisch 
407a7fa0d04STakashi Sakamoto 	err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
40831ef9134SClemens Ladisch 	if (err < 0)
40931ef9134SClemens Ladisch 		cmp_error(c, "plug is still connected\n");
41031ef9134SClemens Ladisch 
41131ef9134SClemens Ladisch 	c->connected = false;
41231ef9134SClemens Ladisch 
41331ef9134SClemens Ladisch 	mutex_unlock(&c->mutex);
41431ef9134SClemens Ladisch }
41531ef9134SClemens Ladisch EXPORT_SYMBOL(cmp_connection_break);
416