xref: /openbmc/linux/sound/firewire/iso-resources.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*da607e19SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
231ef9134SClemens Ladisch /*
331ef9134SClemens Ladisch  * isochronous resources 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>
11d81a6d71SPaul Gortmaker #include <linux/export.h>
1231ef9134SClemens Ladisch #include <linux/jiffies.h>
1331ef9134SClemens Ladisch #include <linux/mutex.h>
1431ef9134SClemens Ladisch #include <linux/sched.h>
1531ef9134SClemens Ladisch #include <linux/spinlock.h>
1631ef9134SClemens Ladisch #include "iso-resources.h"
1731ef9134SClemens Ladisch 
1831ef9134SClemens Ladisch /**
1931ef9134SClemens Ladisch  * fw_iso_resources_init - initializes a &struct fw_iso_resources
2031ef9134SClemens Ladisch  * @r: the resource manager to initialize
2131ef9134SClemens Ladisch  * @unit: the device unit for which the resources will be needed
2231ef9134SClemens Ladisch  *
2331ef9134SClemens Ladisch  * If the device does not support all channel numbers, change @r->channels_mask
2431ef9134SClemens Ladisch  * after calling this function.
2531ef9134SClemens Ladisch  */
fw_iso_resources_init(struct fw_iso_resources * r,struct fw_unit * unit)265b2599a0SClemens Ladisch int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
2731ef9134SClemens Ladisch {
2831ef9134SClemens Ladisch 	r->channels_mask = ~0uLL;
2959294a01STakashi Sakamoto 	r->unit = unit;
3031ef9134SClemens Ladisch 	mutex_init(&r->mutex);
3131ef9134SClemens Ladisch 	r->allocated = false;
325b2599a0SClemens Ladisch 
335b2599a0SClemens Ladisch 	return 0;
3431ef9134SClemens Ladisch }
353a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_init);
3631ef9134SClemens Ladisch 
3731ef9134SClemens Ladisch /**
3831ef9134SClemens Ladisch  * fw_iso_resources_destroy - destroy a resource manager
3931ef9134SClemens Ladisch  * @r: the resource manager that is no longer needed
4031ef9134SClemens Ladisch  */
fw_iso_resources_destroy(struct fw_iso_resources * r)4131ef9134SClemens Ladisch void fw_iso_resources_destroy(struct fw_iso_resources *r)
4231ef9134SClemens Ladisch {
4331ef9134SClemens Ladisch 	WARN_ON(r->allocated);
4431ef9134SClemens Ladisch 	mutex_destroy(&r->mutex);
4531ef9134SClemens Ladisch }
463a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_destroy);
4731ef9134SClemens Ladisch 
packet_bandwidth(unsigned int max_payload_bytes,int speed)4831ef9134SClemens Ladisch static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed)
4931ef9134SClemens Ladisch {
5031ef9134SClemens Ladisch 	unsigned int bytes, s400_bytes;
5131ef9134SClemens Ladisch 
5231ef9134SClemens Ladisch 	/* iso packets have three header quadlets and quadlet-aligned payload */
5331ef9134SClemens Ladisch 	bytes = 3 * 4 + ALIGN(max_payload_bytes, 4);
5431ef9134SClemens Ladisch 
5531ef9134SClemens Ladisch 	/* convert to bandwidth units (quadlets at S1600 = bytes at S400) */
5631ef9134SClemens Ladisch 	if (speed <= SCODE_400)
5731ef9134SClemens Ladisch 		s400_bytes = bytes * (1 << (SCODE_400 - speed));
5831ef9134SClemens Ladisch 	else
5931ef9134SClemens Ladisch 		s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400));
6031ef9134SClemens Ladisch 
6131ef9134SClemens Ladisch 	return s400_bytes;
6231ef9134SClemens Ladisch }
6331ef9134SClemens Ladisch 
current_bandwidth_overhead(struct fw_card * card)6431ef9134SClemens Ladisch static int current_bandwidth_overhead(struct fw_card *card)
6531ef9134SClemens Ladisch {
6631ef9134SClemens Ladisch 	/*
6731ef9134SClemens Ladisch 	 * Under the usual pessimistic assumption (cable length 4.5 m), the
6831ef9134SClemens Ladisch 	 * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or
6931ef9134SClemens Ladisch 	 * 88.3 + N * 24.3 in bandwidth units.
7031ef9134SClemens Ladisch 	 *
7131ef9134SClemens Ladisch 	 * The calculation below tries to deduce N from the current gap count.
7231ef9134SClemens Ladisch 	 * If the gap count has been optimized by measuring the actual packet
7331ef9134SClemens Ladisch 	 * transmission time, this derived overhead should be near the actual
7431ef9134SClemens Ladisch 	 * overhead as well.
7531ef9134SClemens Ladisch 	 */
7631ef9134SClemens Ladisch 	return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512;
7731ef9134SClemens Ladisch }
7831ef9134SClemens Ladisch 
wait_isoch_resource_delay_after_bus_reset(struct fw_card * card)7931ef9134SClemens Ladisch static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card)
8031ef9134SClemens Ladisch {
8131ef9134SClemens Ladisch 	for (;;) {
8231ef9134SClemens Ladisch 		s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64();
8331ef9134SClemens Ladisch 		if (delay <= 0)
8431ef9134SClemens Ladisch 			return 0;
8531ef9134SClemens Ladisch 		if (schedule_timeout_interruptible(delay) > 0)
8631ef9134SClemens Ladisch 			return -ERESTARTSYS;
8731ef9134SClemens Ladisch 	}
8831ef9134SClemens Ladisch }
8931ef9134SClemens Ladisch 
9031ef9134SClemens Ladisch /**
9131ef9134SClemens Ladisch  * fw_iso_resources_allocate - allocate isochronous channel and bandwidth
9231ef9134SClemens Ladisch  * @r: the resource manager
9331ef9134SClemens Ladisch  * @max_payload_bytes: the amount of data (including CIP headers) per packet
9431ef9134SClemens Ladisch  * @speed: the speed (e.g., SCODE_400) at which the packets will be sent
9531ef9134SClemens Ladisch  *
9631ef9134SClemens Ladisch  * This function allocates one isochronous channel and enough bandwidth for the
9731ef9134SClemens Ladisch  * specified packet size.
9831ef9134SClemens Ladisch  *
9931ef9134SClemens Ladisch  * Returns the channel number that the caller must use for streaming, or
10031ef9134SClemens Ladisch  * a negative error code.  Due to potentionally long delays, this function is
10131ef9134SClemens Ladisch  * interruptible and can return -ERESTARTSYS.  On success, the caller is
10231ef9134SClemens Ladisch  * responsible for calling fw_iso_resources_update() on bus resets, and
10331ef9134SClemens Ladisch  * fw_iso_resources_free() when the resources are not longer needed.
10431ef9134SClemens Ladisch  */
fw_iso_resources_allocate(struct fw_iso_resources * r,unsigned int max_payload_bytes,int speed)10531ef9134SClemens Ladisch int fw_iso_resources_allocate(struct fw_iso_resources *r,
10631ef9134SClemens Ladisch 			      unsigned int max_payload_bytes, int speed)
10731ef9134SClemens Ladisch {
10831ef9134SClemens Ladisch 	struct fw_card *card = fw_parent_device(r->unit)->card;
10931ef9134SClemens Ladisch 	int bandwidth, channel, err;
11031ef9134SClemens Ladisch 
11131ef9134SClemens Ladisch 	if (WARN_ON(r->allocated))
11231ef9134SClemens Ladisch 		return -EBADFD;
11331ef9134SClemens Ladisch 
11431ef9134SClemens Ladisch 	r->bandwidth = packet_bandwidth(max_payload_bytes, speed);
11531ef9134SClemens Ladisch 
11631ef9134SClemens Ladisch retry_after_bus_reset:
11731ef9134SClemens Ladisch 	spin_lock_irq(&card->lock);
11831ef9134SClemens Ladisch 	r->generation = card->generation;
11931ef9134SClemens Ladisch 	r->bandwidth_overhead = current_bandwidth_overhead(card);
12031ef9134SClemens Ladisch 	spin_unlock_irq(&card->lock);
12131ef9134SClemens Ladisch 
12231ef9134SClemens Ladisch 	err = wait_isoch_resource_delay_after_bus_reset(card);
12331ef9134SClemens Ladisch 	if (err < 0)
12431ef9134SClemens Ladisch 		return err;
12531ef9134SClemens Ladisch 
12631ef9134SClemens Ladisch 	mutex_lock(&r->mutex);
12731ef9134SClemens Ladisch 
12831ef9134SClemens Ladisch 	bandwidth = r->bandwidth + r->bandwidth_overhead;
12931ef9134SClemens Ladisch 	fw_iso_resource_manage(card, r->generation, r->channels_mask,
130f30e6d3eSStefan Richter 			       &channel, &bandwidth, true);
13131ef9134SClemens Ladisch 	if (channel == -EAGAIN) {
13231ef9134SClemens Ladisch 		mutex_unlock(&r->mutex);
13331ef9134SClemens Ladisch 		goto retry_after_bus_reset;
13431ef9134SClemens Ladisch 	}
13531ef9134SClemens Ladisch 	if (channel >= 0) {
13631ef9134SClemens Ladisch 		r->channel = channel;
13731ef9134SClemens Ladisch 		r->allocated = true;
13831ef9134SClemens Ladisch 	} else {
13931ef9134SClemens Ladisch 		if (channel == -EBUSY)
14031ef9134SClemens Ladisch 			dev_err(&r->unit->device,
14131ef9134SClemens Ladisch 				"isochronous resources exhausted\n");
14231ef9134SClemens Ladisch 		else
14331ef9134SClemens Ladisch 			dev_err(&r->unit->device,
14431ef9134SClemens Ladisch 				"isochronous resource allocation failed\n");
14531ef9134SClemens Ladisch 	}
14631ef9134SClemens Ladisch 
14731ef9134SClemens Ladisch 	mutex_unlock(&r->mutex);
14831ef9134SClemens Ladisch 
14931ef9134SClemens Ladisch 	return channel;
15031ef9134SClemens Ladisch }
1513a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_allocate);
15231ef9134SClemens Ladisch 
15331ef9134SClemens Ladisch /**
15431ef9134SClemens Ladisch  * fw_iso_resources_update - update resource allocations after a bus reset
15531ef9134SClemens Ladisch  * @r: the resource manager
15631ef9134SClemens Ladisch  *
15731ef9134SClemens Ladisch  * This function must be called from the driver's .update handler to reallocate
15831ef9134SClemens Ladisch  * any resources that were allocated before the bus reset.  It is safe to call
15931ef9134SClemens Ladisch  * this function if no resources are currently allocated.
16031ef9134SClemens Ladisch  *
16131ef9134SClemens Ladisch  * Returns a negative error code on failure.  If this happens, the caller must
16231ef9134SClemens Ladisch  * stop streaming.
16331ef9134SClemens Ladisch  */
fw_iso_resources_update(struct fw_iso_resources * r)16431ef9134SClemens Ladisch int fw_iso_resources_update(struct fw_iso_resources *r)
16531ef9134SClemens Ladisch {
16631ef9134SClemens Ladisch 	struct fw_card *card = fw_parent_device(r->unit)->card;
16731ef9134SClemens Ladisch 	int bandwidth, channel;
16831ef9134SClemens Ladisch 
16931ef9134SClemens Ladisch 	mutex_lock(&r->mutex);
17031ef9134SClemens Ladisch 
17131ef9134SClemens Ladisch 	if (!r->allocated) {
17231ef9134SClemens Ladisch 		mutex_unlock(&r->mutex);
17331ef9134SClemens Ladisch 		return 0;
17431ef9134SClemens Ladisch 	}
17531ef9134SClemens Ladisch 
17631ef9134SClemens Ladisch 	spin_lock_irq(&card->lock);
17731ef9134SClemens Ladisch 	r->generation = card->generation;
17831ef9134SClemens Ladisch 	r->bandwidth_overhead = current_bandwidth_overhead(card);
17931ef9134SClemens Ladisch 	spin_unlock_irq(&card->lock);
18031ef9134SClemens Ladisch 
18131ef9134SClemens Ladisch 	bandwidth = r->bandwidth + r->bandwidth_overhead;
18231ef9134SClemens Ladisch 
18331ef9134SClemens Ladisch 	fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
184f30e6d3eSStefan Richter 			       &channel, &bandwidth, true);
18531ef9134SClemens Ladisch 	/*
18631ef9134SClemens Ladisch 	 * When another bus reset happens, pretend that the allocation
18731ef9134SClemens Ladisch 	 * succeeded; we will try again for the new generation later.
18831ef9134SClemens Ladisch 	 */
18931ef9134SClemens Ladisch 	if (channel < 0 && channel != -EAGAIN) {
19031ef9134SClemens Ladisch 		r->allocated = false;
19131ef9134SClemens Ladisch 		if (channel == -EBUSY)
19231ef9134SClemens Ladisch 			dev_err(&r->unit->device,
19331ef9134SClemens Ladisch 				"isochronous resources exhausted\n");
19431ef9134SClemens Ladisch 		else
19531ef9134SClemens Ladisch 			dev_err(&r->unit->device,
19631ef9134SClemens Ladisch 				"isochronous resource allocation failed\n");
19731ef9134SClemens Ladisch 	}
19831ef9134SClemens Ladisch 
19931ef9134SClemens Ladisch 	mutex_unlock(&r->mutex);
20031ef9134SClemens Ladisch 
20131ef9134SClemens Ladisch 	return channel;
20231ef9134SClemens Ladisch }
2033a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_update);
20431ef9134SClemens Ladisch 
20531ef9134SClemens Ladisch /**
20631ef9134SClemens Ladisch  * fw_iso_resources_free - frees allocated resources
20731ef9134SClemens Ladisch  * @r: the resource manager
20831ef9134SClemens Ladisch  *
20931ef9134SClemens Ladisch  * This function deallocates the channel and bandwidth, if allocated.
21031ef9134SClemens Ladisch  */
fw_iso_resources_free(struct fw_iso_resources * r)21131ef9134SClemens Ladisch void fw_iso_resources_free(struct fw_iso_resources *r)
21231ef9134SClemens Ladisch {
2130c264af7STakashi Sakamoto 	struct fw_card *card;
21431ef9134SClemens Ladisch 	int bandwidth, channel;
21531ef9134SClemens Ladisch 
2160c264af7STakashi Sakamoto 	/* Not initialized. */
2170c264af7STakashi Sakamoto 	if (r->unit == NULL)
2180c264af7STakashi Sakamoto 		return;
2190c264af7STakashi Sakamoto 	card = fw_parent_device(r->unit)->card;
2200c264af7STakashi Sakamoto 
22131ef9134SClemens Ladisch 	mutex_lock(&r->mutex);
22231ef9134SClemens Ladisch 
22331ef9134SClemens Ladisch 	if (r->allocated) {
22431ef9134SClemens Ladisch 		bandwidth = r->bandwidth + r->bandwidth_overhead;
22531ef9134SClemens Ladisch 		fw_iso_resource_manage(card, r->generation, 1uLL << r->channel,
226f30e6d3eSStefan Richter 				       &channel, &bandwidth, false);
22731ef9134SClemens Ladisch 		if (channel < 0)
22831ef9134SClemens Ladisch 			dev_err(&r->unit->device,
22931ef9134SClemens Ladisch 				"isochronous resource deallocation failed\n");
23031ef9134SClemens Ladisch 
23131ef9134SClemens Ladisch 		r->allocated = false;
23231ef9134SClemens Ladisch 	}
23331ef9134SClemens Ladisch 
23431ef9134SClemens Ladisch 	mutex_unlock(&r->mutex);
23531ef9134SClemens Ladisch }
2363a691b28SClemens Ladisch EXPORT_SYMBOL(fw_iso_resources_free);
237