18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
48c2ecf20Sopenharmony_ci * with Common Isochronous Packet (IEC 61883-1) headers
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/firewire.h>
128c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <sound/pcm.h>
168c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
178c2ecf20Sopenharmony_ci#include "amdtp-stream.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define TICKS_PER_CYCLE		3072
208c2ecf20Sopenharmony_ci#define CYCLES_PER_SECOND	8000
218c2ecf20Sopenharmony_ci#define TICKS_PER_SECOND	(TICKS_PER_CYCLE * CYCLES_PER_SECOND)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define OHCI_MAX_SECOND		8
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Always support Linux tracing subsystem. */
268c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
278c2ecf20Sopenharmony_ci#include "amdtp-stream-trace.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define TRANSFER_DELAY_TICKS	0x2e00 /* 479.17 microseconds */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* isochronous header parameters */
328c2ecf20Sopenharmony_ci#define ISO_DATA_LENGTH_SHIFT	16
338c2ecf20Sopenharmony_ci#define TAG_NO_CIP_HEADER	0
348c2ecf20Sopenharmony_ci#define TAG_CIP			1
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* common isochronous packet header parameters */
378c2ecf20Sopenharmony_ci#define CIP_EOH_SHIFT		31
388c2ecf20Sopenharmony_ci#define CIP_EOH			(1u << CIP_EOH_SHIFT)
398c2ecf20Sopenharmony_ci#define CIP_EOH_MASK		0x80000000
408c2ecf20Sopenharmony_ci#define CIP_SID_SHIFT		24
418c2ecf20Sopenharmony_ci#define CIP_SID_MASK		0x3f000000
428c2ecf20Sopenharmony_ci#define CIP_DBS_MASK		0x00ff0000
438c2ecf20Sopenharmony_ci#define CIP_DBS_SHIFT		16
448c2ecf20Sopenharmony_ci#define CIP_SPH_MASK		0x00000400
458c2ecf20Sopenharmony_ci#define CIP_SPH_SHIFT		10
468c2ecf20Sopenharmony_ci#define CIP_DBC_MASK		0x000000ff
478c2ecf20Sopenharmony_ci#define CIP_FMT_SHIFT		24
488c2ecf20Sopenharmony_ci#define CIP_FMT_MASK		0x3f000000
498c2ecf20Sopenharmony_ci#define CIP_FDF_MASK		0x00ff0000
508c2ecf20Sopenharmony_ci#define CIP_FDF_SHIFT		16
518c2ecf20Sopenharmony_ci#define CIP_SYT_MASK		0x0000ffff
528c2ecf20Sopenharmony_ci#define CIP_SYT_NO_INFO		0xffff
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* Audio and Music transfer protocol specific parameters */
558c2ecf20Sopenharmony_ci#define CIP_FMT_AM		0x10
568c2ecf20Sopenharmony_ci#define AMDTP_FDF_NO_DATA	0xff
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci// For iso header, tstamp and 2 CIP header.
598c2ecf20Sopenharmony_ci#define IR_CTX_HEADER_SIZE_CIP		16
608c2ecf20Sopenharmony_ci// For iso header and tstamp.
618c2ecf20Sopenharmony_ci#define IR_CTX_HEADER_SIZE_NO_CIP	8
628c2ecf20Sopenharmony_ci#define HEADER_TSTAMP_MASK	0x0000ffff
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define IT_PKT_HEADER_SIZE_CIP		8 // For 2 CIP header.
658c2ecf20Sopenharmony_ci#define IT_PKT_HEADER_SIZE_NO_CIP	0 // Nothing.
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void pcm_period_work(struct work_struct *work);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/**
708c2ecf20Sopenharmony_ci * amdtp_stream_init - initialize an AMDTP stream structure
718c2ecf20Sopenharmony_ci * @s: the AMDTP stream to initialize
728c2ecf20Sopenharmony_ci * @unit: the target of the stream
738c2ecf20Sopenharmony_ci * @dir: the direction of stream
748c2ecf20Sopenharmony_ci * @flags: the packet transmission method to use
758c2ecf20Sopenharmony_ci * @fmt: the value of fmt field in CIP header
768c2ecf20Sopenharmony_ci * @process_ctx_payloads: callback handler to process payloads of isoc context
778c2ecf20Sopenharmony_ci * @protocol_size: the size to allocate newly for protocol
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_ciint amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
808c2ecf20Sopenharmony_ci		      enum amdtp_stream_direction dir, enum cip_flags flags,
818c2ecf20Sopenharmony_ci		      unsigned int fmt,
828c2ecf20Sopenharmony_ci		      amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
838c2ecf20Sopenharmony_ci		      unsigned int protocol_size)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	if (process_ctx_payloads == NULL)
868c2ecf20Sopenharmony_ci		return -EINVAL;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	s->protocol = kzalloc(protocol_size, GFP_KERNEL);
898c2ecf20Sopenharmony_ci	if (!s->protocol)
908c2ecf20Sopenharmony_ci		return -ENOMEM;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	s->unit = unit;
938c2ecf20Sopenharmony_ci	s->direction = dir;
948c2ecf20Sopenharmony_ci	s->flags = flags;
958c2ecf20Sopenharmony_ci	s->context = ERR_PTR(-1);
968c2ecf20Sopenharmony_ci	mutex_init(&s->mutex);
978c2ecf20Sopenharmony_ci	INIT_WORK(&s->period_work, pcm_period_work);
988c2ecf20Sopenharmony_ci	s->packet_index = 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	init_waitqueue_head(&s->callback_wait);
1018c2ecf20Sopenharmony_ci	s->callbacked = false;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	s->fmt = fmt;
1048c2ecf20Sopenharmony_ci	s->process_ctx_payloads = process_ctx_payloads;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (dir == AMDTP_OUT_STREAM)
1078c2ecf20Sopenharmony_ci		s->ctx_data.rx.syt_override = -1;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_init);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/**
1148c2ecf20Sopenharmony_ci * amdtp_stream_destroy - free stream resources
1158c2ecf20Sopenharmony_ci * @s: the AMDTP stream to destroy
1168c2ecf20Sopenharmony_ci */
1178c2ecf20Sopenharmony_civoid amdtp_stream_destroy(struct amdtp_stream *s)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	/* Not initialized. */
1208c2ecf20Sopenharmony_ci	if (s->protocol == NULL)
1218c2ecf20Sopenharmony_ci		return;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	WARN_ON(amdtp_stream_running(s));
1248c2ecf20Sopenharmony_ci	kfree(s->protocol);
1258c2ecf20Sopenharmony_ci	mutex_destroy(&s->mutex);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_destroy);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ciconst unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
1308c2ecf20Sopenharmony_ci	[CIP_SFC_32000]  =  8,
1318c2ecf20Sopenharmony_ci	[CIP_SFC_44100]  =  8,
1328c2ecf20Sopenharmony_ci	[CIP_SFC_48000]  =  8,
1338c2ecf20Sopenharmony_ci	[CIP_SFC_88200]  = 16,
1348c2ecf20Sopenharmony_ci	[CIP_SFC_96000]  = 16,
1358c2ecf20Sopenharmony_ci	[CIP_SFC_176400] = 32,
1368c2ecf20Sopenharmony_ci	[CIP_SFC_192000] = 32,
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_syt_intervals);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciconst unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
1418c2ecf20Sopenharmony_ci	[CIP_SFC_32000]  =  32000,
1428c2ecf20Sopenharmony_ci	[CIP_SFC_44100]  =  44100,
1438c2ecf20Sopenharmony_ci	[CIP_SFC_48000]  =  48000,
1448c2ecf20Sopenharmony_ci	[CIP_SFC_88200]  =  88200,
1458c2ecf20Sopenharmony_ci	[CIP_SFC_96000]  =  96000,
1468c2ecf20Sopenharmony_ci	[CIP_SFC_176400] = 176400,
1478c2ecf20Sopenharmony_ci	[CIP_SFC_192000] = 192000,
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_rate_table);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int apply_constraint_to_size(struct snd_pcm_hw_params *params,
1528c2ecf20Sopenharmony_ci				    struct snd_pcm_hw_rule *rule)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct snd_interval *s = hw_param_interval(params, rule->var);
1558c2ecf20Sopenharmony_ci	const struct snd_interval *r =
1568c2ecf20Sopenharmony_ci		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
1578c2ecf20Sopenharmony_ci	struct snd_interval t = {0};
1588c2ecf20Sopenharmony_ci	unsigned int step = 0;
1598c2ecf20Sopenharmony_ci	int i;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	for (i = 0; i < CIP_SFC_COUNT; ++i) {
1628c2ecf20Sopenharmony_ci		if (snd_interval_test(r, amdtp_rate_table[i]))
1638c2ecf20Sopenharmony_ci			step = max(step, amdtp_syt_intervals[i]);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	t.min = roundup(s->min, step);
1678c2ecf20Sopenharmony_ci	t.max = rounddown(s->max, step);
1688c2ecf20Sopenharmony_ci	t.integer = 1;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return snd_interval_refine(s, &t);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci/**
1748c2ecf20Sopenharmony_ci * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
1758c2ecf20Sopenharmony_ci * @s:		the AMDTP stream, which must be initialized.
1768c2ecf20Sopenharmony_ci * @runtime:	the PCM substream runtime
1778c2ecf20Sopenharmony_ci */
1788c2ecf20Sopenharmony_ciint amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
1798c2ecf20Sopenharmony_ci					struct snd_pcm_runtime *runtime)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct snd_pcm_hardware *hw = &runtime->hw;
1828c2ecf20Sopenharmony_ci	unsigned int ctx_header_size;
1838c2ecf20Sopenharmony_ci	unsigned int maximum_usec_per_period;
1848c2ecf20Sopenharmony_ci	int err;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	hw->info = SNDRV_PCM_INFO_BATCH |
1878c2ecf20Sopenharmony_ci		   SNDRV_PCM_INFO_BLOCK_TRANSFER |
1888c2ecf20Sopenharmony_ci		   SNDRV_PCM_INFO_INTERLEAVED |
1898c2ecf20Sopenharmony_ci		   SNDRV_PCM_INFO_JOINT_DUPLEX |
1908c2ecf20Sopenharmony_ci		   SNDRV_PCM_INFO_MMAP |
1918c2ecf20Sopenharmony_ci		   SNDRV_PCM_INFO_MMAP_VALID;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* SNDRV_PCM_INFO_BATCH */
1948c2ecf20Sopenharmony_ci	hw->periods_min = 2;
1958c2ecf20Sopenharmony_ci	hw->periods_max = UINT_MAX;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* bytes for a frame */
1988c2ecf20Sopenharmony_ci	hw->period_bytes_min = 4 * hw->channels_max;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* Just to prevent from allocating much pages. */
2018c2ecf20Sopenharmony_ci	hw->period_bytes_max = hw->period_bytes_min * 2048;
2028c2ecf20Sopenharmony_ci	hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	// Linux driver for 1394 OHCI controller voluntarily flushes isoc
2058c2ecf20Sopenharmony_ci	// context when total size of accumulated context header reaches
2068c2ecf20Sopenharmony_ci	// PAGE_SIZE. This kicks work for the isoc context and brings
2078c2ecf20Sopenharmony_ci	// callback in the middle of scheduled interrupts.
2088c2ecf20Sopenharmony_ci	// Although AMDTP streams in the same domain use the same events per
2098c2ecf20Sopenharmony_ci	// IRQ, use the largest size of context header between IT/IR contexts.
2108c2ecf20Sopenharmony_ci	// Here, use the value of context header in IR context is for both
2118c2ecf20Sopenharmony_ci	// contexts.
2128c2ecf20Sopenharmony_ci	if (!(s->flags & CIP_NO_HEADER))
2138c2ecf20Sopenharmony_ci		ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
2148c2ecf20Sopenharmony_ci	else
2158c2ecf20Sopenharmony_ci		ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
2168c2ecf20Sopenharmony_ci	maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE /
2178c2ecf20Sopenharmony_ci				  CYCLES_PER_SECOND / ctx_header_size;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	// In IEC 61883-6, one isoc packet can transfer events up to the value
2208c2ecf20Sopenharmony_ci	// of syt interval. This comes from the interval of isoc cycle. As 1394
2218c2ecf20Sopenharmony_ci	// OHCI controller can generate hardware IRQ per isoc packet, the
2228c2ecf20Sopenharmony_ci	// interval is 125 usec.
2238c2ecf20Sopenharmony_ci	// However, there are two ways of transmission in IEC 61883-6; blocking
2248c2ecf20Sopenharmony_ci	// and non-blocking modes. In blocking mode, the sequence of isoc packet
2258c2ecf20Sopenharmony_ci	// includes 'empty' or 'NODATA' packets which include no event. In
2268c2ecf20Sopenharmony_ci	// non-blocking mode, the number of events per packet is variable up to
2278c2ecf20Sopenharmony_ci	// the syt interval.
2288c2ecf20Sopenharmony_ci	// Due to the above protocol design, the minimum PCM frames per
2298c2ecf20Sopenharmony_ci	// interrupt should be double of the value of syt interval, thus it is
2308c2ecf20Sopenharmony_ci	// 250 usec.
2318c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_minmax(runtime,
2328c2ecf20Sopenharmony_ci					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
2338c2ecf20Sopenharmony_ci					   250, maximum_usec_per_period);
2348c2ecf20Sopenharmony_ci	if (err < 0)
2358c2ecf20Sopenharmony_ci		goto end;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Non-Blocking stream has no more constraints */
2388c2ecf20Sopenharmony_ci	if (!(s->flags & CIP_BLOCKING))
2398c2ecf20Sopenharmony_ci		goto end;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/*
2428c2ecf20Sopenharmony_ci	 * One AMDTP packet can include some frames. In blocking mode, the
2438c2ecf20Sopenharmony_ci	 * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
2448c2ecf20Sopenharmony_ci	 * depending on its sampling rate. For accurate period interrupt, it's
2458c2ecf20Sopenharmony_ci	 * preferrable to align period/buffer sizes to current SYT_INTERVAL.
2468c2ecf20Sopenharmony_ci	 */
2478c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
2488c2ecf20Sopenharmony_ci				  apply_constraint_to_size, NULL,
2498c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
2508c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE, -1);
2518c2ecf20Sopenharmony_ci	if (err < 0)
2528c2ecf20Sopenharmony_ci		goto end;
2538c2ecf20Sopenharmony_ci	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
2548c2ecf20Sopenharmony_ci				  apply_constraint_to_size, NULL,
2558c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
2568c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE, -1);
2578c2ecf20Sopenharmony_ci	if (err < 0)
2588c2ecf20Sopenharmony_ci		goto end;
2598c2ecf20Sopenharmony_ciend:
2608c2ecf20Sopenharmony_ci	return err;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/**
2658c2ecf20Sopenharmony_ci * amdtp_stream_set_parameters - set stream parameters
2668c2ecf20Sopenharmony_ci * @s: the AMDTP stream to configure
2678c2ecf20Sopenharmony_ci * @rate: the sample rate
2688c2ecf20Sopenharmony_ci * @data_block_quadlets: the size of a data block in quadlet unit
2698c2ecf20Sopenharmony_ci *
2708c2ecf20Sopenharmony_ci * The parameters must be set before the stream is started, and must not be
2718c2ecf20Sopenharmony_ci * changed while the stream is running.
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_ciint amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
2748c2ecf20Sopenharmony_ci				unsigned int data_block_quadlets)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	unsigned int sfc;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
2798c2ecf20Sopenharmony_ci		if (amdtp_rate_table[sfc] == rate)
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci	if (sfc == ARRAY_SIZE(amdtp_rate_table))
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	s->sfc = sfc;
2868c2ecf20Sopenharmony_ci	s->data_block_quadlets = data_block_quadlets;
2878c2ecf20Sopenharmony_ci	s->syt_interval = amdtp_syt_intervals[sfc];
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	// default buffering in the device.
2908c2ecf20Sopenharmony_ci	if (s->direction == AMDTP_OUT_STREAM) {
2918c2ecf20Sopenharmony_ci		s->ctx_data.rx.transfer_delay =
2928c2ecf20Sopenharmony_ci					TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		if (s->flags & CIP_BLOCKING) {
2958c2ecf20Sopenharmony_ci			// additional buffering needed to adjust for no-data
2968c2ecf20Sopenharmony_ci			// packets.
2978c2ecf20Sopenharmony_ci			s->ctx_data.rx.transfer_delay +=
2988c2ecf20Sopenharmony_ci				TICKS_PER_SECOND * s->syt_interval / rate;
2998c2ecf20Sopenharmony_ci		}
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	return 0;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_set_parameters);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/**
3078c2ecf20Sopenharmony_ci * amdtp_stream_get_max_payload - get the stream's packet size
3088c2ecf20Sopenharmony_ci * @s: the AMDTP stream
3098c2ecf20Sopenharmony_ci *
3108c2ecf20Sopenharmony_ci * This function must not be called before the stream has been configured
3118c2ecf20Sopenharmony_ci * with amdtp_stream_set_parameters().
3128c2ecf20Sopenharmony_ci */
3138c2ecf20Sopenharmony_ciunsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	unsigned int multiplier = 1;
3168c2ecf20Sopenharmony_ci	unsigned int cip_header_size = 0;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (s->flags & CIP_JUMBO_PAYLOAD)
3198c2ecf20Sopenharmony_ci		multiplier = 5;
3208c2ecf20Sopenharmony_ci	if (!(s->flags & CIP_NO_HEADER))
3218c2ecf20Sopenharmony_ci		cip_header_size = sizeof(__be32) * 2;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return cip_header_size +
3248c2ecf20Sopenharmony_ci		s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_get_max_payload);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci/**
3298c2ecf20Sopenharmony_ci * amdtp_stream_pcm_prepare - prepare PCM device for running
3308c2ecf20Sopenharmony_ci * @s: the AMDTP stream
3318c2ecf20Sopenharmony_ci *
3328c2ecf20Sopenharmony_ci * This function should be called from the PCM device's .prepare callback.
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_civoid amdtp_stream_pcm_prepare(struct amdtp_stream *s)
3358c2ecf20Sopenharmony_ci{
3368c2ecf20Sopenharmony_ci	cancel_work_sync(&s->period_work);
3378c2ecf20Sopenharmony_ci	s->pcm_buffer_pointer = 0;
3388c2ecf20Sopenharmony_ci	s->pcm_period_pointer = 0;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_pcm_prepare);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic unsigned int calculate_data_blocks(unsigned int *data_block_state,
3438c2ecf20Sopenharmony_ci				bool is_blocking, bool is_no_info,
3448c2ecf20Sopenharmony_ci				unsigned int syt_interval, enum cip_sfc sfc)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	unsigned int data_blocks;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* Blocking mode. */
3498c2ecf20Sopenharmony_ci	if (is_blocking) {
3508c2ecf20Sopenharmony_ci		/* This module generate empty packet for 'no data'. */
3518c2ecf20Sopenharmony_ci		if (is_no_info)
3528c2ecf20Sopenharmony_ci			data_blocks = 0;
3538c2ecf20Sopenharmony_ci		else
3548c2ecf20Sopenharmony_ci			data_blocks = syt_interval;
3558c2ecf20Sopenharmony_ci	/* Non-blocking mode. */
3568c2ecf20Sopenharmony_ci	} else {
3578c2ecf20Sopenharmony_ci		if (!cip_sfc_is_base_44100(sfc)) {
3588c2ecf20Sopenharmony_ci			// Sample_rate / 8000 is an integer, and precomputed.
3598c2ecf20Sopenharmony_ci			data_blocks = *data_block_state;
3608c2ecf20Sopenharmony_ci		} else {
3618c2ecf20Sopenharmony_ci			unsigned int phase = *data_block_state;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci		/*
3648c2ecf20Sopenharmony_ci		 * This calculates the number of data blocks per packet so that
3658c2ecf20Sopenharmony_ci		 * 1) the overall rate is correct and exactly synchronized to
3668c2ecf20Sopenharmony_ci		 *    the bus clock, and
3678c2ecf20Sopenharmony_ci		 * 2) packets with a rounded-up number of blocks occur as early
3688c2ecf20Sopenharmony_ci		 *    as possible in the sequence (to prevent underruns of the
3698c2ecf20Sopenharmony_ci		 *    device's buffer).
3708c2ecf20Sopenharmony_ci		 */
3718c2ecf20Sopenharmony_ci			if (sfc == CIP_SFC_44100)
3728c2ecf20Sopenharmony_ci				/* 6 6 5 6 5 6 5 ... */
3738c2ecf20Sopenharmony_ci				data_blocks = 5 + ((phase & 1) ^
3748c2ecf20Sopenharmony_ci						   (phase == 0 || phase >= 40));
3758c2ecf20Sopenharmony_ci			else
3768c2ecf20Sopenharmony_ci				/* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
3778c2ecf20Sopenharmony_ci				data_blocks = 11 * (sfc >> 1) + (phase == 0);
3788c2ecf20Sopenharmony_ci			if (++phase >= (80 >> (sfc >> 1)))
3798c2ecf20Sopenharmony_ci				phase = 0;
3808c2ecf20Sopenharmony_ci			*data_block_state = phase;
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	return data_blocks;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic unsigned int calculate_syt_offset(unsigned int *last_syt_offset,
3888c2ecf20Sopenharmony_ci			unsigned int *syt_offset_state, enum cip_sfc sfc)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	unsigned int syt_offset;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (*last_syt_offset < TICKS_PER_CYCLE) {
3938c2ecf20Sopenharmony_ci		if (!cip_sfc_is_base_44100(sfc))
3948c2ecf20Sopenharmony_ci			syt_offset = *last_syt_offset + *syt_offset_state;
3958c2ecf20Sopenharmony_ci		else {
3968c2ecf20Sopenharmony_ci		/*
3978c2ecf20Sopenharmony_ci		 * The time, in ticks, of the n'th SYT_INTERVAL sample is:
3988c2ecf20Sopenharmony_ci		 *   n * SYT_INTERVAL * 24576000 / sample_rate
3998c2ecf20Sopenharmony_ci		 * Modulo TICKS_PER_CYCLE, the difference between successive
4008c2ecf20Sopenharmony_ci		 * elements is about 1386.23.  Rounding the results of this
4018c2ecf20Sopenharmony_ci		 * formula to the SYT precision results in a sequence of
4028c2ecf20Sopenharmony_ci		 * differences that begins with:
4038c2ecf20Sopenharmony_ci		 *   1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
4048c2ecf20Sopenharmony_ci		 * This code generates _exactly_ the same sequence.
4058c2ecf20Sopenharmony_ci		 */
4068c2ecf20Sopenharmony_ci			unsigned int phase = *syt_offset_state;
4078c2ecf20Sopenharmony_ci			unsigned int index = phase % 13;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			syt_offset = *last_syt_offset;
4108c2ecf20Sopenharmony_ci			syt_offset += 1386 + ((index && !(index & 3)) ||
4118c2ecf20Sopenharmony_ci					      phase == 146);
4128c2ecf20Sopenharmony_ci			if (++phase >= 147)
4138c2ecf20Sopenharmony_ci				phase = 0;
4148c2ecf20Sopenharmony_ci			*syt_offset_state = phase;
4158c2ecf20Sopenharmony_ci		}
4168c2ecf20Sopenharmony_ci	} else
4178c2ecf20Sopenharmony_ci		syt_offset = *last_syt_offset - TICKS_PER_CYCLE;
4188c2ecf20Sopenharmony_ci	*last_syt_offset = syt_offset;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (syt_offset >= TICKS_PER_CYCLE)
4218c2ecf20Sopenharmony_ci		syt_offset = CIP_SYT_NO_INFO;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return syt_offset;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void update_pcm_pointers(struct amdtp_stream *s,
4278c2ecf20Sopenharmony_ci				struct snd_pcm_substream *pcm,
4288c2ecf20Sopenharmony_ci				unsigned int frames)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	unsigned int ptr;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	ptr = s->pcm_buffer_pointer + frames;
4338c2ecf20Sopenharmony_ci	if (ptr >= pcm->runtime->buffer_size)
4348c2ecf20Sopenharmony_ci		ptr -= pcm->runtime->buffer_size;
4358c2ecf20Sopenharmony_ci	WRITE_ONCE(s->pcm_buffer_pointer, ptr);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	s->pcm_period_pointer += frames;
4388c2ecf20Sopenharmony_ci	if (s->pcm_period_pointer >= pcm->runtime->period_size) {
4398c2ecf20Sopenharmony_ci		s->pcm_period_pointer -= pcm->runtime->period_size;
4408c2ecf20Sopenharmony_ci		queue_work(system_highpri_wq, &s->period_work);
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic void pcm_period_work(struct work_struct *work)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct amdtp_stream *s = container_of(work, struct amdtp_stream,
4478c2ecf20Sopenharmony_ci					      period_work);
4488c2ecf20Sopenharmony_ci	struct snd_pcm_substream *pcm = READ_ONCE(s->pcm);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (pcm)
4518c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(pcm);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params,
4558c2ecf20Sopenharmony_ci			bool sched_irq)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	int err;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	params->interrupt = sched_irq;
4608c2ecf20Sopenharmony_ci	params->tag = s->tag;
4618c2ecf20Sopenharmony_ci	params->sy = 0;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	err = fw_iso_context_queue(s->context, params, &s->buffer.iso_buffer,
4648c2ecf20Sopenharmony_ci				   s->buffer.packets[s->packet_index].offset);
4658c2ecf20Sopenharmony_ci	if (err < 0) {
4668c2ecf20Sopenharmony_ci		dev_err(&s->unit->device, "queueing error: %d\n", err);
4678c2ecf20Sopenharmony_ci		goto end;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (++s->packet_index >= s->queue_size)
4718c2ecf20Sopenharmony_ci		s->packet_index = 0;
4728c2ecf20Sopenharmony_ciend:
4738c2ecf20Sopenharmony_ci	return err;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic inline int queue_out_packet(struct amdtp_stream *s,
4778c2ecf20Sopenharmony_ci				   struct fw_iso_packet *params, bool sched_irq)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	params->skip =
4808c2ecf20Sopenharmony_ci		!!(params->header_length == 0 && params->payload_length == 0);
4818c2ecf20Sopenharmony_ci	return queue_packet(s, params, sched_irq);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic inline int queue_in_packet(struct amdtp_stream *s,
4858c2ecf20Sopenharmony_ci				  struct fw_iso_packet *params)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	// Queue one packet for IR context.
4888c2ecf20Sopenharmony_ci	params->header_length = s->ctx_data.tx.ctx_header_size;
4898c2ecf20Sopenharmony_ci	params->payload_length = s->ctx_data.tx.max_ctx_payload_length;
4908c2ecf20Sopenharmony_ci	params->skip = false;
4918c2ecf20Sopenharmony_ci	return queue_packet(s, params, false);
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2],
4958c2ecf20Sopenharmony_ci			unsigned int data_block_counter, unsigned int syt)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) |
4988c2ecf20Sopenharmony_ci				(s->data_block_quadlets << CIP_DBS_SHIFT) |
4998c2ecf20Sopenharmony_ci				((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
5008c2ecf20Sopenharmony_ci				data_block_counter);
5018c2ecf20Sopenharmony_ci	cip_header[1] = cpu_to_be32(CIP_EOH |
5028c2ecf20Sopenharmony_ci			((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
5038c2ecf20Sopenharmony_ci			((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
5048c2ecf20Sopenharmony_ci			(syt & CIP_SYT_MASK));
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
5088c2ecf20Sopenharmony_ci				struct fw_iso_packet *params,
5098c2ecf20Sopenharmony_ci				unsigned int data_blocks,
5108c2ecf20Sopenharmony_ci				unsigned int data_block_counter,
5118c2ecf20Sopenharmony_ci				unsigned int syt, unsigned int index)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	unsigned int payload_length;
5148c2ecf20Sopenharmony_ci	__be32 *cip_header;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets;
5178c2ecf20Sopenharmony_ci	params->payload_length = payload_length;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (!(s->flags & CIP_NO_HEADER)) {
5208c2ecf20Sopenharmony_ci		cip_header = (__be32 *)params->header;
5218c2ecf20Sopenharmony_ci		generate_cip_header(s, cip_header, data_block_counter, syt);
5228c2ecf20Sopenharmony_ci		params->header_length = 2 * sizeof(__be32);
5238c2ecf20Sopenharmony_ci		payload_length += params->header_length;
5248c2ecf20Sopenharmony_ci	} else {
5258c2ecf20Sopenharmony_ci		cip_header = NULL;
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks,
5298c2ecf20Sopenharmony_ci			   data_block_counter, s->packet_index, index);
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
5338c2ecf20Sopenharmony_ci			    unsigned int payload_length,
5348c2ecf20Sopenharmony_ci			    unsigned int *data_blocks,
5358c2ecf20Sopenharmony_ci			    unsigned int *data_block_counter, unsigned int *syt)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	u32 cip_header[2];
5388c2ecf20Sopenharmony_ci	unsigned int sph;
5398c2ecf20Sopenharmony_ci	unsigned int fmt;
5408c2ecf20Sopenharmony_ci	unsigned int fdf;
5418c2ecf20Sopenharmony_ci	unsigned int dbc;
5428c2ecf20Sopenharmony_ci	bool lost;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	cip_header[0] = be32_to_cpu(buf[0]);
5458c2ecf20Sopenharmony_ci	cip_header[1] = be32_to_cpu(buf[1]);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/*
5488c2ecf20Sopenharmony_ci	 * This module supports 'Two-quadlet CIP header with SYT field'.
5498c2ecf20Sopenharmony_ci	 * For convenience, also check FMT field is AM824 or not.
5508c2ecf20Sopenharmony_ci	 */
5518c2ecf20Sopenharmony_ci	if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
5528c2ecf20Sopenharmony_ci	     ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
5538c2ecf20Sopenharmony_ci	    (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
5548c2ecf20Sopenharmony_ci		dev_info_ratelimited(&s->unit->device,
5558c2ecf20Sopenharmony_ci				"Invalid CIP header for AMDTP: %08X:%08X\n",
5568c2ecf20Sopenharmony_ci				cip_header[0], cip_header[1]);
5578c2ecf20Sopenharmony_ci		return -EAGAIN;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	/* Check valid protocol or not. */
5618c2ecf20Sopenharmony_ci	sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT;
5628c2ecf20Sopenharmony_ci	fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
5638c2ecf20Sopenharmony_ci	if (sph != s->sph || fmt != s->fmt) {
5648c2ecf20Sopenharmony_ci		dev_info_ratelimited(&s->unit->device,
5658c2ecf20Sopenharmony_ci				     "Detect unexpected protocol: %08x %08x\n",
5668c2ecf20Sopenharmony_ci				     cip_header[0], cip_header[1]);
5678c2ecf20Sopenharmony_ci		return -EAGAIN;
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* Calculate data blocks */
5718c2ecf20Sopenharmony_ci	fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
5728c2ecf20Sopenharmony_ci	if (payload_length < sizeof(__be32) * 2 ||
5738c2ecf20Sopenharmony_ci	    (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
5748c2ecf20Sopenharmony_ci		*data_blocks = 0;
5758c2ecf20Sopenharmony_ci	} else {
5768c2ecf20Sopenharmony_ci		unsigned int data_block_quadlets =
5778c2ecf20Sopenharmony_ci				(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
5788c2ecf20Sopenharmony_ci		/* avoid division by zero */
5798c2ecf20Sopenharmony_ci		if (data_block_quadlets == 0) {
5808c2ecf20Sopenharmony_ci			dev_err(&s->unit->device,
5818c2ecf20Sopenharmony_ci				"Detect invalid value in dbs field: %08X\n",
5828c2ecf20Sopenharmony_ci				cip_header[0]);
5838c2ecf20Sopenharmony_ci			return -EPROTO;
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci		if (s->flags & CIP_WRONG_DBS)
5868c2ecf20Sopenharmony_ci			data_block_quadlets = s->data_block_quadlets;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		*data_blocks = (payload_length / sizeof(__be32) - 2) /
5898c2ecf20Sopenharmony_ci							data_block_quadlets;
5908c2ecf20Sopenharmony_ci	}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* Check data block counter continuity */
5938c2ecf20Sopenharmony_ci	dbc = cip_header[0] & CIP_DBC_MASK;
5948c2ecf20Sopenharmony_ci	if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
5958c2ecf20Sopenharmony_ci	    *data_block_counter != UINT_MAX)
5968c2ecf20Sopenharmony_ci		dbc = *data_block_counter;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) ||
5998c2ecf20Sopenharmony_ci	    *data_block_counter == UINT_MAX) {
6008c2ecf20Sopenharmony_ci		lost = false;
6018c2ecf20Sopenharmony_ci	} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
6028c2ecf20Sopenharmony_ci		lost = dbc != *data_block_counter;
6038c2ecf20Sopenharmony_ci	} else {
6048c2ecf20Sopenharmony_ci		unsigned int dbc_interval;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
6078c2ecf20Sopenharmony_ci			dbc_interval = s->ctx_data.tx.dbc_interval;
6088c2ecf20Sopenharmony_ci		else
6098c2ecf20Sopenharmony_ci			dbc_interval = *data_blocks;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci		lost = dbc != ((*data_block_counter + dbc_interval) & 0xff);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (lost) {
6158c2ecf20Sopenharmony_ci		dev_err(&s->unit->device,
6168c2ecf20Sopenharmony_ci			"Detect discontinuity of CIP: %02X %02X\n",
6178c2ecf20Sopenharmony_ci			*data_block_counter, dbc);
6188c2ecf20Sopenharmony_ci		return -EIO;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	*data_block_counter = dbc;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	*syt = cip_header[1] & CIP_SYT_MASK;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	return 0;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
6298c2ecf20Sopenharmony_ci			       const __be32 *ctx_header,
6308c2ecf20Sopenharmony_ci			       unsigned int *payload_length,
6318c2ecf20Sopenharmony_ci			       unsigned int *data_blocks,
6328c2ecf20Sopenharmony_ci			       unsigned int *data_block_counter,
6338c2ecf20Sopenharmony_ci			       unsigned int *syt, unsigned int packet_index, unsigned int index)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	const __be32 *cip_header;
6368c2ecf20Sopenharmony_ci	unsigned int cip_header_size;
6378c2ecf20Sopenharmony_ci	int err;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	*payload_length = be32_to_cpu(ctx_header[0]) >> ISO_DATA_LENGTH_SHIFT;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (!(s->flags & CIP_NO_HEADER))
6428c2ecf20Sopenharmony_ci		cip_header_size = 8;
6438c2ecf20Sopenharmony_ci	else
6448c2ecf20Sopenharmony_ci		cip_header_size = 0;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (*payload_length > cip_header_size + s->ctx_data.tx.max_ctx_payload_length) {
6478c2ecf20Sopenharmony_ci		dev_err(&s->unit->device,
6488c2ecf20Sopenharmony_ci			"Detect jumbo payload: %04x %04x\n",
6498c2ecf20Sopenharmony_ci			*payload_length, cip_header_size + s->ctx_data.tx.max_ctx_payload_length);
6508c2ecf20Sopenharmony_ci		return -EIO;
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	if (cip_header_size > 0) {
6548c2ecf20Sopenharmony_ci		cip_header = ctx_header + 2;
6558c2ecf20Sopenharmony_ci		err = check_cip_header(s, cip_header, *payload_length,
6568c2ecf20Sopenharmony_ci				       data_blocks, data_block_counter, syt);
6578c2ecf20Sopenharmony_ci		if (err < 0)
6588c2ecf20Sopenharmony_ci			return err;
6598c2ecf20Sopenharmony_ci	} else {
6608c2ecf20Sopenharmony_ci		cip_header = NULL;
6618c2ecf20Sopenharmony_ci		err = 0;
6628c2ecf20Sopenharmony_ci		*data_blocks = *payload_length / sizeof(__be32) /
6638c2ecf20Sopenharmony_ci			       s->data_block_quadlets;
6648c2ecf20Sopenharmony_ci		*syt = 0;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		if (*data_block_counter == UINT_MAX)
6678c2ecf20Sopenharmony_ci			*data_block_counter = 0;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks,
6718c2ecf20Sopenharmony_ci			   *data_block_counter, packet_index, index);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	return err;
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci// In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
6778c2ecf20Sopenharmony_ci// the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
6788c2ecf20Sopenharmony_ci// it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
6798c2ecf20Sopenharmony_cistatic inline u32 compute_cycle_count(__be32 ctx_header_tstamp)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK;
6828c2ecf20Sopenharmony_ci	return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	cycle += addend;
6888c2ecf20Sopenharmony_ci	if (cycle >= OHCI_MAX_SECOND * CYCLES_PER_SECOND)
6898c2ecf20Sopenharmony_ci		cycle -= OHCI_MAX_SECOND * CYCLES_PER_SECOND;
6908c2ecf20Sopenharmony_ci	return cycle;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci// Align to actual cycle count for the packet which is going to be scheduled.
6948c2ecf20Sopenharmony_ci// This module queued the same number of isochronous cycle as the size of queue
6958c2ecf20Sopenharmony_ci// to kip isochronous cycle, therefore it's OK to just increment the cycle by
6968c2ecf20Sopenharmony_ci// the size of queue for scheduled cycle.
6978c2ecf20Sopenharmony_cistatic inline u32 compute_it_cycle(const __be32 ctx_header_tstamp,
6988c2ecf20Sopenharmony_ci				   unsigned int queue_size)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	u32 cycle = compute_cycle_count(ctx_header_tstamp);
7018c2ecf20Sopenharmony_ci	return increment_cycle_count(cycle, queue_size);
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic int generate_device_pkt_descs(struct amdtp_stream *s,
7058c2ecf20Sopenharmony_ci				     struct pkt_desc *descs,
7068c2ecf20Sopenharmony_ci				     const __be32 *ctx_header,
7078c2ecf20Sopenharmony_ci				     unsigned int packets)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	unsigned int dbc = s->data_block_counter;
7108c2ecf20Sopenharmony_ci	unsigned int packet_index = s->packet_index;
7118c2ecf20Sopenharmony_ci	unsigned int queue_size = s->queue_size;
7128c2ecf20Sopenharmony_ci	int i;
7138c2ecf20Sopenharmony_ci	int err;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
7168c2ecf20Sopenharmony_ci		struct pkt_desc *desc = descs + i;
7178c2ecf20Sopenharmony_ci		unsigned int cycle;
7188c2ecf20Sopenharmony_ci		unsigned int payload_length;
7198c2ecf20Sopenharmony_ci		unsigned int data_blocks;
7208c2ecf20Sopenharmony_ci		unsigned int syt;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		cycle = compute_cycle_count(ctx_header[1]);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length,
7258c2ecf20Sopenharmony_ci					  &data_blocks, &dbc, &syt, packet_index, i);
7268c2ecf20Sopenharmony_ci		if (err < 0)
7278c2ecf20Sopenharmony_ci			return err;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci		desc->cycle = cycle;
7308c2ecf20Sopenharmony_ci		desc->syt = syt;
7318c2ecf20Sopenharmony_ci		desc->data_blocks = data_blocks;
7328c2ecf20Sopenharmony_ci		desc->data_block_counter = dbc;
7338c2ecf20Sopenharmony_ci		desc->ctx_payload = s->buffer.packets[packet_index].buffer;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		if (!(s->flags & CIP_DBC_IS_END_EVENT))
7368c2ecf20Sopenharmony_ci			dbc = (dbc + desc->data_blocks) & 0xff;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		ctx_header +=
7398c2ecf20Sopenharmony_ci			s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci		packet_index = (packet_index + 1) % queue_size;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	s->data_block_counter = dbc;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	return 0;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle,
7508c2ecf20Sopenharmony_ci				unsigned int transfer_delay)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	unsigned int syt;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	syt_offset += transfer_delay;
7558c2ecf20Sopenharmony_ci	syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) |
7568c2ecf20Sopenharmony_ci	      (syt_offset % TICKS_PER_CYCLE);
7578c2ecf20Sopenharmony_ci	return syt & CIP_SYT_MASK;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs,
7618c2ecf20Sopenharmony_ci			       const __be32 *ctx_header, unsigned int packets,
7628c2ecf20Sopenharmony_ci			       const struct seq_desc *seq_descs,
7638c2ecf20Sopenharmony_ci			       unsigned int seq_size)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	unsigned int dbc = s->data_block_counter;
7668c2ecf20Sopenharmony_ci	unsigned int seq_index = s->ctx_data.rx.seq_index;
7678c2ecf20Sopenharmony_ci	int i;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
7708c2ecf20Sopenharmony_ci		struct pkt_desc *desc = descs + i;
7718c2ecf20Sopenharmony_ci		unsigned int index = (s->packet_index + i) % s->queue_size;
7728c2ecf20Sopenharmony_ci		const struct seq_desc *seq = seq_descs + seq_index;
7738c2ecf20Sopenharmony_ci		unsigned int syt;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci		desc->cycle = compute_it_cycle(*ctx_header, s->queue_size);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci		syt = seq->syt_offset;
7788c2ecf20Sopenharmony_ci		if (syt != CIP_SYT_NO_INFO) {
7798c2ecf20Sopenharmony_ci			syt = compute_syt(syt, desc->cycle,
7808c2ecf20Sopenharmony_ci					  s->ctx_data.rx.transfer_delay);
7818c2ecf20Sopenharmony_ci		}
7828c2ecf20Sopenharmony_ci		desc->syt = syt;
7838c2ecf20Sopenharmony_ci		desc->data_blocks = seq->data_blocks;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci		if (s->flags & CIP_DBC_IS_END_EVENT)
7868c2ecf20Sopenharmony_ci			dbc = (dbc + desc->data_blocks) & 0xff;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci		desc->data_block_counter = dbc;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci		if (!(s->flags & CIP_DBC_IS_END_EVENT))
7918c2ecf20Sopenharmony_ci			dbc = (dbc + desc->data_blocks) & 0xff;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		desc->ctx_payload = s->buffer.packets[index].buffer;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci		seq_index = (seq_index + 1) % seq_size;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		++ctx_header;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	s->data_block_counter = dbc;
8018c2ecf20Sopenharmony_ci	s->ctx_data.rx.seq_index = seq_index;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic inline void cancel_stream(struct amdtp_stream *s)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	s->packet_index = -1;
8078c2ecf20Sopenharmony_ci	if (in_interrupt())
8088c2ecf20Sopenharmony_ci		amdtp_stream_pcm_abort(s);
8098c2ecf20Sopenharmony_ci	WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic void process_ctx_payloads(struct amdtp_stream *s,
8138c2ecf20Sopenharmony_ci				 const struct pkt_desc *descs,
8148c2ecf20Sopenharmony_ci				 unsigned int packets)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct snd_pcm_substream *pcm;
8178c2ecf20Sopenharmony_ci	unsigned int pcm_frames;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	pcm = READ_ONCE(s->pcm);
8208c2ecf20Sopenharmony_ci	pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm);
8218c2ecf20Sopenharmony_ci	if (pcm)
8228c2ecf20Sopenharmony_ci		update_pcm_pointers(s, pcm, pcm_frames);
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
8268c2ecf20Sopenharmony_ci				size_t header_length, void *header,
8278c2ecf20Sopenharmony_ci				void *private_data)
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	struct amdtp_stream *s = private_data;
8308c2ecf20Sopenharmony_ci	const struct amdtp_domain *d = s->domain;
8318c2ecf20Sopenharmony_ci	const __be32 *ctx_header = header;
8328c2ecf20Sopenharmony_ci	unsigned int events_per_period = s->ctx_data.rx.events_per_period;
8338c2ecf20Sopenharmony_ci	unsigned int event_count = s->ctx_data.rx.event_count;
8348c2ecf20Sopenharmony_ci	unsigned int packets;
8358c2ecf20Sopenharmony_ci	int i;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	if (s->packet_index < 0)
8388c2ecf20Sopenharmony_ci		return;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	// Calculate the number of packets in buffer and check XRUN.
8418c2ecf20Sopenharmony_ci	packets = header_length / sizeof(*ctx_header);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	generate_pkt_descs(s, s->pkt_descs, ctx_header, packets, d->seq_descs,
8448c2ecf20Sopenharmony_ci			   d->seq_size);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	process_ctx_payloads(s, s->pkt_descs, packets);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
8498c2ecf20Sopenharmony_ci		const struct pkt_desc *desc = s->pkt_descs + i;
8508c2ecf20Sopenharmony_ci		unsigned int syt;
8518c2ecf20Sopenharmony_ci		struct {
8528c2ecf20Sopenharmony_ci			struct fw_iso_packet params;
8538c2ecf20Sopenharmony_ci			__be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)];
8548c2ecf20Sopenharmony_ci		} template = { {0}, {0} };
8558c2ecf20Sopenharmony_ci		bool sched_irq = false;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci		if (s->ctx_data.rx.syt_override < 0)
8588c2ecf20Sopenharmony_ci			syt = desc->syt;
8598c2ecf20Sopenharmony_ci		else
8608c2ecf20Sopenharmony_ci			syt = s->ctx_data.rx.syt_override;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci		build_it_pkt_header(s, desc->cycle, &template.params,
8638c2ecf20Sopenharmony_ci				    desc->data_blocks, desc->data_block_counter,
8648c2ecf20Sopenharmony_ci				    syt, i);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci		if (s == s->domain->irq_target) {
8678c2ecf20Sopenharmony_ci			event_count += desc->data_blocks;
8688c2ecf20Sopenharmony_ci			if (event_count >= events_per_period) {
8698c2ecf20Sopenharmony_ci				event_count -= events_per_period;
8708c2ecf20Sopenharmony_ci				sched_irq = true;
8718c2ecf20Sopenharmony_ci			}
8728c2ecf20Sopenharmony_ci		}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci		if (queue_out_packet(s, &template.params, sched_irq) < 0) {
8758c2ecf20Sopenharmony_ci			cancel_stream(s);
8768c2ecf20Sopenharmony_ci			return;
8778c2ecf20Sopenharmony_ci		}
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	s->ctx_data.rx.event_count = event_count;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
8848c2ecf20Sopenharmony_ci			       size_t header_length, void *header,
8858c2ecf20Sopenharmony_ci			       void *private_data)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	struct amdtp_stream *s = private_data;
8888c2ecf20Sopenharmony_ci	__be32 *ctx_header = header;
8898c2ecf20Sopenharmony_ci	unsigned int packets;
8908c2ecf20Sopenharmony_ci	int i;
8918c2ecf20Sopenharmony_ci	int err;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	if (s->packet_index < 0)
8948c2ecf20Sopenharmony_ci		return;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	// Calculate the number of packets in buffer and check XRUN.
8978c2ecf20Sopenharmony_ci	packets = header_length / s->ctx_data.tx.ctx_header_size;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets);
9008c2ecf20Sopenharmony_ci	if (err < 0) {
9018c2ecf20Sopenharmony_ci		if (err != -EAGAIN) {
9028c2ecf20Sopenharmony_ci			cancel_stream(s);
9038c2ecf20Sopenharmony_ci			return;
9048c2ecf20Sopenharmony_ci		}
9058c2ecf20Sopenharmony_ci	} else {
9068c2ecf20Sopenharmony_ci		process_ctx_payloads(s, s->pkt_descs, packets);
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
9108c2ecf20Sopenharmony_ci		struct fw_iso_packet params = {0};
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci		if (queue_in_packet(s, &params) < 0) {
9138c2ecf20Sopenharmony_ci			cancel_stream(s);
9148c2ecf20Sopenharmony_ci			return;
9158c2ecf20Sopenharmony_ci		}
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct amdtp_stream *irq_target = d->irq_target;
9228c2ecf20Sopenharmony_ci	unsigned int seq_tail = d->seq_tail;
9238c2ecf20Sopenharmony_ci	unsigned int seq_size = d->seq_size;
9248c2ecf20Sopenharmony_ci	unsigned int min_avail;
9258c2ecf20Sopenharmony_ci	struct amdtp_stream *s;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	min_avail = d->seq_size;
9288c2ecf20Sopenharmony_ci	list_for_each_entry(s, &d->streams, list) {
9298c2ecf20Sopenharmony_ci		unsigned int seq_index;
9308c2ecf20Sopenharmony_ci		unsigned int avail;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci		if (s->direction == AMDTP_IN_STREAM)
9338c2ecf20Sopenharmony_ci			continue;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci		seq_index = s->ctx_data.rx.seq_index;
9368c2ecf20Sopenharmony_ci		avail = d->seq_tail;
9378c2ecf20Sopenharmony_ci		if (seq_index > avail)
9388c2ecf20Sopenharmony_ci			avail += d->seq_size;
9398c2ecf20Sopenharmony_ci		avail -= seq_index;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci		if (avail < min_avail)
9428c2ecf20Sopenharmony_ci			min_avail = avail;
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	while (min_avail < packets) {
9468c2ecf20Sopenharmony_ci		struct seq_desc *desc = d->seq_descs + seq_tail;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		desc->syt_offset = calculate_syt_offset(&d->last_syt_offset,
9498c2ecf20Sopenharmony_ci					&d->syt_offset_state, irq_target->sfc);
9508c2ecf20Sopenharmony_ci		desc->data_blocks = calculate_data_blocks(&d->data_block_state,
9518c2ecf20Sopenharmony_ci				!!(irq_target->flags & CIP_BLOCKING),
9528c2ecf20Sopenharmony_ci				desc->syt_offset == CIP_SYT_NO_INFO,
9538c2ecf20Sopenharmony_ci				irq_target->syt_interval, irq_target->sfc);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci		++seq_tail;
9568c2ecf20Sopenharmony_ci		seq_tail %= seq_size;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci		++min_avail;
9598c2ecf20Sopenharmony_ci	}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	d->seq_tail = seq_tail;
9628c2ecf20Sopenharmony_ci}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_cistatic void irq_target_callback(struct fw_iso_context *context, u32 tstamp,
9658c2ecf20Sopenharmony_ci				size_t header_length, void *header,
9668c2ecf20Sopenharmony_ci				void *private_data)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	struct amdtp_stream *irq_target = private_data;
9698c2ecf20Sopenharmony_ci	struct amdtp_domain *d = irq_target->domain;
9708c2ecf20Sopenharmony_ci	unsigned int packets = header_length / sizeof(__be32);
9718c2ecf20Sopenharmony_ci	struct amdtp_stream *s;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	// Record enough entries with extra 3 cycles at least.
9748c2ecf20Sopenharmony_ci	pool_ideal_seq_descs(d, packets + 3);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	out_stream_callback(context, tstamp, header_length, header, irq_target);
9778c2ecf20Sopenharmony_ci	if (amdtp_streaming_error(irq_target))
9788c2ecf20Sopenharmony_ci		goto error;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	list_for_each_entry(s, &d->streams, list) {
9818c2ecf20Sopenharmony_ci		if (s != irq_target && amdtp_stream_running(s)) {
9828c2ecf20Sopenharmony_ci			fw_iso_context_flush_completions(s->context);
9838c2ecf20Sopenharmony_ci			if (amdtp_streaming_error(s))
9848c2ecf20Sopenharmony_ci				goto error;
9858c2ecf20Sopenharmony_ci		}
9868c2ecf20Sopenharmony_ci	}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	return;
9898c2ecf20Sopenharmony_cierror:
9908c2ecf20Sopenharmony_ci	if (amdtp_stream_running(irq_target))
9918c2ecf20Sopenharmony_ci		cancel_stream(irq_target);
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	list_for_each_entry(s, &d->streams, list) {
9948c2ecf20Sopenharmony_ci		if (amdtp_stream_running(s))
9958c2ecf20Sopenharmony_ci			cancel_stream(s);
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci// this is executed one time.
10008c2ecf20Sopenharmony_cistatic void amdtp_stream_first_callback(struct fw_iso_context *context,
10018c2ecf20Sopenharmony_ci					u32 tstamp, size_t header_length,
10028c2ecf20Sopenharmony_ci					void *header, void *private_data)
10038c2ecf20Sopenharmony_ci{
10048c2ecf20Sopenharmony_ci	struct amdtp_stream *s = private_data;
10058c2ecf20Sopenharmony_ci	const __be32 *ctx_header = header;
10068c2ecf20Sopenharmony_ci	u32 cycle;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	/*
10098c2ecf20Sopenharmony_ci	 * For in-stream, first packet has come.
10108c2ecf20Sopenharmony_ci	 * For out-stream, prepared to transmit first packet
10118c2ecf20Sopenharmony_ci	 */
10128c2ecf20Sopenharmony_ci	s->callbacked = true;
10138c2ecf20Sopenharmony_ci	wake_up(&s->callback_wait);
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	if (s->direction == AMDTP_IN_STREAM) {
10168c2ecf20Sopenharmony_ci		cycle = compute_cycle_count(ctx_header[1]);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci		context->callback.sc = in_stream_callback;
10198c2ecf20Sopenharmony_ci	} else {
10208c2ecf20Sopenharmony_ci		cycle = compute_it_cycle(*ctx_header, s->queue_size);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci		if (s == s->domain->irq_target)
10238c2ecf20Sopenharmony_ci			context->callback.sc = irq_target_callback;
10248c2ecf20Sopenharmony_ci		else
10258c2ecf20Sopenharmony_ci			context->callback.sc = out_stream_callback;
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	s->start_cycle = cycle;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	context->callback.sc(context, tstamp, header_length, header, s);
10318c2ecf20Sopenharmony_ci}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci/**
10348c2ecf20Sopenharmony_ci * amdtp_stream_start - start transferring packets
10358c2ecf20Sopenharmony_ci * @s: the AMDTP stream to start
10368c2ecf20Sopenharmony_ci * @channel: the isochronous channel on the bus
10378c2ecf20Sopenharmony_ci * @speed: firewire speed code
10388c2ecf20Sopenharmony_ci * @start_cycle: the isochronous cycle to start the context. Start immediately
10398c2ecf20Sopenharmony_ci *		 if negative value is given.
10408c2ecf20Sopenharmony_ci * @queue_size: The number of packets in the queue.
10418c2ecf20Sopenharmony_ci * @idle_irq_interval: the interval to queue packet during initial state.
10428c2ecf20Sopenharmony_ci *
10438c2ecf20Sopenharmony_ci * The stream cannot be started until it has been configured with
10448c2ecf20Sopenharmony_ci * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
10458c2ecf20Sopenharmony_ci * device can be started.
10468c2ecf20Sopenharmony_ci */
10478c2ecf20Sopenharmony_cistatic int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
10488c2ecf20Sopenharmony_ci			      int start_cycle, unsigned int queue_size,
10498c2ecf20Sopenharmony_ci			      unsigned int idle_irq_interval)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	bool is_irq_target = (s == s->domain->irq_target);
10528c2ecf20Sopenharmony_ci	unsigned int ctx_header_size;
10538c2ecf20Sopenharmony_ci	unsigned int max_ctx_payload_size;
10548c2ecf20Sopenharmony_ci	enum dma_data_direction dir;
10558c2ecf20Sopenharmony_ci	int type, tag, err;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	mutex_lock(&s->mutex);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	if (WARN_ON(amdtp_stream_running(s) ||
10608c2ecf20Sopenharmony_ci		    (s->data_block_quadlets < 1))) {
10618c2ecf20Sopenharmony_ci		err = -EBADFD;
10628c2ecf20Sopenharmony_ci		goto err_unlock;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (s->direction == AMDTP_IN_STREAM) {
10668c2ecf20Sopenharmony_ci		// NOTE: IT context should be used for constant IRQ.
10678c2ecf20Sopenharmony_ci		if (is_irq_target) {
10688c2ecf20Sopenharmony_ci			err = -EINVAL;
10698c2ecf20Sopenharmony_ci			goto err_unlock;
10708c2ecf20Sopenharmony_ci		}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci		s->data_block_counter = UINT_MAX;
10738c2ecf20Sopenharmony_ci	} else {
10748c2ecf20Sopenharmony_ci		s->data_block_counter = 0;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	// initialize packet buffer.
10788c2ecf20Sopenharmony_ci	max_ctx_payload_size = amdtp_stream_get_max_payload(s);
10798c2ecf20Sopenharmony_ci	if (s->direction == AMDTP_IN_STREAM) {
10808c2ecf20Sopenharmony_ci		dir = DMA_FROM_DEVICE;
10818c2ecf20Sopenharmony_ci		type = FW_ISO_CONTEXT_RECEIVE;
10828c2ecf20Sopenharmony_ci		if (!(s->flags & CIP_NO_HEADER)) {
10838c2ecf20Sopenharmony_ci			max_ctx_payload_size -= 8;
10848c2ecf20Sopenharmony_ci			ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
10858c2ecf20Sopenharmony_ci		} else {
10868c2ecf20Sopenharmony_ci			ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
10878c2ecf20Sopenharmony_ci		}
10888c2ecf20Sopenharmony_ci	} else {
10898c2ecf20Sopenharmony_ci		dir = DMA_TO_DEVICE;
10908c2ecf20Sopenharmony_ci		type = FW_ISO_CONTEXT_TRANSMIT;
10918c2ecf20Sopenharmony_ci		ctx_header_size = 0;	// No effect for IT context.
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci		if (!(s->flags & CIP_NO_HEADER))
10948c2ecf20Sopenharmony_ci			max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP;
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size,
10988c2ecf20Sopenharmony_ci				      max_ctx_payload_size, dir);
10998c2ecf20Sopenharmony_ci	if (err < 0)
11008c2ecf20Sopenharmony_ci		goto err_unlock;
11018c2ecf20Sopenharmony_ci	s->queue_size = queue_size;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
11048c2ecf20Sopenharmony_ci					  type, channel, speed, ctx_header_size,
11058c2ecf20Sopenharmony_ci					  amdtp_stream_first_callback, s);
11068c2ecf20Sopenharmony_ci	if (IS_ERR(s->context)) {
11078c2ecf20Sopenharmony_ci		err = PTR_ERR(s->context);
11088c2ecf20Sopenharmony_ci		if (err == -EBUSY)
11098c2ecf20Sopenharmony_ci			dev_err(&s->unit->device,
11108c2ecf20Sopenharmony_ci				"no free stream on this controller\n");
11118c2ecf20Sopenharmony_ci		goto err_buffer;
11128c2ecf20Sopenharmony_ci	}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	amdtp_stream_update(s);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (s->direction == AMDTP_IN_STREAM) {
11178c2ecf20Sopenharmony_ci		s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size;
11188c2ecf20Sopenharmony_ci		s->ctx_data.tx.ctx_header_size = ctx_header_size;
11198c2ecf20Sopenharmony_ci	}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	if (s->flags & CIP_NO_HEADER)
11228c2ecf20Sopenharmony_ci		s->tag = TAG_NO_CIP_HEADER;
11238c2ecf20Sopenharmony_ci	else
11248c2ecf20Sopenharmony_ci		s->tag = TAG_CIP;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	s->pkt_descs = kcalloc(s->queue_size, sizeof(*s->pkt_descs),
11278c2ecf20Sopenharmony_ci			       GFP_KERNEL);
11288c2ecf20Sopenharmony_ci	if (!s->pkt_descs) {
11298c2ecf20Sopenharmony_ci		err = -ENOMEM;
11308c2ecf20Sopenharmony_ci		goto err_context;
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	s->packet_index = 0;
11348c2ecf20Sopenharmony_ci	do {
11358c2ecf20Sopenharmony_ci		struct fw_iso_packet params;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci		if (s->direction == AMDTP_IN_STREAM) {
11388c2ecf20Sopenharmony_ci			err = queue_in_packet(s, &params);
11398c2ecf20Sopenharmony_ci		} else {
11408c2ecf20Sopenharmony_ci			bool sched_irq = false;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci			params.header_length = 0;
11438c2ecf20Sopenharmony_ci			params.payload_length = 0;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci			if (is_irq_target) {
11468c2ecf20Sopenharmony_ci				sched_irq = !((s->packet_index + 1) %
11478c2ecf20Sopenharmony_ci					      idle_irq_interval);
11488c2ecf20Sopenharmony_ci			}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci			err = queue_out_packet(s, &params, sched_irq);
11518c2ecf20Sopenharmony_ci		}
11528c2ecf20Sopenharmony_ci		if (err < 0)
11538c2ecf20Sopenharmony_ci			goto err_pkt_descs;
11548c2ecf20Sopenharmony_ci	} while (s->packet_index > 0);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* NOTE: TAG1 matches CIP. This just affects in stream. */
11578c2ecf20Sopenharmony_ci	tag = FW_ISO_CONTEXT_MATCH_TAG1;
11588c2ecf20Sopenharmony_ci	if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
11598c2ecf20Sopenharmony_ci		tag |= FW_ISO_CONTEXT_MATCH_TAG0;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	s->callbacked = false;
11628c2ecf20Sopenharmony_ci	err = fw_iso_context_start(s->context, start_cycle, 0, tag);
11638c2ecf20Sopenharmony_ci	if (err < 0)
11648c2ecf20Sopenharmony_ci		goto err_pkt_descs;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	mutex_unlock(&s->mutex);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	return 0;
11698c2ecf20Sopenharmony_cierr_pkt_descs:
11708c2ecf20Sopenharmony_ci	kfree(s->pkt_descs);
11718c2ecf20Sopenharmony_cierr_context:
11728c2ecf20Sopenharmony_ci	fw_iso_context_destroy(s->context);
11738c2ecf20Sopenharmony_ci	s->context = ERR_PTR(-1);
11748c2ecf20Sopenharmony_cierr_buffer:
11758c2ecf20Sopenharmony_ci	iso_packets_buffer_destroy(&s->buffer, s->unit);
11768c2ecf20Sopenharmony_cierr_unlock:
11778c2ecf20Sopenharmony_ci	mutex_unlock(&s->mutex);
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	return err;
11808c2ecf20Sopenharmony_ci}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci/**
11838c2ecf20Sopenharmony_ci * amdtp_domain_stream_pcm_pointer - get the PCM buffer position
11848c2ecf20Sopenharmony_ci * @d: the AMDTP domain.
11858c2ecf20Sopenharmony_ci * @s: the AMDTP stream that transports the PCM data
11868c2ecf20Sopenharmony_ci *
11878c2ecf20Sopenharmony_ci * Returns the current buffer position, in frames.
11888c2ecf20Sopenharmony_ci */
11898c2ecf20Sopenharmony_ciunsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
11908c2ecf20Sopenharmony_ci					      struct amdtp_stream *s)
11918c2ecf20Sopenharmony_ci{
11928c2ecf20Sopenharmony_ci	struct amdtp_stream *irq_target = d->irq_target;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	if (irq_target && amdtp_stream_running(irq_target)) {
11958c2ecf20Sopenharmony_ci		// This function is called in software IRQ context of
11968c2ecf20Sopenharmony_ci		// period_work or process context.
11978c2ecf20Sopenharmony_ci		//
11988c2ecf20Sopenharmony_ci		// When the software IRQ context was scheduled by software IRQ
11998c2ecf20Sopenharmony_ci		// context of IT contexts, queued packets were already handled.
12008c2ecf20Sopenharmony_ci		// Therefore, no need to flush the queue in buffer furthermore.
12018c2ecf20Sopenharmony_ci		//
12028c2ecf20Sopenharmony_ci		// When the process context reach here, some packets will be
12038c2ecf20Sopenharmony_ci		// already queued in the buffer. These packets should be handled
12048c2ecf20Sopenharmony_ci		// immediately to keep better granularity of PCM pointer.
12058c2ecf20Sopenharmony_ci		//
12068c2ecf20Sopenharmony_ci		// Later, the process context will sometimes schedules software
12078c2ecf20Sopenharmony_ci		// IRQ context of the period_work. Then, no need to flush the
12088c2ecf20Sopenharmony_ci		// queue by the same reason as described in the above
12098c2ecf20Sopenharmony_ci		if (current_work() != &s->period_work) {
12108c2ecf20Sopenharmony_ci			// Queued packet should be processed without any kernel
12118c2ecf20Sopenharmony_ci			// preemption to keep latency against bus cycle.
12128c2ecf20Sopenharmony_ci			preempt_disable();
12138c2ecf20Sopenharmony_ci			fw_iso_context_flush_completions(irq_target->context);
12148c2ecf20Sopenharmony_ci			preempt_enable();
12158c2ecf20Sopenharmony_ci		}
12168c2ecf20Sopenharmony_ci	}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	return READ_ONCE(s->pcm_buffer_pointer);
12198c2ecf20Sopenharmony_ci}
12208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer);
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci/**
12238c2ecf20Sopenharmony_ci * amdtp_domain_stream_pcm_ack - acknowledge queued PCM frames
12248c2ecf20Sopenharmony_ci * @d: the AMDTP domain.
12258c2ecf20Sopenharmony_ci * @s: the AMDTP stream that transfers the PCM frames
12268c2ecf20Sopenharmony_ci *
12278c2ecf20Sopenharmony_ci * Returns zero always.
12288c2ecf20Sopenharmony_ci */
12298c2ecf20Sopenharmony_ciint amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s)
12308c2ecf20Sopenharmony_ci{
12318c2ecf20Sopenharmony_ci	struct amdtp_stream *irq_target = d->irq_target;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	// Process isochronous packets for recent isochronous cycle to handle
12348c2ecf20Sopenharmony_ci	// queued PCM frames.
12358c2ecf20Sopenharmony_ci	if (irq_target && amdtp_stream_running(irq_target)) {
12368c2ecf20Sopenharmony_ci		// Queued packet should be processed without any kernel
12378c2ecf20Sopenharmony_ci		// preemption to keep latency against bus cycle.
12388c2ecf20Sopenharmony_ci		preempt_disable();
12398c2ecf20Sopenharmony_ci		fw_iso_context_flush_completions(irq_target->context);
12408c2ecf20Sopenharmony_ci		preempt_enable();
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	return 0;
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci/**
12488c2ecf20Sopenharmony_ci * amdtp_stream_update - update the stream after a bus reset
12498c2ecf20Sopenharmony_ci * @s: the AMDTP stream
12508c2ecf20Sopenharmony_ci */
12518c2ecf20Sopenharmony_civoid amdtp_stream_update(struct amdtp_stream *s)
12528c2ecf20Sopenharmony_ci{
12538c2ecf20Sopenharmony_ci	/* Precomputing. */
12548c2ecf20Sopenharmony_ci	WRITE_ONCE(s->source_node_id_field,
12558c2ecf20Sopenharmony_ci                   (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & CIP_SID_MASK);
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_update);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci/**
12608c2ecf20Sopenharmony_ci * amdtp_stream_stop - stop sending packets
12618c2ecf20Sopenharmony_ci * @s: the AMDTP stream to stop
12628c2ecf20Sopenharmony_ci *
12638c2ecf20Sopenharmony_ci * All PCM and MIDI devices of the stream must be stopped before the stream
12648c2ecf20Sopenharmony_ci * itself can be stopped.
12658c2ecf20Sopenharmony_ci */
12668c2ecf20Sopenharmony_cistatic void amdtp_stream_stop(struct amdtp_stream *s)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	mutex_lock(&s->mutex);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	if (!amdtp_stream_running(s)) {
12718c2ecf20Sopenharmony_ci		mutex_unlock(&s->mutex);
12728c2ecf20Sopenharmony_ci		return;
12738c2ecf20Sopenharmony_ci	}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	cancel_work_sync(&s->period_work);
12768c2ecf20Sopenharmony_ci	fw_iso_context_stop(s->context);
12778c2ecf20Sopenharmony_ci	fw_iso_context_destroy(s->context);
12788c2ecf20Sopenharmony_ci	s->context = ERR_PTR(-1);
12798c2ecf20Sopenharmony_ci	iso_packets_buffer_destroy(&s->buffer, s->unit);
12808c2ecf20Sopenharmony_ci	kfree(s->pkt_descs);
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	s->callbacked = false;
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	mutex_unlock(&s->mutex);
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci/**
12888c2ecf20Sopenharmony_ci * amdtp_stream_pcm_abort - abort the running PCM device
12898c2ecf20Sopenharmony_ci * @s: the AMDTP stream about to be stopped
12908c2ecf20Sopenharmony_ci *
12918c2ecf20Sopenharmony_ci * If the isochronous stream needs to be stopped asynchronously, call this
12928c2ecf20Sopenharmony_ci * function first to stop the PCM device.
12938c2ecf20Sopenharmony_ci */
12948c2ecf20Sopenharmony_civoid amdtp_stream_pcm_abort(struct amdtp_stream *s)
12958c2ecf20Sopenharmony_ci{
12968c2ecf20Sopenharmony_ci	struct snd_pcm_substream *pcm;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	pcm = READ_ONCE(s->pcm);
12998c2ecf20Sopenharmony_ci	if (pcm)
13008c2ecf20Sopenharmony_ci		snd_pcm_stop_xrun(pcm);
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_pcm_abort);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci/**
13058c2ecf20Sopenharmony_ci * amdtp_domain_init - initialize an AMDTP domain structure
13068c2ecf20Sopenharmony_ci * @d: the AMDTP domain to initialize.
13078c2ecf20Sopenharmony_ci */
13088c2ecf20Sopenharmony_ciint amdtp_domain_init(struct amdtp_domain *d)
13098c2ecf20Sopenharmony_ci{
13108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&d->streams);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	d->events_per_period = 0;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	d->seq_descs = NULL;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	return 0;
13178c2ecf20Sopenharmony_ci}
13188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_init);
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci/**
13218c2ecf20Sopenharmony_ci * amdtp_domain_destroy - destroy an AMDTP domain structure
13228c2ecf20Sopenharmony_ci * @d: the AMDTP domain to destroy.
13238c2ecf20Sopenharmony_ci */
13248c2ecf20Sopenharmony_civoid amdtp_domain_destroy(struct amdtp_domain *d)
13258c2ecf20Sopenharmony_ci{
13268c2ecf20Sopenharmony_ci	// At present nothing to do.
13278c2ecf20Sopenharmony_ci	return;
13288c2ecf20Sopenharmony_ci}
13298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_destroy);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci/**
13328c2ecf20Sopenharmony_ci * amdtp_domain_add_stream - register isoc context into the domain.
13338c2ecf20Sopenharmony_ci * @d: the AMDTP domain.
13348c2ecf20Sopenharmony_ci * @s: the AMDTP stream.
13358c2ecf20Sopenharmony_ci * @channel: the isochronous channel on the bus.
13368c2ecf20Sopenharmony_ci * @speed: firewire speed code.
13378c2ecf20Sopenharmony_ci */
13388c2ecf20Sopenharmony_ciint amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
13398c2ecf20Sopenharmony_ci			    int channel, int speed)
13408c2ecf20Sopenharmony_ci{
13418c2ecf20Sopenharmony_ci	struct amdtp_stream *tmp;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	list_for_each_entry(tmp, &d->streams, list) {
13448c2ecf20Sopenharmony_ci		if (s == tmp)
13458c2ecf20Sopenharmony_ci			return -EBUSY;
13468c2ecf20Sopenharmony_ci	}
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	list_add(&s->list, &d->streams);
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	s->channel = channel;
13518c2ecf20Sopenharmony_ci	s->speed = speed;
13528c2ecf20Sopenharmony_ci	s->domain = d;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	return 0;
13558c2ecf20Sopenharmony_ci}
13568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_add_stream);
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle)
13598c2ecf20Sopenharmony_ci{
13608c2ecf20Sopenharmony_ci	int generation;
13618c2ecf20Sopenharmony_ci	int rcode;
13628c2ecf20Sopenharmony_ci	__be32 reg;
13638c2ecf20Sopenharmony_ci	u32 data;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	// This is a request to local 1394 OHCI controller and expected to
13668c2ecf20Sopenharmony_ci	// complete without any event waiting.
13678c2ecf20Sopenharmony_ci	generation = fw_card->generation;
13688c2ecf20Sopenharmony_ci	smp_rmb();	// node_id vs. generation.
13698c2ecf20Sopenharmony_ci	rcode = fw_run_transaction(fw_card, TCODE_READ_QUADLET_REQUEST,
13708c2ecf20Sopenharmony_ci				   fw_card->node_id, generation, SCODE_100,
13718c2ecf20Sopenharmony_ci				   CSR_REGISTER_BASE + CSR_CYCLE_TIME,
13728c2ecf20Sopenharmony_ci				   &reg, sizeof(reg));
13738c2ecf20Sopenharmony_ci	if (rcode != RCODE_COMPLETE)
13748c2ecf20Sopenharmony_ci		return -EIO;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	data = be32_to_cpu(reg);
13778c2ecf20Sopenharmony_ci	*cur_cycle = data >> 12;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	return 0;
13808c2ecf20Sopenharmony_ci}
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci/**
13838c2ecf20Sopenharmony_ci * amdtp_domain_start - start sending packets for isoc context in the domain.
13848c2ecf20Sopenharmony_ci * @d: the AMDTP domain.
13858c2ecf20Sopenharmony_ci * @ir_delay_cycle: the cycle delay to start all IR contexts.
13868c2ecf20Sopenharmony_ci */
13878c2ecf20Sopenharmony_ciint amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle)
13888c2ecf20Sopenharmony_ci{
13898c2ecf20Sopenharmony_ci	static const struct {
13908c2ecf20Sopenharmony_ci		unsigned int data_block;
13918c2ecf20Sopenharmony_ci		unsigned int syt_offset;
13928c2ecf20Sopenharmony_ci	} *entry, initial_state[] = {
13938c2ecf20Sopenharmony_ci		[CIP_SFC_32000]  = {  4, 3072 },
13948c2ecf20Sopenharmony_ci		[CIP_SFC_48000]  = {  6, 1024 },
13958c2ecf20Sopenharmony_ci		[CIP_SFC_96000]  = { 12, 1024 },
13968c2ecf20Sopenharmony_ci		[CIP_SFC_192000] = { 24, 1024 },
13978c2ecf20Sopenharmony_ci		[CIP_SFC_44100]  = {  0,   67 },
13988c2ecf20Sopenharmony_ci		[CIP_SFC_88200]  = {  0,   67 },
13998c2ecf20Sopenharmony_ci		[CIP_SFC_176400] = {  0,   67 },
14008c2ecf20Sopenharmony_ci	};
14018c2ecf20Sopenharmony_ci	unsigned int events_per_buffer = d->events_per_buffer;
14028c2ecf20Sopenharmony_ci	unsigned int events_per_period = d->events_per_period;
14038c2ecf20Sopenharmony_ci	unsigned int idle_irq_interval;
14048c2ecf20Sopenharmony_ci	unsigned int queue_size;
14058c2ecf20Sopenharmony_ci	struct amdtp_stream *s;
14068c2ecf20Sopenharmony_ci	int cycle;
14078c2ecf20Sopenharmony_ci	bool found = false;
14088c2ecf20Sopenharmony_ci	int err;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	// Select an IT context as IRQ target.
14118c2ecf20Sopenharmony_ci	list_for_each_entry(s, &d->streams, list) {
14128c2ecf20Sopenharmony_ci		if (s->direction == AMDTP_OUT_STREAM) {
14138c2ecf20Sopenharmony_ci			found = true;
14148c2ecf20Sopenharmony_ci			break;
14158c2ecf20Sopenharmony_ci		}
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci	if (!found)
14188c2ecf20Sopenharmony_ci		return -ENXIO;
14198c2ecf20Sopenharmony_ci	d->irq_target = s;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	// This is a case that AMDTP streams in domain run just for MIDI
14228c2ecf20Sopenharmony_ci	// substream. Use the number of events equivalent to 10 msec as
14238c2ecf20Sopenharmony_ci	// interval of hardware IRQ.
14248c2ecf20Sopenharmony_ci	if (events_per_period == 0)
14258c2ecf20Sopenharmony_ci		events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100;
14268c2ecf20Sopenharmony_ci	if (events_per_buffer == 0)
14278c2ecf20Sopenharmony_ci		events_per_buffer = events_per_period * 3;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer,
14308c2ecf20Sopenharmony_ci				  amdtp_rate_table[d->irq_target->sfc]);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	d->seq_descs = kcalloc(queue_size, sizeof(*d->seq_descs), GFP_KERNEL);
14338c2ecf20Sopenharmony_ci	if (!d->seq_descs)
14348c2ecf20Sopenharmony_ci		return -ENOMEM;
14358c2ecf20Sopenharmony_ci	d->seq_size = queue_size;
14368c2ecf20Sopenharmony_ci	d->seq_tail = 0;
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	entry = &initial_state[s->sfc];
14398c2ecf20Sopenharmony_ci	d->data_block_state = entry->data_block;
14408c2ecf20Sopenharmony_ci	d->syt_offset_state = entry->syt_offset;
14418c2ecf20Sopenharmony_ci	d->last_syt_offset = TICKS_PER_CYCLE;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	if (ir_delay_cycle > 0) {
14448c2ecf20Sopenharmony_ci		struct fw_card *fw_card = fw_parent_device(s->unit)->card;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci		err = get_current_cycle_time(fw_card, &cycle);
14478c2ecf20Sopenharmony_ci		if (err < 0)
14488c2ecf20Sopenharmony_ci			goto error;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci		// No need to care overflow in cycle field because of enough
14518c2ecf20Sopenharmony_ci		// width.
14528c2ecf20Sopenharmony_ci		cycle += ir_delay_cycle;
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci		// Round up to sec field.
14558c2ecf20Sopenharmony_ci		if ((cycle & 0x00001fff) >= CYCLES_PER_SECOND) {
14568c2ecf20Sopenharmony_ci			unsigned int sec;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci			// The sec field can overflow.
14598c2ecf20Sopenharmony_ci			sec = (cycle & 0xffffe000) >> 13;
14608c2ecf20Sopenharmony_ci			cycle = (++sec << 13) |
14618c2ecf20Sopenharmony_ci				((cycle & 0x00001fff) / CYCLES_PER_SECOND);
14628c2ecf20Sopenharmony_ci		}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci		// In OHCI 1394 specification, lower 2 bits are available for
14658c2ecf20Sopenharmony_ci		// sec field.
14668c2ecf20Sopenharmony_ci		cycle &= 0x00007fff;
14678c2ecf20Sopenharmony_ci	} else {
14688c2ecf20Sopenharmony_ci		cycle = -1;
14698c2ecf20Sopenharmony_ci	}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	list_for_each_entry(s, &d->streams, list) {
14728c2ecf20Sopenharmony_ci		int cycle_match;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci		if (s->direction == AMDTP_IN_STREAM) {
14758c2ecf20Sopenharmony_ci			cycle_match = cycle;
14768c2ecf20Sopenharmony_ci		} else {
14778c2ecf20Sopenharmony_ci			// IT context starts immediately.
14788c2ecf20Sopenharmony_ci			cycle_match = -1;
14798c2ecf20Sopenharmony_ci			s->ctx_data.rx.seq_index = 0;
14808c2ecf20Sopenharmony_ci		}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci		if (s != d->irq_target) {
14838c2ecf20Sopenharmony_ci			err = amdtp_stream_start(s, s->channel, s->speed,
14848c2ecf20Sopenharmony_ci						 cycle_match, queue_size, 0);
14858c2ecf20Sopenharmony_ci			if (err < 0)
14868c2ecf20Sopenharmony_ci				goto error;
14878c2ecf20Sopenharmony_ci		}
14888c2ecf20Sopenharmony_ci	}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	s = d->irq_target;
14918c2ecf20Sopenharmony_ci	s->ctx_data.rx.events_per_period = events_per_period;
14928c2ecf20Sopenharmony_ci	s->ctx_data.rx.event_count = 0;
14938c2ecf20Sopenharmony_ci	s->ctx_data.rx.seq_index = 0;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period,
14968c2ecf20Sopenharmony_ci					 amdtp_rate_table[d->irq_target->sfc]);
14978c2ecf20Sopenharmony_ci	err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size,
14988c2ecf20Sopenharmony_ci				 idle_irq_interval);
14998c2ecf20Sopenharmony_ci	if (err < 0)
15008c2ecf20Sopenharmony_ci		goto error;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	return 0;
15038c2ecf20Sopenharmony_cierror:
15048c2ecf20Sopenharmony_ci	list_for_each_entry(s, &d->streams, list)
15058c2ecf20Sopenharmony_ci		amdtp_stream_stop(s);
15068c2ecf20Sopenharmony_ci	kfree(d->seq_descs);
15078c2ecf20Sopenharmony_ci	d->seq_descs = NULL;
15088c2ecf20Sopenharmony_ci	return err;
15098c2ecf20Sopenharmony_ci}
15108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_start);
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci/**
15138c2ecf20Sopenharmony_ci * amdtp_domain_stop - stop sending packets for isoc context in the same domain.
15148c2ecf20Sopenharmony_ci * @d: the AMDTP domain to which the isoc contexts belong.
15158c2ecf20Sopenharmony_ci */
15168c2ecf20Sopenharmony_civoid amdtp_domain_stop(struct amdtp_domain *d)
15178c2ecf20Sopenharmony_ci{
15188c2ecf20Sopenharmony_ci	struct amdtp_stream *s, *next;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	if (d->irq_target)
15218c2ecf20Sopenharmony_ci		amdtp_stream_stop(d->irq_target);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	list_for_each_entry_safe(s, next, &d->streams, list) {
15248c2ecf20Sopenharmony_ci		list_del(&s->list);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		if (s != d->irq_target)
15278c2ecf20Sopenharmony_ci			amdtp_stream_stop(s);
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	d->events_per_period = 0;
15318c2ecf20Sopenharmony_ci	d->irq_target = NULL;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	kfree(d->seq_descs);
15348c2ecf20Sopenharmony_ci	d->seq_descs = NULL;
15358c2ecf20Sopenharmony_ci}
15368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_stop);
1537