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