162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Audio and Music Data Transmission Protocol (IEC 61883-6) streams 462306a36Sopenharmony_ci * with Common Isochronous Packet (IEC 61883-1) headers 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/firewire.h> 1262306a36Sopenharmony_ci#include <linux/firewire-constants.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <sound/pcm.h> 1662306a36Sopenharmony_ci#include <sound/pcm_params.h> 1762306a36Sopenharmony_ci#include "amdtp-stream.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define TICKS_PER_CYCLE 3072 2062306a36Sopenharmony_ci#define CYCLES_PER_SECOND 8000 2162306a36Sopenharmony_ci#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define OHCI_SECOND_MODULUS 8 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Always support Linux tracing subsystem. */ 2662306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 2762306a36Sopenharmony_ci#include "amdtp-stream-trace.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* isochronous header parameters */ 3262306a36Sopenharmony_ci#define ISO_DATA_LENGTH_SHIFT 16 3362306a36Sopenharmony_ci#define TAG_NO_CIP_HEADER 0 3462306a36Sopenharmony_ci#define TAG_CIP 1 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci// Common Isochronous Packet (CIP) header parameters. Use two quadlets CIP header when supported. 3762306a36Sopenharmony_ci#define CIP_HEADER_QUADLETS 2 3862306a36Sopenharmony_ci#define CIP_EOH_SHIFT 31 3962306a36Sopenharmony_ci#define CIP_EOH (1u << CIP_EOH_SHIFT) 4062306a36Sopenharmony_ci#define CIP_EOH_MASK 0x80000000 4162306a36Sopenharmony_ci#define CIP_SID_SHIFT 24 4262306a36Sopenharmony_ci#define CIP_SID_MASK 0x3f000000 4362306a36Sopenharmony_ci#define CIP_DBS_MASK 0x00ff0000 4462306a36Sopenharmony_ci#define CIP_DBS_SHIFT 16 4562306a36Sopenharmony_ci#define CIP_SPH_MASK 0x00000400 4662306a36Sopenharmony_ci#define CIP_SPH_SHIFT 10 4762306a36Sopenharmony_ci#define CIP_DBC_MASK 0x000000ff 4862306a36Sopenharmony_ci#define CIP_FMT_SHIFT 24 4962306a36Sopenharmony_ci#define CIP_FMT_MASK 0x3f000000 5062306a36Sopenharmony_ci#define CIP_FDF_MASK 0x00ff0000 5162306a36Sopenharmony_ci#define CIP_FDF_SHIFT 16 5262306a36Sopenharmony_ci#define CIP_FDF_NO_DATA 0xff 5362306a36Sopenharmony_ci#define CIP_SYT_MASK 0x0000ffff 5462306a36Sopenharmony_ci#define CIP_SYT_NO_INFO 0xffff 5562306a36Sopenharmony_ci#define CIP_SYT_CYCLE_MODULUS 16 5662306a36Sopenharmony_ci#define CIP_NO_DATA ((CIP_FDF_NO_DATA << CIP_FDF_SHIFT) | CIP_SYT_NO_INFO) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define CIP_HEADER_SIZE (sizeof(__be32) * CIP_HEADER_QUADLETS) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* Audio and Music transfer protocol specific parameters */ 6162306a36Sopenharmony_ci#define CIP_FMT_AM 0x10 6262306a36Sopenharmony_ci#define AMDTP_FDF_NO_DATA 0xff 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci// For iso header and tstamp. 6562306a36Sopenharmony_ci#define IR_CTX_HEADER_DEFAULT_QUADLETS 2 6662306a36Sopenharmony_ci// Add nothing. 6762306a36Sopenharmony_ci#define IR_CTX_HEADER_SIZE_NO_CIP (sizeof(__be32) * IR_CTX_HEADER_DEFAULT_QUADLETS) 6862306a36Sopenharmony_ci// Add two quadlets CIP header. 6962306a36Sopenharmony_ci#define IR_CTX_HEADER_SIZE_CIP (IR_CTX_HEADER_SIZE_NO_CIP + CIP_HEADER_SIZE) 7062306a36Sopenharmony_ci#define HEADER_TSTAMP_MASK 0x0000ffff 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define IT_PKT_HEADER_SIZE_CIP CIP_HEADER_SIZE 7362306a36Sopenharmony_ci#define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing. 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci// The initial firmware of OXFW970 can postpone transmission of packet during finishing 7662306a36Sopenharmony_ci// asynchronous transaction. This module accepts 5 cycles to skip as maximum to avoid buffer 7762306a36Sopenharmony_ci// overrun. Actual device can skip more, then this module stops the packet streaming. 7862306a36Sopenharmony_ci#define IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES 5 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * amdtp_stream_init - initialize an AMDTP stream structure 8262306a36Sopenharmony_ci * @s: the AMDTP stream to initialize 8362306a36Sopenharmony_ci * @unit: the target of the stream 8462306a36Sopenharmony_ci * @dir: the direction of stream 8562306a36Sopenharmony_ci * @flags: the details of the streaming protocol consist of cip_flags enumeration-constants. 8662306a36Sopenharmony_ci * @fmt: the value of fmt field in CIP header 8762306a36Sopenharmony_ci * @process_ctx_payloads: callback handler to process payloads of isoc context 8862306a36Sopenharmony_ci * @protocol_size: the size to allocate newly for protocol 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ciint amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, 9162306a36Sopenharmony_ci enum amdtp_stream_direction dir, unsigned int flags, 9262306a36Sopenharmony_ci unsigned int fmt, 9362306a36Sopenharmony_ci amdtp_stream_process_ctx_payloads_t process_ctx_payloads, 9462306a36Sopenharmony_ci unsigned int protocol_size) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci if (process_ctx_payloads == NULL) 9762306a36Sopenharmony_ci return -EINVAL; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci s->protocol = kzalloc(protocol_size, GFP_KERNEL); 10062306a36Sopenharmony_ci if (!s->protocol) 10162306a36Sopenharmony_ci return -ENOMEM; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci s->unit = unit; 10462306a36Sopenharmony_ci s->direction = dir; 10562306a36Sopenharmony_ci s->flags = flags; 10662306a36Sopenharmony_ci s->context = ERR_PTR(-1); 10762306a36Sopenharmony_ci mutex_init(&s->mutex); 10862306a36Sopenharmony_ci s->packet_index = 0; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci init_waitqueue_head(&s->ready_wait); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci s->fmt = fmt; 11362306a36Sopenharmony_ci s->process_ctx_payloads = process_ctx_payloads; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_init); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * amdtp_stream_destroy - free stream resources 12162306a36Sopenharmony_ci * @s: the AMDTP stream to destroy 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_civoid amdtp_stream_destroy(struct amdtp_stream *s) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci /* Not initialized. */ 12662306a36Sopenharmony_ci if (s->protocol == NULL) 12762306a36Sopenharmony_ci return; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci WARN_ON(amdtp_stream_running(s)); 13062306a36Sopenharmony_ci kfree(s->protocol); 13162306a36Sopenharmony_ci mutex_destroy(&s->mutex); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_destroy); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciconst unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { 13662306a36Sopenharmony_ci [CIP_SFC_32000] = 8, 13762306a36Sopenharmony_ci [CIP_SFC_44100] = 8, 13862306a36Sopenharmony_ci [CIP_SFC_48000] = 8, 13962306a36Sopenharmony_ci [CIP_SFC_88200] = 16, 14062306a36Sopenharmony_ci [CIP_SFC_96000] = 16, 14162306a36Sopenharmony_ci [CIP_SFC_176400] = 32, 14262306a36Sopenharmony_ci [CIP_SFC_192000] = 32, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_syt_intervals); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciconst unsigned int amdtp_rate_table[CIP_SFC_COUNT] = { 14762306a36Sopenharmony_ci [CIP_SFC_32000] = 32000, 14862306a36Sopenharmony_ci [CIP_SFC_44100] = 44100, 14962306a36Sopenharmony_ci [CIP_SFC_48000] = 48000, 15062306a36Sopenharmony_ci [CIP_SFC_88200] = 88200, 15162306a36Sopenharmony_ci [CIP_SFC_96000] = 96000, 15262306a36Sopenharmony_ci [CIP_SFC_176400] = 176400, 15362306a36Sopenharmony_ci [CIP_SFC_192000] = 192000, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_rate_table); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int apply_constraint_to_size(struct snd_pcm_hw_params *params, 15862306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct snd_interval *s = hw_param_interval(params, rule->var); 16162306a36Sopenharmony_ci const struct snd_interval *r = 16262306a36Sopenharmony_ci hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); 16362306a36Sopenharmony_ci struct snd_interval t = {0}; 16462306a36Sopenharmony_ci unsigned int step = 0; 16562306a36Sopenharmony_ci int i; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (i = 0; i < CIP_SFC_COUNT; ++i) { 16862306a36Sopenharmony_ci if (snd_interval_test(r, amdtp_rate_table[i])) 16962306a36Sopenharmony_ci step = max(step, amdtp_syt_intervals[i]); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci t.min = roundup(s->min, step); 17362306a36Sopenharmony_ci t.max = rounddown(s->max, step); 17462306a36Sopenharmony_ci t.integer = 1; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return snd_interval_refine(s, &t); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/** 18062306a36Sopenharmony_ci * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream 18162306a36Sopenharmony_ci * @s: the AMDTP stream, which must be initialized. 18262306a36Sopenharmony_ci * @runtime: the PCM substream runtime 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ciint amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, 18562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct snd_pcm_hardware *hw = &runtime->hw; 18862306a36Sopenharmony_ci unsigned int ctx_header_size; 18962306a36Sopenharmony_ci unsigned int maximum_usec_per_period; 19062306a36Sopenharmony_ci int err; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci hw->info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 19362306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 19462306a36Sopenharmony_ci SNDRV_PCM_INFO_JOINT_DUPLEX | 19562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 19662306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 19762306a36Sopenharmony_ci SNDRV_PCM_INFO_NO_PERIOD_WAKEUP; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci hw->periods_min = 2; 20062306a36Sopenharmony_ci hw->periods_max = UINT_MAX; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* bytes for a frame */ 20362306a36Sopenharmony_ci hw->period_bytes_min = 4 * hw->channels_max; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Just to prevent from allocating much pages. */ 20662306a36Sopenharmony_ci hw->period_bytes_max = hw->period_bytes_min * 2048; 20762306a36Sopenharmony_ci hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci // Linux driver for 1394 OHCI controller voluntarily flushes isoc 21062306a36Sopenharmony_ci // context when total size of accumulated context header reaches 21162306a36Sopenharmony_ci // PAGE_SIZE. This kicks work for the isoc context and brings 21262306a36Sopenharmony_ci // callback in the middle of scheduled interrupts. 21362306a36Sopenharmony_ci // Although AMDTP streams in the same domain use the same events per 21462306a36Sopenharmony_ci // IRQ, use the largest size of context header between IT/IR contexts. 21562306a36Sopenharmony_ci // Here, use the value of context header in IR context is for both 21662306a36Sopenharmony_ci // contexts. 21762306a36Sopenharmony_ci if (!(s->flags & CIP_NO_HEADER)) 21862306a36Sopenharmony_ci ctx_header_size = IR_CTX_HEADER_SIZE_CIP; 21962306a36Sopenharmony_ci else 22062306a36Sopenharmony_ci ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP; 22162306a36Sopenharmony_ci maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE / 22262306a36Sopenharmony_ci CYCLES_PER_SECOND / ctx_header_size; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci // In IEC 61883-6, one isoc packet can transfer events up to the value 22562306a36Sopenharmony_ci // of syt interval. This comes from the interval of isoc cycle. As 1394 22662306a36Sopenharmony_ci // OHCI controller can generate hardware IRQ per isoc packet, the 22762306a36Sopenharmony_ci // interval is 125 usec. 22862306a36Sopenharmony_ci // However, there are two ways of transmission in IEC 61883-6; blocking 22962306a36Sopenharmony_ci // and non-blocking modes. In blocking mode, the sequence of isoc packet 23062306a36Sopenharmony_ci // includes 'empty' or 'NODATA' packets which include no event. In 23162306a36Sopenharmony_ci // non-blocking mode, the number of events per packet is variable up to 23262306a36Sopenharmony_ci // the syt interval. 23362306a36Sopenharmony_ci // Due to the above protocol design, the minimum PCM frames per 23462306a36Sopenharmony_ci // interrupt should be double of the value of syt interval, thus it is 23562306a36Sopenharmony_ci // 250 usec. 23662306a36Sopenharmony_ci err = snd_pcm_hw_constraint_minmax(runtime, 23762306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_TIME, 23862306a36Sopenharmony_ci 250, maximum_usec_per_period); 23962306a36Sopenharmony_ci if (err < 0) 24062306a36Sopenharmony_ci goto end; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Non-Blocking stream has no more constraints */ 24362306a36Sopenharmony_ci if (!(s->flags & CIP_BLOCKING)) 24462306a36Sopenharmony_ci goto end; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * One AMDTP packet can include some frames. In blocking mode, the 24862306a36Sopenharmony_ci * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, 24962306a36Sopenharmony_ci * depending on its sampling rate. For accurate period interrupt, it's 25062306a36Sopenharmony_ci * preferrable to align period/buffer sizes to current SYT_INTERVAL. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 25362306a36Sopenharmony_ci apply_constraint_to_size, NULL, 25462306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 25562306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 25662306a36Sopenharmony_ci if (err < 0) 25762306a36Sopenharmony_ci goto end; 25862306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 25962306a36Sopenharmony_ci apply_constraint_to_size, NULL, 26062306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 26162306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 26262306a36Sopenharmony_ci if (err < 0) 26362306a36Sopenharmony_ci goto end; 26462306a36Sopenharmony_ciend: 26562306a36Sopenharmony_ci return err; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/** 27062306a36Sopenharmony_ci * amdtp_stream_set_parameters - set stream parameters 27162306a36Sopenharmony_ci * @s: the AMDTP stream to configure 27262306a36Sopenharmony_ci * @rate: the sample rate 27362306a36Sopenharmony_ci * @data_block_quadlets: the size of a data block in quadlet unit 27462306a36Sopenharmony_ci * @pcm_frame_multiplier: the multiplier to compute the number of PCM frames by the number of AMDTP 27562306a36Sopenharmony_ci * events. 27662306a36Sopenharmony_ci * 27762306a36Sopenharmony_ci * The parameters must be set before the stream is started, and must not be 27862306a36Sopenharmony_ci * changed while the stream is running. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ciint amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, 28162306a36Sopenharmony_ci unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci unsigned int sfc; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { 28662306a36Sopenharmony_ci if (amdtp_rate_table[sfc] == rate) 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci if (sfc == ARRAY_SIZE(amdtp_rate_table)) 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci s->sfc = sfc; 29362306a36Sopenharmony_ci s->data_block_quadlets = data_block_quadlets; 29462306a36Sopenharmony_ci s->syt_interval = amdtp_syt_intervals[sfc]; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci // default buffering in the device. 29762306a36Sopenharmony_ci s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci // additional buffering needed to adjust for no-data packets. 30062306a36Sopenharmony_ci if (s->flags & CIP_BLOCKING) 30162306a36Sopenharmony_ci s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci s->pcm_frame_multiplier = pcm_frame_multiplier; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_set_parameters); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci// The CIP header is processed in context header apart from context payload. 31062306a36Sopenharmony_cistatic int amdtp_stream_get_max_ctx_payload_size(struct amdtp_stream *s) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci unsigned int multiplier; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (s->flags & CIP_JUMBO_PAYLOAD) 31562306a36Sopenharmony_ci multiplier = IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES; 31662306a36Sopenharmony_ci else 31762306a36Sopenharmony_ci multiplier = 1; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci/** 32362306a36Sopenharmony_ci * amdtp_stream_get_max_payload - get the stream's packet size 32462306a36Sopenharmony_ci * @s: the AMDTP stream 32562306a36Sopenharmony_ci * 32662306a36Sopenharmony_ci * This function must not be called before the stream has been configured 32762306a36Sopenharmony_ci * with amdtp_stream_set_parameters(). 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ciunsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci unsigned int cip_header_size; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!(s->flags & CIP_NO_HEADER)) 33462306a36Sopenharmony_ci cip_header_size = CIP_HEADER_SIZE; 33562306a36Sopenharmony_ci else 33662306a36Sopenharmony_ci cip_header_size = 0; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return cip_header_size + amdtp_stream_get_max_ctx_payload_size(s); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_get_max_payload); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/** 34362306a36Sopenharmony_ci * amdtp_stream_pcm_prepare - prepare PCM device for running 34462306a36Sopenharmony_ci * @s: the AMDTP stream 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * This function should be called from the PCM device's .prepare callback. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_civoid amdtp_stream_pcm_prepare(struct amdtp_stream *s) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci s->pcm_buffer_pointer = 0; 35162306a36Sopenharmony_ci s->pcm_period_pointer = 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_pcm_prepare); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci#define prev_packet_desc(s, desc) \ 35662306a36Sopenharmony_ci list_prev_entry_circular(desc, &s->packet_descs_list, link) 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic void pool_blocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs, 35962306a36Sopenharmony_ci unsigned int size, unsigned int pos, unsigned int count) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci const unsigned int syt_interval = s->syt_interval; 36262306a36Sopenharmony_ci int i; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 36562306a36Sopenharmony_ci struct seq_desc *desc = descs + pos; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (desc->syt_offset != CIP_SYT_NO_INFO) 36862306a36Sopenharmony_ci desc->data_blocks = syt_interval; 36962306a36Sopenharmony_ci else 37062306a36Sopenharmony_ci desc->data_blocks = 0; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci pos = (pos + 1) % size; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs, 37762306a36Sopenharmony_ci unsigned int size, unsigned int pos, 37862306a36Sopenharmony_ci unsigned int count) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci const enum cip_sfc sfc = s->sfc; 38162306a36Sopenharmony_ci unsigned int state = s->ctx_data.rx.data_block_state; 38262306a36Sopenharmony_ci int i; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 38562306a36Sopenharmony_ci struct seq_desc *desc = descs + pos; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!cip_sfc_is_base_44100(sfc)) { 38862306a36Sopenharmony_ci // Sample_rate / 8000 is an integer, and precomputed. 38962306a36Sopenharmony_ci desc->data_blocks = state; 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci unsigned int phase = state; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * This calculates the number of data blocks per packet so that 39562306a36Sopenharmony_ci * 1) the overall rate is correct and exactly synchronized to 39662306a36Sopenharmony_ci * the bus clock, and 39762306a36Sopenharmony_ci * 2) packets with a rounded-up number of blocks occur as early 39862306a36Sopenharmony_ci * as possible in the sequence (to prevent underruns of the 39962306a36Sopenharmony_ci * device's buffer). 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci if (sfc == CIP_SFC_44100) 40262306a36Sopenharmony_ci /* 6 6 5 6 5 6 5 ... */ 40362306a36Sopenharmony_ci desc->data_blocks = 5 + ((phase & 1) ^ (phase == 0 || phase >= 40)); 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ 40662306a36Sopenharmony_ci desc->data_blocks = 11 * (sfc >> 1) + (phase == 0); 40762306a36Sopenharmony_ci if (++phase >= (80 >> (sfc >> 1))) 40862306a36Sopenharmony_ci phase = 0; 40962306a36Sopenharmony_ci state = phase; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci pos = (pos + 1) % size; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci s->ctx_data.rx.data_block_state = state; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic unsigned int calculate_syt_offset(unsigned int *last_syt_offset, 41962306a36Sopenharmony_ci unsigned int *syt_offset_state, enum cip_sfc sfc) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci unsigned int syt_offset; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (*last_syt_offset < TICKS_PER_CYCLE) { 42462306a36Sopenharmony_ci if (!cip_sfc_is_base_44100(sfc)) 42562306a36Sopenharmony_ci syt_offset = *last_syt_offset + *syt_offset_state; 42662306a36Sopenharmony_ci else { 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * The time, in ticks, of the n'th SYT_INTERVAL sample is: 42962306a36Sopenharmony_ci * n * SYT_INTERVAL * 24576000 / sample_rate 43062306a36Sopenharmony_ci * Modulo TICKS_PER_CYCLE, the difference between successive 43162306a36Sopenharmony_ci * elements is about 1386.23. Rounding the results of this 43262306a36Sopenharmony_ci * formula to the SYT precision results in a sequence of 43362306a36Sopenharmony_ci * differences that begins with: 43462306a36Sopenharmony_ci * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... 43562306a36Sopenharmony_ci * This code generates _exactly_ the same sequence. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci unsigned int phase = *syt_offset_state; 43862306a36Sopenharmony_ci unsigned int index = phase % 13; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci syt_offset = *last_syt_offset; 44162306a36Sopenharmony_ci syt_offset += 1386 + ((index && !(index & 3)) || 44262306a36Sopenharmony_ci phase == 146); 44362306a36Sopenharmony_ci if (++phase >= 147) 44462306a36Sopenharmony_ci phase = 0; 44562306a36Sopenharmony_ci *syt_offset_state = phase; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci } else 44862306a36Sopenharmony_ci syt_offset = *last_syt_offset - TICKS_PER_CYCLE; 44962306a36Sopenharmony_ci *last_syt_offset = syt_offset; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (syt_offset >= TICKS_PER_CYCLE) 45262306a36Sopenharmony_ci syt_offset = CIP_SYT_NO_INFO; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return syt_offset; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *descs, 45862306a36Sopenharmony_ci unsigned int size, unsigned int pos, unsigned int count) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci const enum cip_sfc sfc = s->sfc; 46162306a36Sopenharmony_ci unsigned int last = s->ctx_data.rx.last_syt_offset; 46262306a36Sopenharmony_ci unsigned int state = s->ctx_data.rx.syt_offset_state; 46362306a36Sopenharmony_ci int i; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 46662306a36Sopenharmony_ci struct seq_desc *desc = descs + pos; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci desc->syt_offset = calculate_syt_offset(&last, &state, sfc); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci pos = (pos + 1) % size; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci s->ctx_data.rx.last_syt_offset = last; 47462306a36Sopenharmony_ci s->ctx_data.rx.syt_offset_state = state; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic unsigned int compute_syt_offset(unsigned int syt, unsigned int cycle, 47862306a36Sopenharmony_ci unsigned int transfer_delay) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci unsigned int cycle_lo = (cycle % CYCLES_PER_SECOND) & 0x0f; 48162306a36Sopenharmony_ci unsigned int syt_cycle_lo = (syt & 0xf000) >> 12; 48262306a36Sopenharmony_ci unsigned int syt_offset; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci // Round up. 48562306a36Sopenharmony_ci if (syt_cycle_lo < cycle_lo) 48662306a36Sopenharmony_ci syt_cycle_lo += CIP_SYT_CYCLE_MODULUS; 48762306a36Sopenharmony_ci syt_cycle_lo -= cycle_lo; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci // Subtract transfer delay so that the synchronization offset is not so large 49062306a36Sopenharmony_ci // at transmission. 49162306a36Sopenharmony_ci syt_offset = syt_cycle_lo * TICKS_PER_CYCLE + (syt & 0x0fff); 49262306a36Sopenharmony_ci if (syt_offset < transfer_delay) 49362306a36Sopenharmony_ci syt_offset += CIP_SYT_CYCLE_MODULUS * TICKS_PER_CYCLE; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return syt_offset - transfer_delay; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci// Both of the producer and consumer of the queue runs in the same clock of IEEE 1394 bus. 49962306a36Sopenharmony_ci// Additionally, the sequence of tx packets is severely checked against any discontinuity 50062306a36Sopenharmony_ci// before filling entries in the queue. The calculation is safe even if it looks fragile by 50162306a36Sopenharmony_ci// overrun. 50262306a36Sopenharmony_cistatic unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigned int head) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci const unsigned int cache_size = s->ctx_data.tx.cache.size; 50562306a36Sopenharmony_ci unsigned int cycles = s->ctx_data.tx.cache.pos; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (cycles < head) 50862306a36Sopenharmony_ci cycles += cache_size; 50962306a36Sopenharmony_ci cycles -= head; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return cycles; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void cache_seq(struct amdtp_stream *s, const struct pkt_desc *src, unsigned int desc_count) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci const unsigned int transfer_delay = s->transfer_delay; 51762306a36Sopenharmony_ci const unsigned int cache_size = s->ctx_data.tx.cache.size; 51862306a36Sopenharmony_ci struct seq_desc *cache = s->ctx_data.tx.cache.descs; 51962306a36Sopenharmony_ci unsigned int cache_pos = s->ctx_data.tx.cache.pos; 52062306a36Sopenharmony_ci bool aware_syt = !(s->flags & CIP_UNAWARE_SYT); 52162306a36Sopenharmony_ci int i; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci for (i = 0; i < desc_count; ++i) { 52462306a36Sopenharmony_ci struct seq_desc *dst = cache + cache_pos; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (aware_syt && src->syt != CIP_SYT_NO_INFO) 52762306a36Sopenharmony_ci dst->syt_offset = compute_syt_offset(src->syt, src->cycle, transfer_delay); 52862306a36Sopenharmony_ci else 52962306a36Sopenharmony_ci dst->syt_offset = CIP_SYT_NO_INFO; 53062306a36Sopenharmony_ci dst->data_blocks = src->data_blocks; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci cache_pos = (cache_pos + 1) % cache_size; 53362306a36Sopenharmony_ci src = amdtp_stream_next_packet_desc(s, src); 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci s->ctx_data.tx.cache.pos = cache_pos; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic void pool_ideal_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, 54062306a36Sopenharmony_ci unsigned int pos, unsigned int count) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci pool_ideal_syt_offsets(s, descs, size, pos, count); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (s->flags & CIP_BLOCKING) 54562306a36Sopenharmony_ci pool_blocking_data_blocks(s, descs, size, pos, count); 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci pool_ideal_nonblocking_data_blocks(s, descs, size, pos, count); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void pool_replayed_seq(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, 55162306a36Sopenharmony_ci unsigned int pos, unsigned int count) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct amdtp_stream *target = s->ctx_data.rx.replay_target; 55462306a36Sopenharmony_ci const struct seq_desc *cache = target->ctx_data.tx.cache.descs; 55562306a36Sopenharmony_ci const unsigned int cache_size = target->ctx_data.tx.cache.size; 55662306a36Sopenharmony_ci unsigned int cache_pos = s->ctx_data.rx.cache_pos; 55762306a36Sopenharmony_ci int i; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 56062306a36Sopenharmony_ci descs[pos] = cache[cache_pos]; 56162306a36Sopenharmony_ci cache_pos = (cache_pos + 1) % cache_size; 56262306a36Sopenharmony_ci pos = (pos + 1) % size; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci s->ctx_data.rx.cache_pos = cache_pos; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void pool_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, 56962306a36Sopenharmony_ci unsigned int pos, unsigned int count) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 57262306a36Sopenharmony_ci void (*pool_seq_descs)(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size, 57362306a36Sopenharmony_ci unsigned int pos, unsigned int count); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (!d->replay.enable || !s->ctx_data.rx.replay_target) { 57662306a36Sopenharmony_ci pool_seq_descs = pool_ideal_seq_descs; 57762306a36Sopenharmony_ci } else { 57862306a36Sopenharmony_ci if (!d->replay.on_the_fly) { 57962306a36Sopenharmony_ci pool_seq_descs = pool_replayed_seq; 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci struct amdtp_stream *tx = s->ctx_data.rx.replay_target; 58262306a36Sopenharmony_ci const unsigned int cache_size = tx->ctx_data.tx.cache.size; 58362306a36Sopenharmony_ci const unsigned int cache_pos = s->ctx_data.rx.cache_pos; 58462306a36Sopenharmony_ci unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_pos); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (cached_cycles > count && cached_cycles > cache_size / 2) 58762306a36Sopenharmony_ci pool_seq_descs = pool_replayed_seq; 58862306a36Sopenharmony_ci else 58962306a36Sopenharmony_ci pool_seq_descs = pool_ideal_seq_descs; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci pool_seq_descs(s, descs, size, pos, count); 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic void update_pcm_pointers(struct amdtp_stream *s, 59762306a36Sopenharmony_ci struct snd_pcm_substream *pcm, 59862306a36Sopenharmony_ci unsigned int frames) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci unsigned int ptr; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ptr = s->pcm_buffer_pointer + frames; 60362306a36Sopenharmony_ci if (ptr >= pcm->runtime->buffer_size) 60462306a36Sopenharmony_ci ptr -= pcm->runtime->buffer_size; 60562306a36Sopenharmony_ci WRITE_ONCE(s->pcm_buffer_pointer, ptr); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci s->pcm_period_pointer += frames; 60862306a36Sopenharmony_ci if (s->pcm_period_pointer >= pcm->runtime->period_size) { 60962306a36Sopenharmony_ci s->pcm_period_pointer -= pcm->runtime->period_size; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci // The program in user process should periodically check the status of intermediate 61262306a36Sopenharmony_ci // buffer associated to PCM substream to process PCM frames in the buffer, instead 61362306a36Sopenharmony_ci // of receiving notification of period elapsed by poll wait. 61462306a36Sopenharmony_ci if (!pcm->runtime->no_period_wakeup) { 61562306a36Sopenharmony_ci if (in_softirq()) { 61662306a36Sopenharmony_ci // In software IRQ context for 1394 OHCI. 61762306a36Sopenharmony_ci snd_pcm_period_elapsed(pcm); 61862306a36Sopenharmony_ci } else { 61962306a36Sopenharmony_ci // In process context of ALSA PCM application under acquired lock of 62062306a36Sopenharmony_ci // PCM substream. 62162306a36Sopenharmony_ci snd_pcm_period_elapsed_under_stream_lock(pcm); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params, 62862306a36Sopenharmony_ci bool sched_irq) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci int err; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci params->interrupt = sched_irq; 63362306a36Sopenharmony_ci params->tag = s->tag; 63462306a36Sopenharmony_ci params->sy = 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci err = fw_iso_context_queue(s->context, params, &s->buffer.iso_buffer, 63762306a36Sopenharmony_ci s->buffer.packets[s->packet_index].offset); 63862306a36Sopenharmony_ci if (err < 0) { 63962306a36Sopenharmony_ci dev_err(&s->unit->device, "queueing error: %d\n", err); 64062306a36Sopenharmony_ci goto end; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (++s->packet_index >= s->queue_size) 64462306a36Sopenharmony_ci s->packet_index = 0; 64562306a36Sopenharmony_ciend: 64662306a36Sopenharmony_ci return err; 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic inline int queue_out_packet(struct amdtp_stream *s, 65062306a36Sopenharmony_ci struct fw_iso_packet *params, bool sched_irq) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci params->skip = 65362306a36Sopenharmony_ci !!(params->header_length == 0 && params->payload_length == 0); 65462306a36Sopenharmony_ci return queue_packet(s, params, sched_irq); 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic inline int queue_in_packet(struct amdtp_stream *s, 65862306a36Sopenharmony_ci struct fw_iso_packet *params) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci // Queue one packet for IR context. 66162306a36Sopenharmony_ci params->header_length = s->ctx_data.tx.ctx_header_size; 66262306a36Sopenharmony_ci params->payload_length = s->ctx_data.tx.max_ctx_payload_length; 66362306a36Sopenharmony_ci params->skip = false; 66462306a36Sopenharmony_ci return queue_packet(s, params, false); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], 66862306a36Sopenharmony_ci unsigned int data_block_counter, unsigned int syt) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) | 67162306a36Sopenharmony_ci (s->data_block_quadlets << CIP_DBS_SHIFT) | 67262306a36Sopenharmony_ci ((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) | 67362306a36Sopenharmony_ci data_block_counter); 67462306a36Sopenharmony_ci cip_header[1] = cpu_to_be32(CIP_EOH | 67562306a36Sopenharmony_ci ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | 67662306a36Sopenharmony_ci ((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | 67762306a36Sopenharmony_ci (syt & CIP_SYT_MASK)); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, 68162306a36Sopenharmony_ci struct fw_iso_packet *params, unsigned int header_length, 68262306a36Sopenharmony_ci unsigned int data_blocks, 68362306a36Sopenharmony_ci unsigned int data_block_counter, 68462306a36Sopenharmony_ci unsigned int syt, unsigned int index, u32 curr_cycle_time) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci unsigned int payload_length; 68762306a36Sopenharmony_ci __be32 *cip_header; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets; 69062306a36Sopenharmony_ci params->payload_length = payload_length; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (header_length > 0) { 69362306a36Sopenharmony_ci cip_header = (__be32 *)params->header; 69462306a36Sopenharmony_ci generate_cip_header(s, cip_header, data_block_counter, syt); 69562306a36Sopenharmony_ci params->header_length = header_length; 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci cip_header = NULL; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci trace_amdtp_packet(s, cycle, cip_header, payload_length + header_length, data_blocks, 70162306a36Sopenharmony_ci data_block_counter, s->packet_index, index, curr_cycle_time); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int check_cip_header(struct amdtp_stream *s, const __be32 *buf, 70562306a36Sopenharmony_ci unsigned int payload_length, 70662306a36Sopenharmony_ci unsigned int *data_blocks, 70762306a36Sopenharmony_ci unsigned int *data_block_counter, unsigned int *syt) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci u32 cip_header[2]; 71062306a36Sopenharmony_ci unsigned int sph; 71162306a36Sopenharmony_ci unsigned int fmt; 71262306a36Sopenharmony_ci unsigned int fdf; 71362306a36Sopenharmony_ci unsigned int dbc; 71462306a36Sopenharmony_ci bool lost; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci cip_header[0] = be32_to_cpu(buf[0]); 71762306a36Sopenharmony_ci cip_header[1] = be32_to_cpu(buf[1]); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * This module supports 'Two-quadlet CIP header with SYT field'. 72162306a36Sopenharmony_ci * For convenience, also check FMT field is AM824 or not. 72262306a36Sopenharmony_ci */ 72362306a36Sopenharmony_ci if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || 72462306a36Sopenharmony_ci ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) && 72562306a36Sopenharmony_ci (!(s->flags & CIP_HEADER_WITHOUT_EOH))) { 72662306a36Sopenharmony_ci dev_info_ratelimited(&s->unit->device, 72762306a36Sopenharmony_ci "Invalid CIP header for AMDTP: %08X:%08X\n", 72862306a36Sopenharmony_ci cip_header[0], cip_header[1]); 72962306a36Sopenharmony_ci return -EAGAIN; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Check valid protocol or not. */ 73362306a36Sopenharmony_ci sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT; 73462306a36Sopenharmony_ci fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; 73562306a36Sopenharmony_ci if (sph != s->sph || fmt != s->fmt) { 73662306a36Sopenharmony_ci dev_info_ratelimited(&s->unit->device, 73762306a36Sopenharmony_ci "Detect unexpected protocol: %08x %08x\n", 73862306a36Sopenharmony_ci cip_header[0], cip_header[1]); 73962306a36Sopenharmony_ci return -EAGAIN; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* Calculate data blocks */ 74362306a36Sopenharmony_ci fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; 74462306a36Sopenharmony_ci if (payload_length == 0 || (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { 74562306a36Sopenharmony_ci *data_blocks = 0; 74662306a36Sopenharmony_ci } else { 74762306a36Sopenharmony_ci unsigned int data_block_quadlets = 74862306a36Sopenharmony_ci (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; 74962306a36Sopenharmony_ci /* avoid division by zero */ 75062306a36Sopenharmony_ci if (data_block_quadlets == 0) { 75162306a36Sopenharmony_ci dev_err(&s->unit->device, 75262306a36Sopenharmony_ci "Detect invalid value in dbs field: %08X\n", 75362306a36Sopenharmony_ci cip_header[0]); 75462306a36Sopenharmony_ci return -EPROTO; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci if (s->flags & CIP_WRONG_DBS) 75762306a36Sopenharmony_ci data_block_quadlets = s->data_block_quadlets; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci *data_blocks = payload_length / sizeof(__be32) / data_block_quadlets; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Check data block counter continuity */ 76362306a36Sopenharmony_ci dbc = cip_header[0] & CIP_DBC_MASK; 76462306a36Sopenharmony_ci if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && 76562306a36Sopenharmony_ci *data_block_counter != UINT_MAX) 76662306a36Sopenharmony_ci dbc = *data_block_counter; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if ((dbc == 0x00 && (s->flags & CIP_SKIP_DBC_ZERO_CHECK)) || 76962306a36Sopenharmony_ci *data_block_counter == UINT_MAX) { 77062306a36Sopenharmony_ci lost = false; 77162306a36Sopenharmony_ci } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { 77262306a36Sopenharmony_ci lost = dbc != *data_block_counter; 77362306a36Sopenharmony_ci } else { 77462306a36Sopenharmony_ci unsigned int dbc_interval; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0) 77762306a36Sopenharmony_ci dbc_interval = s->ctx_data.tx.dbc_interval; 77862306a36Sopenharmony_ci else 77962306a36Sopenharmony_ci dbc_interval = *data_blocks; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci lost = dbc != ((*data_block_counter + dbc_interval) & 0xff); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (lost) { 78562306a36Sopenharmony_ci dev_err(&s->unit->device, 78662306a36Sopenharmony_ci "Detect discontinuity of CIP: %02X %02X\n", 78762306a36Sopenharmony_ci *data_block_counter, dbc); 78862306a36Sopenharmony_ci return -EIO; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci *data_block_counter = dbc; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (!(s->flags & CIP_UNAWARE_SYT)) 79462306a36Sopenharmony_ci *syt = cip_header[1] & CIP_SYT_MASK; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, 80062306a36Sopenharmony_ci const __be32 *ctx_header, 80162306a36Sopenharmony_ci unsigned int *data_blocks, 80262306a36Sopenharmony_ci unsigned int *data_block_counter, 80362306a36Sopenharmony_ci unsigned int *syt, unsigned int packet_index, unsigned int index, 80462306a36Sopenharmony_ci u32 curr_cycle_time) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci unsigned int payload_length; 80762306a36Sopenharmony_ci const __be32 *cip_header; 80862306a36Sopenharmony_ci unsigned int cip_header_size; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci payload_length = be32_to_cpu(ctx_header[0]) >> ISO_DATA_LENGTH_SHIFT; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (!(s->flags & CIP_NO_HEADER)) 81362306a36Sopenharmony_ci cip_header_size = CIP_HEADER_SIZE; 81462306a36Sopenharmony_ci else 81562306a36Sopenharmony_ci cip_header_size = 0; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (payload_length > cip_header_size + s->ctx_data.tx.max_ctx_payload_length) { 81862306a36Sopenharmony_ci dev_err(&s->unit->device, 81962306a36Sopenharmony_ci "Detect jumbo payload: %04x %04x\n", 82062306a36Sopenharmony_ci payload_length, cip_header_size + s->ctx_data.tx.max_ctx_payload_length); 82162306a36Sopenharmony_ci return -EIO; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (cip_header_size > 0) { 82562306a36Sopenharmony_ci if (payload_length >= cip_header_size) { 82662306a36Sopenharmony_ci int err; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci cip_header = ctx_header + IR_CTX_HEADER_DEFAULT_QUADLETS; 82962306a36Sopenharmony_ci err = check_cip_header(s, cip_header, payload_length - cip_header_size, 83062306a36Sopenharmony_ci data_blocks, data_block_counter, syt); 83162306a36Sopenharmony_ci if (err < 0) 83262306a36Sopenharmony_ci return err; 83362306a36Sopenharmony_ci } else { 83462306a36Sopenharmony_ci // Handle the cycle so that empty packet arrives. 83562306a36Sopenharmony_ci cip_header = NULL; 83662306a36Sopenharmony_ci *data_blocks = 0; 83762306a36Sopenharmony_ci *syt = 0; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci } else { 84062306a36Sopenharmony_ci cip_header = NULL; 84162306a36Sopenharmony_ci *data_blocks = payload_length / sizeof(__be32) / s->data_block_quadlets; 84262306a36Sopenharmony_ci *syt = 0; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (*data_block_counter == UINT_MAX) 84562306a36Sopenharmony_ci *data_block_counter = 0; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci trace_amdtp_packet(s, cycle, cip_header, payload_length, *data_blocks, 84962306a36Sopenharmony_ci *data_block_counter, packet_index, index, curr_cycle_time); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci// In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On 85562306a36Sopenharmony_ci// the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent 85662306a36Sopenharmony_ci// it. Thus, via Linux firewire subsystem, we can get the 3 bits for second. 85762306a36Sopenharmony_cistatic inline u32 compute_ohci_iso_ctx_cycle_count(u32 tstamp) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci return (((tstamp >> 13) & 0x07) * CYCLES_PER_SECOND) + (tstamp & 0x1fff); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic inline u32 compute_ohci_cycle_count(__be32 ctx_header_tstamp) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK; 86562306a36Sopenharmony_ci return compute_ohci_iso_ctx_cycle_count(tstamp); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci cycle += addend; 87162306a36Sopenharmony_ci if (cycle >= OHCI_SECOND_MODULUS * CYCLES_PER_SECOND) 87262306a36Sopenharmony_ci cycle -= OHCI_SECOND_MODULUS * CYCLES_PER_SECOND; 87362306a36Sopenharmony_ci return cycle; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic inline u32 decrement_ohci_cycle_count(u32 minuend, u32 subtrahend) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci if (minuend < subtrahend) 87962306a36Sopenharmony_ci minuend += OHCI_SECOND_MODULUS * CYCLES_PER_SECOND; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return minuend - subtrahend; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int compare_ohci_cycle_count(u32 lval, u32 rval) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci if (lval == rval) 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci else if (lval < rval && rval - lval < OHCI_SECOND_MODULUS * CYCLES_PER_SECOND / 2) 88962306a36Sopenharmony_ci return -1; 89062306a36Sopenharmony_ci else 89162306a36Sopenharmony_ci return 1; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci// Align to actual cycle count for the packet which is going to be scheduled. 89562306a36Sopenharmony_ci// This module queued the same number of isochronous cycle as the size of queue 89662306a36Sopenharmony_ci// to kip isochronous cycle, therefore it's OK to just increment the cycle by 89762306a36Sopenharmony_ci// the size of queue for scheduled cycle. 89862306a36Sopenharmony_cistatic inline u32 compute_ohci_it_cycle(const __be32 ctx_header_tstamp, 89962306a36Sopenharmony_ci unsigned int queue_size) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci u32 cycle = compute_ohci_cycle_count(ctx_header_tstamp); 90262306a36Sopenharmony_ci return increment_ohci_cycle_count(cycle, queue_size); 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc, 90662306a36Sopenharmony_ci const __be32 *ctx_header, unsigned int packet_count, 90762306a36Sopenharmony_ci unsigned int *desc_count) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci unsigned int next_cycle = s->next_cycle; 91062306a36Sopenharmony_ci unsigned int dbc = s->data_block_counter; 91162306a36Sopenharmony_ci unsigned int packet_index = s->packet_index; 91262306a36Sopenharmony_ci unsigned int queue_size = s->queue_size; 91362306a36Sopenharmony_ci u32 curr_cycle_time = 0; 91462306a36Sopenharmony_ci int i; 91562306a36Sopenharmony_ci int err; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (trace_amdtp_packet_enabled()) 91862306a36Sopenharmony_ci (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci *desc_count = 0; 92162306a36Sopenharmony_ci for (i = 0; i < packet_count; ++i) { 92262306a36Sopenharmony_ci unsigned int cycle; 92362306a36Sopenharmony_ci bool lost; 92462306a36Sopenharmony_ci unsigned int data_blocks; 92562306a36Sopenharmony_ci unsigned int syt; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci cycle = compute_ohci_cycle_count(ctx_header[1]); 92862306a36Sopenharmony_ci lost = (next_cycle != cycle); 92962306a36Sopenharmony_ci if (lost) { 93062306a36Sopenharmony_ci if (s->flags & CIP_NO_HEADER) { 93162306a36Sopenharmony_ci // Fireface skips transmission just for an isoc cycle corresponding 93262306a36Sopenharmony_ci // to empty packet. 93362306a36Sopenharmony_ci unsigned int prev_cycle = next_cycle; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci next_cycle = increment_ohci_cycle_count(next_cycle, 1); 93662306a36Sopenharmony_ci lost = (next_cycle != cycle); 93762306a36Sopenharmony_ci if (!lost) { 93862306a36Sopenharmony_ci // Prepare a description for the skipped cycle for 93962306a36Sopenharmony_ci // sequence replay. 94062306a36Sopenharmony_ci desc->cycle = prev_cycle; 94162306a36Sopenharmony_ci desc->syt = 0; 94262306a36Sopenharmony_ci desc->data_blocks = 0; 94362306a36Sopenharmony_ci desc->data_block_counter = dbc; 94462306a36Sopenharmony_ci desc->ctx_payload = NULL; 94562306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 94662306a36Sopenharmony_ci ++(*desc_count); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci } else if (s->flags & CIP_JUMBO_PAYLOAD) { 94962306a36Sopenharmony_ci // OXFW970 skips transmission for several isoc cycles during 95062306a36Sopenharmony_ci // asynchronous transaction. The sequence replay is impossible due 95162306a36Sopenharmony_ci // to the reason. 95262306a36Sopenharmony_ci unsigned int safe_cycle = increment_ohci_cycle_count(next_cycle, 95362306a36Sopenharmony_ci IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES); 95462306a36Sopenharmony_ci lost = (compare_ohci_cycle_count(safe_cycle, cycle) < 0); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci if (lost) { 95762306a36Sopenharmony_ci dev_err(&s->unit->device, "Detect discontinuity of cycle: %d %d\n", 95862306a36Sopenharmony_ci next_cycle, cycle); 95962306a36Sopenharmony_ci return -EIO; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci err = parse_ir_ctx_header(s, cycle, ctx_header, &data_blocks, &dbc, &syt, 96462306a36Sopenharmony_ci packet_index, i, curr_cycle_time); 96562306a36Sopenharmony_ci if (err < 0) 96662306a36Sopenharmony_ci return err; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci desc->cycle = cycle; 96962306a36Sopenharmony_ci desc->syt = syt; 97062306a36Sopenharmony_ci desc->data_blocks = data_blocks; 97162306a36Sopenharmony_ci desc->data_block_counter = dbc; 97262306a36Sopenharmony_ci desc->ctx_payload = s->buffer.packets[packet_index].buffer; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (!(s->flags & CIP_DBC_IS_END_EVENT)) 97562306a36Sopenharmony_ci dbc = (dbc + desc->data_blocks) & 0xff; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci next_cycle = increment_ohci_cycle_count(next_cycle, 1); 97862306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 97962306a36Sopenharmony_ci ++(*desc_count); 98062306a36Sopenharmony_ci ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); 98162306a36Sopenharmony_ci packet_index = (packet_index + 1) % queue_size; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci s->next_cycle = next_cycle; 98562306a36Sopenharmony_ci s->data_block_counter = dbc; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle, 99162306a36Sopenharmony_ci unsigned int transfer_delay) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci unsigned int syt; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci syt_offset += transfer_delay; 99662306a36Sopenharmony_ci syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) | 99762306a36Sopenharmony_ci (syt_offset % TICKS_PER_CYCLE); 99862306a36Sopenharmony_ci return syt & CIP_SYT_MASK; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc, 100262306a36Sopenharmony_ci const __be32 *ctx_header, unsigned int packet_count) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs; 100562306a36Sopenharmony_ci unsigned int seq_size = s->ctx_data.rx.seq.size; 100662306a36Sopenharmony_ci unsigned int seq_pos = s->ctx_data.rx.seq.pos; 100762306a36Sopenharmony_ci unsigned int dbc = s->data_block_counter; 100862306a36Sopenharmony_ci bool aware_syt = !(s->flags & CIP_UNAWARE_SYT); 100962306a36Sopenharmony_ci int i; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci pool_seq_descs(s, seq_descs, seq_size, seq_pos, packet_count); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci for (i = 0; i < packet_count; ++i) { 101462306a36Sopenharmony_ci unsigned int index = (s->packet_index + i) % s->queue_size; 101562306a36Sopenharmony_ci const struct seq_desc *seq = seq_descs + seq_pos; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci desc->cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (aware_syt && seq->syt_offset != CIP_SYT_NO_INFO) 102062306a36Sopenharmony_ci desc->syt = compute_syt(seq->syt_offset, desc->cycle, s->transfer_delay); 102162306a36Sopenharmony_ci else 102262306a36Sopenharmony_ci desc->syt = CIP_SYT_NO_INFO; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci desc->data_blocks = seq->data_blocks; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (s->flags & CIP_DBC_IS_END_EVENT) 102762306a36Sopenharmony_ci dbc = (dbc + desc->data_blocks) & 0xff; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci desc->data_block_counter = dbc; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (!(s->flags & CIP_DBC_IS_END_EVENT)) 103262306a36Sopenharmony_ci dbc = (dbc + desc->data_blocks) & 0xff; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci desc->ctx_payload = s->buffer.packets[index].buffer; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci seq_pos = (seq_pos + 1) % seq_size; 103762306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci ++ctx_header; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci s->data_block_counter = dbc; 104362306a36Sopenharmony_ci s->ctx_data.rx.seq.pos = seq_pos; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic inline void cancel_stream(struct amdtp_stream *s) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci s->packet_index = -1; 104962306a36Sopenharmony_ci if (in_softirq()) 105062306a36Sopenharmony_ci amdtp_stream_pcm_abort(s); 105162306a36Sopenharmony_ci WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic snd_pcm_sframes_t compute_pcm_extra_delay(struct amdtp_stream *s, 105562306a36Sopenharmony_ci const struct pkt_desc *desc, unsigned int count) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci unsigned int data_block_count = 0; 105862306a36Sopenharmony_ci u32 latest_cycle; 105962306a36Sopenharmony_ci u32 cycle_time; 106062306a36Sopenharmony_ci u32 curr_cycle; 106162306a36Sopenharmony_ci u32 cycle_gap; 106262306a36Sopenharmony_ci int i, err; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (count == 0) 106562306a36Sopenharmony_ci goto end; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci // Forward to the latest record. 106862306a36Sopenharmony_ci for (i = 0; i < count - 1; ++i) 106962306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 107062306a36Sopenharmony_ci latest_cycle = desc->cycle; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci err = fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &cycle_time); 107362306a36Sopenharmony_ci if (err < 0) 107462306a36Sopenharmony_ci goto end; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci // Compute cycle count with lower 3 bits of second field and cycle field like timestamp 107762306a36Sopenharmony_ci // format of 1394 OHCI isochronous context. 107862306a36Sopenharmony_ci curr_cycle = compute_ohci_iso_ctx_cycle_count((cycle_time >> 12) & 0x0000ffff); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 108162306a36Sopenharmony_ci // NOTE: The AMDTP packet descriptor should be for the past isochronous cycle since 108262306a36Sopenharmony_ci // it corresponds to arrived isochronous packet. 108362306a36Sopenharmony_ci if (compare_ohci_cycle_count(latest_cycle, curr_cycle) > 0) 108462306a36Sopenharmony_ci goto end; 108562306a36Sopenharmony_ci cycle_gap = decrement_ohci_cycle_count(curr_cycle, latest_cycle); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci // NOTE: estimate delay by recent history of arrived AMDTP packets. The estimated 108862306a36Sopenharmony_ci // value expectedly corresponds to a few packets (0-2) since the packet arrived at 108962306a36Sopenharmony_ci // the most recent isochronous cycle has been already processed. 109062306a36Sopenharmony_ci for (i = 0; i < cycle_gap; ++i) { 109162306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 109262306a36Sopenharmony_ci data_block_count += desc->data_blocks; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci } else { 109562306a36Sopenharmony_ci // NOTE: The AMDTP packet descriptor should be for the future isochronous cycle 109662306a36Sopenharmony_ci // since it was already scheduled. 109762306a36Sopenharmony_ci if (compare_ohci_cycle_count(latest_cycle, curr_cycle) < 0) 109862306a36Sopenharmony_ci goto end; 109962306a36Sopenharmony_ci cycle_gap = decrement_ohci_cycle_count(latest_cycle, curr_cycle); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci // NOTE: use history of scheduled packets. 110262306a36Sopenharmony_ci for (i = 0; i < cycle_gap; ++i) { 110362306a36Sopenharmony_ci data_block_count += desc->data_blocks; 110462306a36Sopenharmony_ci desc = prev_packet_desc(s, desc); 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ciend: 110862306a36Sopenharmony_ci return data_block_count * s->pcm_frame_multiplier; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic void process_ctx_payloads(struct amdtp_stream *s, 111262306a36Sopenharmony_ci const struct pkt_desc *desc, 111362306a36Sopenharmony_ci unsigned int count) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct snd_pcm_substream *pcm; 111662306a36Sopenharmony_ci int i; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci pcm = READ_ONCE(s->pcm); 111962306a36Sopenharmony_ci s->process_ctx_payloads(s, desc, count, pcm); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (pcm) { 112262306a36Sopenharmony_ci unsigned int data_block_count = 0; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci pcm->runtime->delay = compute_pcm_extra_delay(s, desc, count); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 112762306a36Sopenharmony_ci data_block_count += desc->data_blocks; 112862306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci update_pcm_pointers(s, pcm, data_block_count * s->pcm_frame_multiplier); 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, 113662306a36Sopenharmony_ci void *header, void *private_data) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 113962306a36Sopenharmony_ci const struct amdtp_domain *d = s->domain; 114062306a36Sopenharmony_ci const __be32 *ctx_header = header; 114162306a36Sopenharmony_ci const unsigned int events_per_period = d->events_per_period; 114262306a36Sopenharmony_ci unsigned int event_count = s->ctx_data.rx.event_count; 114362306a36Sopenharmony_ci struct pkt_desc *desc = s->packet_descs_cursor; 114462306a36Sopenharmony_ci unsigned int pkt_header_length; 114562306a36Sopenharmony_ci unsigned int packets; 114662306a36Sopenharmony_ci u32 curr_cycle_time; 114762306a36Sopenharmony_ci bool need_hw_irq; 114862306a36Sopenharmony_ci int i; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (s->packet_index < 0) 115162306a36Sopenharmony_ci return; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci // Calculate the number of packets in buffer and check XRUN. 115462306a36Sopenharmony_ci packets = header_length / sizeof(*ctx_header); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci generate_rx_packet_descs(s, desc, ctx_header, packets); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci process_ctx_payloads(s, desc, packets); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci if (!(s->flags & CIP_NO_HEADER)) 116162306a36Sopenharmony_ci pkt_header_length = IT_PKT_HEADER_SIZE_CIP; 116262306a36Sopenharmony_ci else 116362306a36Sopenharmony_ci pkt_header_length = 0; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci if (s == d->irq_target) { 116662306a36Sopenharmony_ci // At NO_PERIOD_WAKEUP mode, the packets for all IT/IR contexts are processed by 116762306a36Sopenharmony_ci // the tasks of user process operating ALSA PCM character device by calling ioctl(2) 116862306a36Sopenharmony_ci // with some requests, instead of scheduled hardware IRQ of an IT context. 116962306a36Sopenharmony_ci struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); 117062306a36Sopenharmony_ci need_hw_irq = !pcm || !pcm->runtime->no_period_wakeup; 117162306a36Sopenharmony_ci } else { 117262306a36Sopenharmony_ci need_hw_irq = false; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (trace_amdtp_packet_enabled()) 117662306a36Sopenharmony_ci (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci for (i = 0; i < packets; ++i) { 117962306a36Sopenharmony_ci struct { 118062306a36Sopenharmony_ci struct fw_iso_packet params; 118162306a36Sopenharmony_ci __be32 header[CIP_HEADER_QUADLETS]; 118262306a36Sopenharmony_ci } template = { {0}, {0} }; 118362306a36Sopenharmony_ci bool sched_irq = false; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length, 118662306a36Sopenharmony_ci desc->data_blocks, desc->data_block_counter, 118762306a36Sopenharmony_ci desc->syt, i, curr_cycle_time); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (s == s->domain->irq_target) { 119062306a36Sopenharmony_ci event_count += desc->data_blocks; 119162306a36Sopenharmony_ci if (event_count >= events_per_period) { 119262306a36Sopenharmony_ci event_count -= events_per_period; 119362306a36Sopenharmony_ci sched_irq = need_hw_irq; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (queue_out_packet(s, &template.params, sched_irq) < 0) { 119862306a36Sopenharmony_ci cancel_stream(s); 119962306a36Sopenharmony_ci return; 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci s->ctx_data.rx.event_count = event_count; 120662306a36Sopenharmony_ci s->packet_descs_cursor = desc; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistatic void skip_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, 121062306a36Sopenharmony_ci void *header, void *private_data) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 121362306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 121462306a36Sopenharmony_ci const __be32 *ctx_header = header; 121562306a36Sopenharmony_ci unsigned int packets; 121662306a36Sopenharmony_ci unsigned int cycle; 121762306a36Sopenharmony_ci int i; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci if (s->packet_index < 0) 122062306a36Sopenharmony_ci return; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci packets = header_length / sizeof(*ctx_header); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci cycle = compute_ohci_it_cycle(ctx_header[packets - 1], s->queue_size); 122562306a36Sopenharmony_ci s->next_cycle = increment_ohci_cycle_count(cycle, 1); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci for (i = 0; i < packets; ++i) { 122862306a36Sopenharmony_ci struct fw_iso_packet params = { 122962306a36Sopenharmony_ci .header_length = 0, 123062306a36Sopenharmony_ci .payload_length = 0, 123162306a36Sopenharmony_ci }; 123262306a36Sopenharmony_ci bool sched_irq = (s == d->irq_target && i == packets - 1); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci if (queue_out_packet(s, ¶ms, sched_irq) < 0) { 123562306a36Sopenharmony_ci cancel_stream(s); 123662306a36Sopenharmony_ci return; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, 124262306a36Sopenharmony_ci void *header, void *private_data); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic void process_rx_packets_intermediately(struct fw_iso_context *context, u32 tstamp, 124562306a36Sopenharmony_ci size_t header_length, void *header, void *private_data) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 124862306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 124962306a36Sopenharmony_ci __be32 *ctx_header = header; 125062306a36Sopenharmony_ci const unsigned int queue_size = s->queue_size; 125162306a36Sopenharmony_ci unsigned int packets; 125262306a36Sopenharmony_ci unsigned int offset; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (s->packet_index < 0) 125562306a36Sopenharmony_ci return; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci packets = header_length / sizeof(*ctx_header); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci offset = 0; 126062306a36Sopenharmony_ci while (offset < packets) { 126162306a36Sopenharmony_ci unsigned int cycle = compute_ohci_it_cycle(ctx_header[offset], queue_size); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (compare_ohci_cycle_count(cycle, d->processing_cycle.rx_start) >= 0) 126462306a36Sopenharmony_ci break; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci ++offset; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (offset > 0) { 127062306a36Sopenharmony_ci unsigned int length = sizeof(*ctx_header) * offset; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci skip_rx_packets(context, tstamp, length, ctx_header, private_data); 127362306a36Sopenharmony_ci if (amdtp_streaming_error(s)) 127462306a36Sopenharmony_ci return; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci ctx_header += offset; 127762306a36Sopenharmony_ci header_length -= length; 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci if (offset < packets) { 128162306a36Sopenharmony_ci s->ready_processing = true; 128262306a36Sopenharmony_ci wake_up(&s->ready_wait); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (d->replay.enable) 128562306a36Sopenharmony_ci s->ctx_data.rx.cache_pos = 0; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci process_rx_packets(context, tstamp, header_length, ctx_header, private_data); 128862306a36Sopenharmony_ci if (amdtp_streaming_error(s)) 128962306a36Sopenharmony_ci return; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (s == d->irq_target) 129262306a36Sopenharmony_ci s->context->callback.sc = irq_target_callback; 129362306a36Sopenharmony_ci else 129462306a36Sopenharmony_ci s->context->callback.sc = process_rx_packets; 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, 129962306a36Sopenharmony_ci void *header, void *private_data) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 130262306a36Sopenharmony_ci __be32 *ctx_header = header; 130362306a36Sopenharmony_ci struct pkt_desc *desc = s->packet_descs_cursor; 130462306a36Sopenharmony_ci unsigned int packet_count; 130562306a36Sopenharmony_ci unsigned int desc_count; 130662306a36Sopenharmony_ci int i; 130762306a36Sopenharmony_ci int err; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (s->packet_index < 0) 131062306a36Sopenharmony_ci return; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci // Calculate the number of packets in buffer and check XRUN. 131362306a36Sopenharmony_ci packet_count = header_length / s->ctx_data.tx.ctx_header_size; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci desc_count = 0; 131662306a36Sopenharmony_ci err = generate_tx_packet_descs(s, desc, ctx_header, packet_count, &desc_count); 131762306a36Sopenharmony_ci if (err < 0) { 131862306a36Sopenharmony_ci if (err != -EAGAIN) { 131962306a36Sopenharmony_ci cancel_stream(s); 132062306a36Sopenharmony_ci return; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci } else { 132362306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci process_ctx_payloads(s, desc, desc_count); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci if (d->replay.enable) 132862306a36Sopenharmony_ci cache_seq(s, desc, desc_count); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci for (i = 0; i < desc_count; ++i) 133162306a36Sopenharmony_ci desc = amdtp_stream_next_packet_desc(s, desc); 133262306a36Sopenharmony_ci s->packet_descs_cursor = desc; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci for (i = 0; i < packet_count; ++i) { 133662306a36Sopenharmony_ci struct fw_iso_packet params = {0}; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci if (queue_in_packet(s, ¶ms) < 0) { 133962306a36Sopenharmony_ci cancel_stream(s); 134062306a36Sopenharmony_ci return; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic void drop_tx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length, 134662306a36Sopenharmony_ci void *header, void *private_data) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 134962306a36Sopenharmony_ci const __be32 *ctx_header = header; 135062306a36Sopenharmony_ci unsigned int packets; 135162306a36Sopenharmony_ci unsigned int cycle; 135262306a36Sopenharmony_ci int i; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci if (s->packet_index < 0) 135562306a36Sopenharmony_ci return; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci packets = header_length / s->ctx_data.tx.ctx_header_size; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci ctx_header += (packets - 1) * s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); 136062306a36Sopenharmony_ci cycle = compute_ohci_cycle_count(ctx_header[1]); 136162306a36Sopenharmony_ci s->next_cycle = increment_ohci_cycle_count(cycle, 1); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci for (i = 0; i < packets; ++i) { 136462306a36Sopenharmony_ci struct fw_iso_packet params = {0}; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (queue_in_packet(s, ¶ms) < 0) { 136762306a36Sopenharmony_ci cancel_stream(s); 136862306a36Sopenharmony_ci return; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci} 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic void process_tx_packets_intermediately(struct fw_iso_context *context, u32 tstamp, 137462306a36Sopenharmony_ci size_t header_length, void *header, void *private_data) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 137762306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 137862306a36Sopenharmony_ci __be32 *ctx_header; 137962306a36Sopenharmony_ci unsigned int packets; 138062306a36Sopenharmony_ci unsigned int offset; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci if (s->packet_index < 0) 138362306a36Sopenharmony_ci return; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci packets = header_length / s->ctx_data.tx.ctx_header_size; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci offset = 0; 138862306a36Sopenharmony_ci ctx_header = header; 138962306a36Sopenharmony_ci while (offset < packets) { 139062306a36Sopenharmony_ci unsigned int cycle = compute_ohci_cycle_count(ctx_header[1]); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci if (compare_ohci_cycle_count(cycle, d->processing_cycle.tx_start) >= 0) 139362306a36Sopenharmony_ci break; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(__be32); 139662306a36Sopenharmony_ci ++offset; 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci ctx_header = header; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if (offset > 0) { 140262306a36Sopenharmony_ci size_t length = s->ctx_data.tx.ctx_header_size * offset; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci drop_tx_packets(context, tstamp, length, ctx_header, s); 140562306a36Sopenharmony_ci if (amdtp_streaming_error(s)) 140662306a36Sopenharmony_ci return; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci ctx_header += length / sizeof(*ctx_header); 140962306a36Sopenharmony_ci header_length -= length; 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (offset < packets) { 141362306a36Sopenharmony_ci s->ready_processing = true; 141462306a36Sopenharmony_ci wake_up(&s->ready_wait); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci process_tx_packets(context, tstamp, header_length, ctx_header, s); 141762306a36Sopenharmony_ci if (amdtp_streaming_error(s)) 141862306a36Sopenharmony_ci return; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci context->callback.sc = process_tx_packets; 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic void drop_tx_packets_initially(struct fw_iso_context *context, u32 tstamp, 142562306a36Sopenharmony_ci size_t header_length, void *header, void *private_data) 142662306a36Sopenharmony_ci{ 142762306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 142862306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 142962306a36Sopenharmony_ci __be32 *ctx_header; 143062306a36Sopenharmony_ci unsigned int count; 143162306a36Sopenharmony_ci unsigned int events; 143262306a36Sopenharmony_ci int i; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (s->packet_index < 0) 143562306a36Sopenharmony_ci return; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci count = header_length / s->ctx_data.tx.ctx_header_size; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci // Attempt to detect any event in the batch of packets. 144062306a36Sopenharmony_ci events = 0; 144162306a36Sopenharmony_ci ctx_header = header; 144262306a36Sopenharmony_ci for (i = 0; i < count; ++i) { 144362306a36Sopenharmony_ci unsigned int payload_quads = 144462306a36Sopenharmony_ci (be32_to_cpu(*ctx_header) >> ISO_DATA_LENGTH_SHIFT) / sizeof(__be32); 144562306a36Sopenharmony_ci unsigned int data_blocks; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (s->flags & CIP_NO_HEADER) { 144862306a36Sopenharmony_ci data_blocks = payload_quads / s->data_block_quadlets; 144962306a36Sopenharmony_ci } else { 145062306a36Sopenharmony_ci __be32 *cip_headers = ctx_header + IR_CTX_HEADER_DEFAULT_QUADLETS; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (payload_quads < CIP_HEADER_QUADLETS) { 145362306a36Sopenharmony_ci data_blocks = 0; 145462306a36Sopenharmony_ci } else { 145562306a36Sopenharmony_ci payload_quads -= CIP_HEADER_QUADLETS; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci if (s->flags & CIP_UNAWARE_SYT) { 145862306a36Sopenharmony_ci data_blocks = payload_quads / s->data_block_quadlets; 145962306a36Sopenharmony_ci } else { 146062306a36Sopenharmony_ci u32 cip1 = be32_to_cpu(cip_headers[1]); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci // NODATA packet can includes any data blocks but they are 146362306a36Sopenharmony_ci // not available as event. 146462306a36Sopenharmony_ci if ((cip1 & CIP_NO_DATA) == CIP_NO_DATA) 146562306a36Sopenharmony_ci data_blocks = 0; 146662306a36Sopenharmony_ci else 146762306a36Sopenharmony_ci data_blocks = payload_quads / s->data_block_quadlets; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci events += data_blocks; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(__be32); 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci drop_tx_packets(context, tstamp, header_length, header, s); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (events > 0) 148062306a36Sopenharmony_ci s->ctx_data.tx.event_starts = true; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci // Decide the cycle count to begin processing content of packet in IR contexts. 148362306a36Sopenharmony_ci { 148462306a36Sopenharmony_ci unsigned int stream_count = 0; 148562306a36Sopenharmony_ci unsigned int event_starts_count = 0; 148662306a36Sopenharmony_ci unsigned int cycle = UINT_MAX; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 148962306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 149062306a36Sopenharmony_ci ++stream_count; 149162306a36Sopenharmony_ci if (s->ctx_data.tx.event_starts) 149262306a36Sopenharmony_ci ++event_starts_count; 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if (stream_count == event_starts_count) { 149762306a36Sopenharmony_ci unsigned int next_cycle; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 150062306a36Sopenharmony_ci if (s->direction != AMDTP_IN_STREAM) 150162306a36Sopenharmony_ci continue; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci next_cycle = increment_ohci_cycle_count(s->next_cycle, 150462306a36Sopenharmony_ci d->processing_cycle.tx_init_skip); 150562306a36Sopenharmony_ci if (cycle == UINT_MAX || 150662306a36Sopenharmony_ci compare_ohci_cycle_count(next_cycle, cycle) > 0) 150762306a36Sopenharmony_ci cycle = next_cycle; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci s->context->callback.sc = process_tx_packets_intermediately; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci d->processing_cycle.tx_start = cycle; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_cistatic void process_ctxs_in_domain(struct amdtp_domain *d) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci struct amdtp_stream *s; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 152262306a36Sopenharmony_ci if (s != d->irq_target && amdtp_stream_running(s)) 152362306a36Sopenharmony_ci fw_iso_context_flush_completions(s->context); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (amdtp_streaming_error(s)) 152662306a36Sopenharmony_ci goto error; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci return; 153062306a36Sopenharmony_cierror: 153162306a36Sopenharmony_ci if (amdtp_stream_running(d->irq_target)) 153262306a36Sopenharmony_ci cancel_stream(d->irq_target); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 153562306a36Sopenharmony_ci if (amdtp_stream_running(s)) 153662306a36Sopenharmony_ci cancel_stream(s); 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci} 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_cistatic void irq_target_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, 154162306a36Sopenharmony_ci void *header, void *private_data) 154262306a36Sopenharmony_ci{ 154362306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 154462306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci process_rx_packets(context, tstamp, header_length, header, private_data); 154762306a36Sopenharmony_ci process_ctxs_in_domain(d); 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_cistatic void irq_target_callback_intermediately(struct fw_iso_context *context, u32 tstamp, 155162306a36Sopenharmony_ci size_t header_length, void *header, void *private_data) 155262306a36Sopenharmony_ci{ 155362306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 155462306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci process_rx_packets_intermediately(context, tstamp, header_length, header, private_data); 155762306a36Sopenharmony_ci process_ctxs_in_domain(d); 155862306a36Sopenharmony_ci} 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_cistatic void irq_target_callback_skip(struct fw_iso_context *context, u32 tstamp, 156162306a36Sopenharmony_ci size_t header_length, void *header, void *private_data) 156262306a36Sopenharmony_ci{ 156362306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 156462306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 156562306a36Sopenharmony_ci bool ready_to_start; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci skip_rx_packets(context, tstamp, header_length, header, private_data); 156862306a36Sopenharmony_ci process_ctxs_in_domain(d); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (d->replay.enable && !d->replay.on_the_fly) { 157162306a36Sopenharmony_ci unsigned int rx_count = 0; 157262306a36Sopenharmony_ci unsigned int rx_ready_count = 0; 157362306a36Sopenharmony_ci struct amdtp_stream *rx; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci list_for_each_entry(rx, &d->streams, list) { 157662306a36Sopenharmony_ci struct amdtp_stream *tx; 157762306a36Sopenharmony_ci unsigned int cached_cycles; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (rx->direction != AMDTP_OUT_STREAM) 158062306a36Sopenharmony_ci continue; 158162306a36Sopenharmony_ci ++rx_count; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci tx = rx->ctx_data.rx.replay_target; 158462306a36Sopenharmony_ci cached_cycles = calculate_cached_cycle_count(tx, 0); 158562306a36Sopenharmony_ci if (cached_cycles > tx->ctx_data.tx.cache.size / 2) 158662306a36Sopenharmony_ci ++rx_ready_count; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci ready_to_start = (rx_count == rx_ready_count); 159062306a36Sopenharmony_ci } else { 159162306a36Sopenharmony_ci ready_to_start = true; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci // Decide the cycle count to begin processing content of packet in IT contexts. All of IT 159562306a36Sopenharmony_ci // contexts are expected to start and get callback when reaching here. 159662306a36Sopenharmony_ci if (ready_to_start) { 159762306a36Sopenharmony_ci unsigned int cycle = s->next_cycle; 159862306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 159962306a36Sopenharmony_ci if (s->direction != AMDTP_OUT_STREAM) 160062306a36Sopenharmony_ci continue; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (compare_ohci_cycle_count(s->next_cycle, cycle) > 0) 160362306a36Sopenharmony_ci cycle = s->next_cycle; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci if (s == d->irq_target) 160662306a36Sopenharmony_ci s->context->callback.sc = irq_target_callback_intermediately; 160762306a36Sopenharmony_ci else 160862306a36Sopenharmony_ci s->context->callback.sc = process_rx_packets_intermediately; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci d->processing_cycle.rx_start = cycle; 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci} 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci// This is executed one time. For in-stream, first packet has come. For out-stream, prepared to 161662306a36Sopenharmony_ci// transmit first packet. 161762306a36Sopenharmony_cistatic void amdtp_stream_first_callback(struct fw_iso_context *context, 161862306a36Sopenharmony_ci u32 tstamp, size_t header_length, 161962306a36Sopenharmony_ci void *header, void *private_data) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci struct amdtp_stream *s = private_data; 162262306a36Sopenharmony_ci struct amdtp_domain *d = s->domain; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 162562306a36Sopenharmony_ci context->callback.sc = drop_tx_packets_initially; 162662306a36Sopenharmony_ci } else { 162762306a36Sopenharmony_ci if (s == d->irq_target) 162862306a36Sopenharmony_ci context->callback.sc = irq_target_callback_skip; 162962306a36Sopenharmony_ci else 163062306a36Sopenharmony_ci context->callback.sc = skip_rx_packets; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci context->callback.sc(context, tstamp, header_length, header, s); 163462306a36Sopenharmony_ci} 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci/** 163762306a36Sopenharmony_ci * amdtp_stream_start - start transferring packets 163862306a36Sopenharmony_ci * @s: the AMDTP stream to start 163962306a36Sopenharmony_ci * @channel: the isochronous channel on the bus 164062306a36Sopenharmony_ci * @speed: firewire speed code 164162306a36Sopenharmony_ci * @queue_size: The number of packets in the queue. 164262306a36Sopenharmony_ci * @idle_irq_interval: the interval to queue packet during initial state. 164362306a36Sopenharmony_ci * 164462306a36Sopenharmony_ci * The stream cannot be started until it has been configured with 164562306a36Sopenharmony_ci * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI 164662306a36Sopenharmony_ci * device can be started. 164762306a36Sopenharmony_ci */ 164862306a36Sopenharmony_cistatic int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, 164962306a36Sopenharmony_ci unsigned int queue_size, unsigned int idle_irq_interval) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci bool is_irq_target = (s == s->domain->irq_target); 165262306a36Sopenharmony_ci unsigned int ctx_header_size; 165362306a36Sopenharmony_ci unsigned int max_ctx_payload_size; 165462306a36Sopenharmony_ci enum dma_data_direction dir; 165562306a36Sopenharmony_ci struct pkt_desc *descs; 165662306a36Sopenharmony_ci int i, type, tag, err; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci mutex_lock(&s->mutex); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (WARN_ON(amdtp_stream_running(s) || 166162306a36Sopenharmony_ci (s->data_block_quadlets < 1))) { 166262306a36Sopenharmony_ci err = -EBADFD; 166362306a36Sopenharmony_ci goto err_unlock; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 166762306a36Sopenharmony_ci // NOTE: IT context should be used for constant IRQ. 166862306a36Sopenharmony_ci if (is_irq_target) { 166962306a36Sopenharmony_ci err = -EINVAL; 167062306a36Sopenharmony_ci goto err_unlock; 167162306a36Sopenharmony_ci } 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci s->data_block_counter = UINT_MAX; 167462306a36Sopenharmony_ci } else { 167562306a36Sopenharmony_ci s->data_block_counter = 0; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci // initialize packet buffer. 167962306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 168062306a36Sopenharmony_ci dir = DMA_FROM_DEVICE; 168162306a36Sopenharmony_ci type = FW_ISO_CONTEXT_RECEIVE; 168262306a36Sopenharmony_ci if (!(s->flags & CIP_NO_HEADER)) 168362306a36Sopenharmony_ci ctx_header_size = IR_CTX_HEADER_SIZE_CIP; 168462306a36Sopenharmony_ci else 168562306a36Sopenharmony_ci ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP; 168662306a36Sopenharmony_ci } else { 168762306a36Sopenharmony_ci dir = DMA_TO_DEVICE; 168862306a36Sopenharmony_ci type = FW_ISO_CONTEXT_TRANSMIT; 168962306a36Sopenharmony_ci ctx_header_size = 0; // No effect for IT context. 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci max_ctx_payload_size = amdtp_stream_get_max_ctx_payload_size(s); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, max_ctx_payload_size, dir); 169462306a36Sopenharmony_ci if (err < 0) 169562306a36Sopenharmony_ci goto err_unlock; 169662306a36Sopenharmony_ci s->queue_size = queue_size; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, 169962306a36Sopenharmony_ci type, channel, speed, ctx_header_size, 170062306a36Sopenharmony_ci amdtp_stream_first_callback, s); 170162306a36Sopenharmony_ci if (IS_ERR(s->context)) { 170262306a36Sopenharmony_ci err = PTR_ERR(s->context); 170362306a36Sopenharmony_ci if (err == -EBUSY) 170462306a36Sopenharmony_ci dev_err(&s->unit->device, 170562306a36Sopenharmony_ci "no free stream on this controller\n"); 170662306a36Sopenharmony_ci goto err_buffer; 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci amdtp_stream_update(s); 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 171262306a36Sopenharmony_ci s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size; 171362306a36Sopenharmony_ci s->ctx_data.tx.ctx_header_size = ctx_header_size; 171462306a36Sopenharmony_ci s->ctx_data.tx.event_starts = false; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (s->domain->replay.enable) { 171762306a36Sopenharmony_ci // struct fw_iso_context.drop_overflow_headers is false therefore it's 171862306a36Sopenharmony_ci // possible to cache much unexpectedly. 171962306a36Sopenharmony_ci s->ctx_data.tx.cache.size = max_t(unsigned int, s->syt_interval * 2, 172062306a36Sopenharmony_ci queue_size * 3 / 2); 172162306a36Sopenharmony_ci s->ctx_data.tx.cache.pos = 0; 172262306a36Sopenharmony_ci s->ctx_data.tx.cache.descs = kcalloc(s->ctx_data.tx.cache.size, 172362306a36Sopenharmony_ci sizeof(*s->ctx_data.tx.cache.descs), GFP_KERNEL); 172462306a36Sopenharmony_ci if (!s->ctx_data.tx.cache.descs) { 172562306a36Sopenharmony_ci err = -ENOMEM; 172662306a36Sopenharmony_ci goto err_context; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci } else { 173062306a36Sopenharmony_ci static const struct { 173162306a36Sopenharmony_ci unsigned int data_block; 173262306a36Sopenharmony_ci unsigned int syt_offset; 173362306a36Sopenharmony_ci } *entry, initial_state[] = { 173462306a36Sopenharmony_ci [CIP_SFC_32000] = { 4, 3072 }, 173562306a36Sopenharmony_ci [CIP_SFC_48000] = { 6, 1024 }, 173662306a36Sopenharmony_ci [CIP_SFC_96000] = { 12, 1024 }, 173762306a36Sopenharmony_ci [CIP_SFC_192000] = { 24, 1024 }, 173862306a36Sopenharmony_ci [CIP_SFC_44100] = { 0, 67 }, 173962306a36Sopenharmony_ci [CIP_SFC_88200] = { 0, 67 }, 174062306a36Sopenharmony_ci [CIP_SFC_176400] = { 0, 67 }, 174162306a36Sopenharmony_ci }; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci s->ctx_data.rx.seq.descs = kcalloc(queue_size, sizeof(*s->ctx_data.rx.seq.descs), GFP_KERNEL); 174462306a36Sopenharmony_ci if (!s->ctx_data.rx.seq.descs) { 174562306a36Sopenharmony_ci err = -ENOMEM; 174662306a36Sopenharmony_ci goto err_context; 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci s->ctx_data.rx.seq.size = queue_size; 174962306a36Sopenharmony_ci s->ctx_data.rx.seq.pos = 0; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci entry = &initial_state[s->sfc]; 175262306a36Sopenharmony_ci s->ctx_data.rx.data_block_state = entry->data_block; 175362306a36Sopenharmony_ci s->ctx_data.rx.syt_offset_state = entry->syt_offset; 175462306a36Sopenharmony_ci s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci s->ctx_data.rx.event_count = 0; 175762306a36Sopenharmony_ci } 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci if (s->flags & CIP_NO_HEADER) 176062306a36Sopenharmony_ci s->tag = TAG_NO_CIP_HEADER; 176162306a36Sopenharmony_ci else 176262306a36Sopenharmony_ci s->tag = TAG_CIP; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci // NOTE: When operating without hardIRQ/softIRQ, applications tends to call ioctl request 176562306a36Sopenharmony_ci // for runtime of PCM substream in the interval equivalent to the size of PCM buffer. It 176662306a36Sopenharmony_ci // could take a round over queue of AMDTP packet descriptors and small loss of history. For 176762306a36Sopenharmony_ci // safe, keep more 8 elements for the queue, equivalent to 1 ms. 176862306a36Sopenharmony_ci descs = kcalloc(s->queue_size + 8, sizeof(*descs), GFP_KERNEL); 176962306a36Sopenharmony_ci if (!descs) { 177062306a36Sopenharmony_ci err = -ENOMEM; 177162306a36Sopenharmony_ci goto err_context; 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci s->packet_descs = descs; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci INIT_LIST_HEAD(&s->packet_descs_list); 177662306a36Sopenharmony_ci for (i = 0; i < s->queue_size; ++i) { 177762306a36Sopenharmony_ci INIT_LIST_HEAD(&descs->link); 177862306a36Sopenharmony_ci list_add_tail(&descs->link, &s->packet_descs_list); 177962306a36Sopenharmony_ci ++descs; 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci s->packet_descs_cursor = list_first_entry(&s->packet_descs_list, struct pkt_desc, link); 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci s->packet_index = 0; 178462306a36Sopenharmony_ci do { 178562306a36Sopenharmony_ci struct fw_iso_packet params; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 178862306a36Sopenharmony_ci err = queue_in_packet(s, ¶ms); 178962306a36Sopenharmony_ci } else { 179062306a36Sopenharmony_ci bool sched_irq = false; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci params.header_length = 0; 179362306a36Sopenharmony_ci params.payload_length = 0; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (is_irq_target) { 179662306a36Sopenharmony_ci sched_irq = !((s->packet_index + 1) % 179762306a36Sopenharmony_ci idle_irq_interval); 179862306a36Sopenharmony_ci } 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci err = queue_out_packet(s, ¶ms, sched_irq); 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci if (err < 0) 180362306a36Sopenharmony_ci goto err_pkt_descs; 180462306a36Sopenharmony_ci } while (s->packet_index > 0); 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci /* NOTE: TAG1 matches CIP. This just affects in stream. */ 180762306a36Sopenharmony_ci tag = FW_ISO_CONTEXT_MATCH_TAG1; 180862306a36Sopenharmony_ci if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER)) 180962306a36Sopenharmony_ci tag |= FW_ISO_CONTEXT_MATCH_TAG0; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci s->ready_processing = false; 181262306a36Sopenharmony_ci err = fw_iso_context_start(s->context, -1, 0, tag); 181362306a36Sopenharmony_ci if (err < 0) 181462306a36Sopenharmony_ci goto err_pkt_descs; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci mutex_unlock(&s->mutex); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci return 0; 181962306a36Sopenharmony_cierr_pkt_descs: 182062306a36Sopenharmony_ci kfree(s->packet_descs); 182162306a36Sopenharmony_ci s->packet_descs = NULL; 182262306a36Sopenharmony_cierr_context: 182362306a36Sopenharmony_ci if (s->direction == AMDTP_OUT_STREAM) { 182462306a36Sopenharmony_ci kfree(s->ctx_data.rx.seq.descs); 182562306a36Sopenharmony_ci } else { 182662306a36Sopenharmony_ci if (s->domain->replay.enable) 182762306a36Sopenharmony_ci kfree(s->ctx_data.tx.cache.descs); 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci fw_iso_context_destroy(s->context); 183062306a36Sopenharmony_ci s->context = ERR_PTR(-1); 183162306a36Sopenharmony_cierr_buffer: 183262306a36Sopenharmony_ci iso_packets_buffer_destroy(&s->buffer, s->unit); 183362306a36Sopenharmony_cierr_unlock: 183462306a36Sopenharmony_ci mutex_unlock(&s->mutex); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci return err; 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci/** 184062306a36Sopenharmony_ci * amdtp_domain_stream_pcm_pointer - get the PCM buffer position 184162306a36Sopenharmony_ci * @d: the AMDTP domain. 184262306a36Sopenharmony_ci * @s: the AMDTP stream that transports the PCM data 184362306a36Sopenharmony_ci * 184462306a36Sopenharmony_ci * Returns the current buffer position, in frames. 184562306a36Sopenharmony_ci */ 184662306a36Sopenharmony_ciunsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, 184762306a36Sopenharmony_ci struct amdtp_stream *s) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci struct amdtp_stream *irq_target = d->irq_target; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci // Process isochronous packets queued till recent isochronous cycle to handle PCM frames. 185262306a36Sopenharmony_ci if (irq_target && amdtp_stream_running(irq_target)) { 185362306a36Sopenharmony_ci // In software IRQ context, the call causes dead-lock to disable the tasklet 185462306a36Sopenharmony_ci // synchronously. 185562306a36Sopenharmony_ci if (!in_softirq()) 185662306a36Sopenharmony_ci fw_iso_context_flush_completions(irq_target->context); 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci return READ_ONCE(s->pcm_buffer_pointer); 186062306a36Sopenharmony_ci} 186162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci/** 186462306a36Sopenharmony_ci * amdtp_domain_stream_pcm_ack - acknowledge queued PCM frames 186562306a36Sopenharmony_ci * @d: the AMDTP domain. 186662306a36Sopenharmony_ci * @s: the AMDTP stream that transfers the PCM frames 186762306a36Sopenharmony_ci * 186862306a36Sopenharmony_ci * Returns zero always. 186962306a36Sopenharmony_ci */ 187062306a36Sopenharmony_ciint amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s) 187162306a36Sopenharmony_ci{ 187262306a36Sopenharmony_ci struct amdtp_stream *irq_target = d->irq_target; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci // Process isochronous packets for recent isochronous cycle to handle 187562306a36Sopenharmony_ci // queued PCM frames. 187662306a36Sopenharmony_ci if (irq_target && amdtp_stream_running(irq_target)) 187762306a36Sopenharmony_ci fw_iso_context_flush_completions(irq_target->context); 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci return 0; 188062306a36Sopenharmony_ci} 188162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack); 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci/** 188462306a36Sopenharmony_ci * amdtp_stream_update - update the stream after a bus reset 188562306a36Sopenharmony_ci * @s: the AMDTP stream 188662306a36Sopenharmony_ci */ 188762306a36Sopenharmony_civoid amdtp_stream_update(struct amdtp_stream *s) 188862306a36Sopenharmony_ci{ 188962306a36Sopenharmony_ci /* Precomputing. */ 189062306a36Sopenharmony_ci WRITE_ONCE(s->source_node_id_field, 189162306a36Sopenharmony_ci (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & CIP_SID_MASK); 189262306a36Sopenharmony_ci} 189362306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_update); 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci/** 189662306a36Sopenharmony_ci * amdtp_stream_stop - stop sending packets 189762306a36Sopenharmony_ci * @s: the AMDTP stream to stop 189862306a36Sopenharmony_ci * 189962306a36Sopenharmony_ci * All PCM and MIDI devices of the stream must be stopped before the stream 190062306a36Sopenharmony_ci * itself can be stopped. 190162306a36Sopenharmony_ci */ 190262306a36Sopenharmony_cistatic void amdtp_stream_stop(struct amdtp_stream *s) 190362306a36Sopenharmony_ci{ 190462306a36Sopenharmony_ci mutex_lock(&s->mutex); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci if (!amdtp_stream_running(s)) { 190762306a36Sopenharmony_ci mutex_unlock(&s->mutex); 190862306a36Sopenharmony_ci return; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci fw_iso_context_stop(s->context); 191262306a36Sopenharmony_ci fw_iso_context_destroy(s->context); 191362306a36Sopenharmony_ci s->context = ERR_PTR(-1); 191462306a36Sopenharmony_ci iso_packets_buffer_destroy(&s->buffer, s->unit); 191562306a36Sopenharmony_ci kfree(s->packet_descs); 191662306a36Sopenharmony_ci s->packet_descs = NULL; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci if (s->direction == AMDTP_OUT_STREAM) { 191962306a36Sopenharmony_ci kfree(s->ctx_data.rx.seq.descs); 192062306a36Sopenharmony_ci } else { 192162306a36Sopenharmony_ci if (s->domain->replay.enable) 192262306a36Sopenharmony_ci kfree(s->ctx_data.tx.cache.descs); 192362306a36Sopenharmony_ci } 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci mutex_unlock(&s->mutex); 192662306a36Sopenharmony_ci} 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci/** 192962306a36Sopenharmony_ci * amdtp_stream_pcm_abort - abort the running PCM device 193062306a36Sopenharmony_ci * @s: the AMDTP stream about to be stopped 193162306a36Sopenharmony_ci * 193262306a36Sopenharmony_ci * If the isochronous stream needs to be stopped asynchronously, call this 193362306a36Sopenharmony_ci * function first to stop the PCM device. 193462306a36Sopenharmony_ci */ 193562306a36Sopenharmony_civoid amdtp_stream_pcm_abort(struct amdtp_stream *s) 193662306a36Sopenharmony_ci{ 193762306a36Sopenharmony_ci struct snd_pcm_substream *pcm; 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci pcm = READ_ONCE(s->pcm); 194062306a36Sopenharmony_ci if (pcm) 194162306a36Sopenharmony_ci snd_pcm_stop_xrun(pcm); 194262306a36Sopenharmony_ci} 194362306a36Sopenharmony_ciEXPORT_SYMBOL(amdtp_stream_pcm_abort); 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci/** 194662306a36Sopenharmony_ci * amdtp_domain_init - initialize an AMDTP domain structure 194762306a36Sopenharmony_ci * @d: the AMDTP domain to initialize. 194862306a36Sopenharmony_ci */ 194962306a36Sopenharmony_ciint amdtp_domain_init(struct amdtp_domain *d) 195062306a36Sopenharmony_ci{ 195162306a36Sopenharmony_ci INIT_LIST_HEAD(&d->streams); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci d->events_per_period = 0; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci return 0; 195662306a36Sopenharmony_ci} 195762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_init); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci/** 196062306a36Sopenharmony_ci * amdtp_domain_destroy - destroy an AMDTP domain structure 196162306a36Sopenharmony_ci * @d: the AMDTP domain to destroy. 196262306a36Sopenharmony_ci */ 196362306a36Sopenharmony_civoid amdtp_domain_destroy(struct amdtp_domain *d) 196462306a36Sopenharmony_ci{ 196562306a36Sopenharmony_ci // At present nothing to do. 196662306a36Sopenharmony_ci return; 196762306a36Sopenharmony_ci} 196862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_destroy); 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci/** 197162306a36Sopenharmony_ci * amdtp_domain_add_stream - register isoc context into the domain. 197262306a36Sopenharmony_ci * @d: the AMDTP domain. 197362306a36Sopenharmony_ci * @s: the AMDTP stream. 197462306a36Sopenharmony_ci * @channel: the isochronous channel on the bus. 197562306a36Sopenharmony_ci * @speed: firewire speed code. 197662306a36Sopenharmony_ci */ 197762306a36Sopenharmony_ciint amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, 197862306a36Sopenharmony_ci int channel, int speed) 197962306a36Sopenharmony_ci{ 198062306a36Sopenharmony_ci struct amdtp_stream *tmp; 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci list_for_each_entry(tmp, &d->streams, list) { 198362306a36Sopenharmony_ci if (s == tmp) 198462306a36Sopenharmony_ci return -EBUSY; 198562306a36Sopenharmony_ci } 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci list_add(&s->list, &d->streams); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci s->channel = channel; 199062306a36Sopenharmony_ci s->speed = speed; 199162306a36Sopenharmony_ci s->domain = d; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci return 0; 199462306a36Sopenharmony_ci} 199562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_add_stream); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci// Make the reference from rx stream to tx stream for sequence replay. When the number of tx streams 199862306a36Sopenharmony_ci// is less than the number of rx streams, the first tx stream is selected. 199962306a36Sopenharmony_cistatic int make_association(struct amdtp_domain *d) 200062306a36Sopenharmony_ci{ 200162306a36Sopenharmony_ci unsigned int dst_index = 0; 200262306a36Sopenharmony_ci struct amdtp_stream *rx; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci // Make association to replay target. 200562306a36Sopenharmony_ci list_for_each_entry(rx, &d->streams, list) { 200662306a36Sopenharmony_ci if (rx->direction == AMDTP_OUT_STREAM) { 200762306a36Sopenharmony_ci unsigned int src_index = 0; 200862306a36Sopenharmony_ci struct amdtp_stream *tx = NULL; 200962306a36Sopenharmony_ci struct amdtp_stream *s; 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 201262306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 201362306a36Sopenharmony_ci if (dst_index == src_index) { 201462306a36Sopenharmony_ci tx = s; 201562306a36Sopenharmony_ci break; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci ++src_index; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci } 202162306a36Sopenharmony_ci if (!tx) { 202262306a36Sopenharmony_ci // Select the first entry. 202362306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 202462306a36Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) { 202562306a36Sopenharmony_ci tx = s; 202662306a36Sopenharmony_ci break; 202762306a36Sopenharmony_ci } 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci // No target is available to replay sequence. 203062306a36Sopenharmony_ci if (!tx) 203162306a36Sopenharmony_ci return -EINVAL; 203262306a36Sopenharmony_ci } 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci rx->ctx_data.rx.replay_target = tx; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci ++dst_index; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci return 0; 204162306a36Sopenharmony_ci} 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci/** 204462306a36Sopenharmony_ci * amdtp_domain_start - start sending packets for isoc context in the domain. 204562306a36Sopenharmony_ci * @d: the AMDTP domain. 204662306a36Sopenharmony_ci * @tx_init_skip_cycles: the number of cycles to skip processing packets at initial stage of IR 204762306a36Sopenharmony_ci * contexts. 204862306a36Sopenharmony_ci * @replay_seq: whether to replay the sequence of packet in IR context for the sequence of packet in 204962306a36Sopenharmony_ci * IT context. 205062306a36Sopenharmony_ci * @replay_on_the_fly: transfer rx packets according to nominal frequency, then begin to replay 205162306a36Sopenharmony_ci * according to arrival of events in tx packets. 205262306a36Sopenharmony_ci */ 205362306a36Sopenharmony_ciint amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq, 205462306a36Sopenharmony_ci bool replay_on_the_fly) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci unsigned int events_per_buffer = d->events_per_buffer; 205762306a36Sopenharmony_ci unsigned int events_per_period = d->events_per_period; 205862306a36Sopenharmony_ci unsigned int queue_size; 205962306a36Sopenharmony_ci struct amdtp_stream *s; 206062306a36Sopenharmony_ci bool found = false; 206162306a36Sopenharmony_ci int err; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci if (replay_seq) { 206462306a36Sopenharmony_ci err = make_association(d); 206562306a36Sopenharmony_ci if (err < 0) 206662306a36Sopenharmony_ci return err; 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci d->replay.enable = replay_seq; 206962306a36Sopenharmony_ci d->replay.on_the_fly = replay_on_the_fly; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci // Select an IT context as IRQ target. 207262306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 207362306a36Sopenharmony_ci if (s->direction == AMDTP_OUT_STREAM) { 207462306a36Sopenharmony_ci found = true; 207562306a36Sopenharmony_ci break; 207662306a36Sopenharmony_ci } 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci if (!found) 207962306a36Sopenharmony_ci return -ENXIO; 208062306a36Sopenharmony_ci d->irq_target = s; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci d->processing_cycle.tx_init_skip = tx_init_skip_cycles; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci // This is a case that AMDTP streams in domain run just for MIDI 208562306a36Sopenharmony_ci // substream. Use the number of events equivalent to 10 msec as 208662306a36Sopenharmony_ci // interval of hardware IRQ. 208762306a36Sopenharmony_ci if (events_per_period == 0) 208862306a36Sopenharmony_ci events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100; 208962306a36Sopenharmony_ci if (events_per_buffer == 0) 209062306a36Sopenharmony_ci events_per_buffer = events_per_period * 3; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, 209362306a36Sopenharmony_ci amdtp_rate_table[d->irq_target->sfc]); 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) { 209662306a36Sopenharmony_ci unsigned int idle_irq_interval = 0; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci if (s->direction == AMDTP_OUT_STREAM && s == d->irq_target) { 209962306a36Sopenharmony_ci idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, 210062306a36Sopenharmony_ci amdtp_rate_table[d->irq_target->sfc]); 210162306a36Sopenharmony_ci } 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci // Starts immediately but actually DMA context starts several hundred cycles later. 210462306a36Sopenharmony_ci err = amdtp_stream_start(s, s->channel, s->speed, queue_size, idle_irq_interval); 210562306a36Sopenharmony_ci if (err < 0) 210662306a36Sopenharmony_ci goto error; 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci return 0; 211062306a36Sopenharmony_cierror: 211162306a36Sopenharmony_ci list_for_each_entry(s, &d->streams, list) 211262306a36Sopenharmony_ci amdtp_stream_stop(s); 211362306a36Sopenharmony_ci return err; 211462306a36Sopenharmony_ci} 211562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_start); 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci/** 211862306a36Sopenharmony_ci * amdtp_domain_stop - stop sending packets for isoc context in the same domain. 211962306a36Sopenharmony_ci * @d: the AMDTP domain to which the isoc contexts belong. 212062306a36Sopenharmony_ci */ 212162306a36Sopenharmony_civoid amdtp_domain_stop(struct amdtp_domain *d) 212262306a36Sopenharmony_ci{ 212362306a36Sopenharmony_ci struct amdtp_stream *s, *next; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (d->irq_target) 212662306a36Sopenharmony_ci amdtp_stream_stop(d->irq_target); 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci list_for_each_entry_safe(s, next, &d->streams, list) { 212962306a36Sopenharmony_ci list_del(&s->list); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci if (s != d->irq_target) 213262306a36Sopenharmony_ci amdtp_stream_stop(s); 213362306a36Sopenharmony_ci } 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci d->events_per_period = 0; 213662306a36Sopenharmony_ci d->irq_target = NULL; 213762306a36Sopenharmony_ci} 213862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amdtp_domain_stop); 2139