18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * amdtp-tascam.c - a part of driver for TASCAM FireWire series 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Takashi Sakamoto 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <sound/pcm.h> 98c2ecf20Sopenharmony_ci#include "tascam.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define AMDTP_FMT_TSCM_TX 0x1e 128c2ecf20Sopenharmony_ci#define AMDTP_FMT_TSCM_RX 0x3e 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistruct amdtp_tscm { 158c2ecf20Sopenharmony_ci unsigned int pcm_channels; 168c2ecf20Sopenharmony_ci}; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciint amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct amdtp_tscm *p = s->protocol; 218c2ecf20Sopenharmony_ci unsigned int data_channels; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (amdtp_stream_running(s)) 248c2ecf20Sopenharmony_ci return -EBUSY; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci data_channels = p->pcm_channels; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* Packets in in-stream have extra 2 data channels. */ 298c2ecf20Sopenharmony_ci if (s->direction == AMDTP_IN_STREAM) 308c2ecf20Sopenharmony_ci data_channels += 2; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return amdtp_stream_set_parameters(s, rate, data_channels); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, 368c2ecf20Sopenharmony_ci __be32 *buffer, unsigned int frames, 378c2ecf20Sopenharmony_ci unsigned int pcm_frames) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct amdtp_tscm *p = s->protocol; 408c2ecf20Sopenharmony_ci unsigned int channels = p->pcm_channels; 418c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = pcm->runtime; 428c2ecf20Sopenharmony_ci unsigned int pcm_buffer_pointer; 438c2ecf20Sopenharmony_ci int remaining_frames; 448c2ecf20Sopenharmony_ci const u32 *src; 458c2ecf20Sopenharmony_ci int i, c; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; 488c2ecf20Sopenharmony_ci pcm_buffer_pointer %= runtime->buffer_size; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci src = (void *)runtime->dma_area + 518c2ecf20Sopenharmony_ci frames_to_bytes(runtime, pcm_buffer_pointer); 528c2ecf20Sopenharmony_ci remaining_frames = runtime->buffer_size - pcm_buffer_pointer; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci for (i = 0; i < frames; ++i) { 558c2ecf20Sopenharmony_ci for (c = 0; c < channels; ++c) { 568c2ecf20Sopenharmony_ci buffer[c] = cpu_to_be32(*src); 578c2ecf20Sopenharmony_ci src++; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci buffer += s->data_block_quadlets; 608c2ecf20Sopenharmony_ci if (--remaining_frames == 0) 618c2ecf20Sopenharmony_ci src = (void *)runtime->dma_area; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, 668c2ecf20Sopenharmony_ci __be32 *buffer, unsigned int frames, 678c2ecf20Sopenharmony_ci unsigned int pcm_frames) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct amdtp_tscm *p = s->protocol; 708c2ecf20Sopenharmony_ci unsigned int channels = p->pcm_channels; 718c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = pcm->runtime; 728c2ecf20Sopenharmony_ci unsigned int pcm_buffer_pointer; 738c2ecf20Sopenharmony_ci int remaining_frames; 748c2ecf20Sopenharmony_ci u32 *dst; 758c2ecf20Sopenharmony_ci int i, c; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; 788c2ecf20Sopenharmony_ci pcm_buffer_pointer %= runtime->buffer_size; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci dst = (void *)runtime->dma_area + 818c2ecf20Sopenharmony_ci frames_to_bytes(runtime, pcm_buffer_pointer); 828c2ecf20Sopenharmony_ci remaining_frames = runtime->buffer_size - pcm_buffer_pointer; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* The first data channel is for event counter. */ 858c2ecf20Sopenharmony_ci buffer += 1; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci for (i = 0; i < frames; ++i) { 888c2ecf20Sopenharmony_ci for (c = 0; c < channels; ++c) { 898c2ecf20Sopenharmony_ci *dst = be32_to_cpu(buffer[c]); 908c2ecf20Sopenharmony_ci dst++; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci buffer += s->data_block_quadlets; 938c2ecf20Sopenharmony_ci if (--remaining_frames == 0) 948c2ecf20Sopenharmony_ci dst = (void *)runtime->dma_area; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer, 998c2ecf20Sopenharmony_ci unsigned int data_blocks) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct amdtp_tscm *p = s->protocol; 1028c2ecf20Sopenharmony_ci unsigned int channels, i, c; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci channels = p->pcm_channels; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < data_blocks; ++i) { 1078c2ecf20Sopenharmony_ci for (c = 0; c < channels; ++c) 1088c2ecf20Sopenharmony_ci buffer[c] = 0x00000000; 1098c2ecf20Sopenharmony_ci buffer += s->data_block_quadlets; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciint amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, 1148c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int err; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* 1198c2ecf20Sopenharmony_ci * Our implementation allows this protocol to deliver 24 bit sample in 1208c2ecf20Sopenharmony_ci * 32bit data channel. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); 1238c2ecf20Sopenharmony_ci if (err < 0) 1248c2ecf20Sopenharmony_ci return err; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return amdtp_stream_add_pcm_hw_constraints(s, runtime); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void read_status_messages(struct amdtp_stream *s, 1308c2ecf20Sopenharmony_ci __be32 *buffer, unsigned int data_blocks) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream); 1338c2ecf20Sopenharmony_ci bool used = READ_ONCE(tscm->hwdep->used); 1348c2ecf20Sopenharmony_ci int i; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < data_blocks; i++) { 1378c2ecf20Sopenharmony_ci unsigned int index; 1388c2ecf20Sopenharmony_ci __be32 before; 1398c2ecf20Sopenharmony_ci __be32 after; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT; 1428c2ecf20Sopenharmony_ci before = tscm->state[index]; 1438c2ecf20Sopenharmony_ci after = buffer[s->data_block_quadlets - 1]; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (used && index > 4 && index < 16) { 1468c2ecf20Sopenharmony_ci __be32 mask; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (index == 5) 1498c2ecf20Sopenharmony_ci mask = cpu_to_be32(~0x0000ffff); 1508c2ecf20Sopenharmony_ci else if (index == 6) 1518c2ecf20Sopenharmony_ci mask = cpu_to_be32(~0x0000ffff); 1528c2ecf20Sopenharmony_ci else if (index == 8) 1538c2ecf20Sopenharmony_ci mask = cpu_to_be32(~0x000f0f00); 1548c2ecf20Sopenharmony_ci else 1558c2ecf20Sopenharmony_ci mask = cpu_to_be32(~0x00000000); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if ((before ^ after) & mask) { 1588c2ecf20Sopenharmony_ci struct snd_firewire_tascam_change *entry = 1598c2ecf20Sopenharmony_ci &tscm->queue[tscm->push_pos]; 1608c2ecf20Sopenharmony_ci unsigned long flag; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci spin_lock_irqsave(&tscm->lock, flag); 1638c2ecf20Sopenharmony_ci entry->index = index; 1648c2ecf20Sopenharmony_ci entry->before = before; 1658c2ecf20Sopenharmony_ci entry->after = after; 1668c2ecf20Sopenharmony_ci if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT) 1678c2ecf20Sopenharmony_ci tscm->push_pos = 0; 1688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tscm->lock, flag); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci wake_up(&tscm->hwdep_wait); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci tscm->state[index] = after; 1758c2ecf20Sopenharmony_ci buffer += s->data_block_quadlets; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, 1808c2ecf20Sopenharmony_ci const struct pkt_desc *descs, 1818c2ecf20Sopenharmony_ci unsigned int packets, 1828c2ecf20Sopenharmony_ci struct snd_pcm_substream *pcm) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci unsigned int pcm_frames = 0; 1858c2ecf20Sopenharmony_ci int i; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci for (i = 0; i < packets; ++i) { 1888c2ecf20Sopenharmony_ci const struct pkt_desc *desc = descs + i; 1898c2ecf20Sopenharmony_ci __be32 *buf = desc->ctx_payload; 1908c2ecf20Sopenharmony_ci unsigned int data_blocks = desc->data_blocks; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (pcm) { 1938c2ecf20Sopenharmony_ci read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); 1948c2ecf20Sopenharmony_ci pcm_frames += data_blocks; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci read_status_messages(s, buf, data_blocks); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return pcm_frames; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic unsigned int process_it_ctx_payloads(struct amdtp_stream *s, 2048c2ecf20Sopenharmony_ci const struct pkt_desc *descs, 2058c2ecf20Sopenharmony_ci unsigned int packets, 2068c2ecf20Sopenharmony_ci struct snd_pcm_substream *pcm) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci unsigned int pcm_frames = 0; 2098c2ecf20Sopenharmony_ci int i; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (i = 0; i < packets; ++i) { 2128c2ecf20Sopenharmony_ci const struct pkt_desc *desc = descs + i; 2138c2ecf20Sopenharmony_ci __be32 *buf = desc->ctx_payload; 2148c2ecf20Sopenharmony_ci unsigned int data_blocks = desc->data_blocks; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (pcm) { 2178c2ecf20Sopenharmony_ci write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); 2188c2ecf20Sopenharmony_ci pcm_frames += data_blocks; 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci write_pcm_silence(s, buf, data_blocks); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return pcm_frames; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciint amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, 2288c2ecf20Sopenharmony_ci enum amdtp_stream_direction dir, unsigned int pcm_channels) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci amdtp_stream_process_ctx_payloads_t process_ctx_payloads; 2318c2ecf20Sopenharmony_ci struct amdtp_tscm *p; 2328c2ecf20Sopenharmony_ci unsigned int fmt; 2338c2ecf20Sopenharmony_ci int err; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (dir == AMDTP_IN_STREAM) { 2368c2ecf20Sopenharmony_ci fmt = AMDTP_FMT_TSCM_TX; 2378c2ecf20Sopenharmony_ci process_ctx_payloads = process_ir_ctx_payloads; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci fmt = AMDTP_FMT_TSCM_RX; 2408c2ecf20Sopenharmony_ci process_ctx_payloads = process_it_ctx_payloads; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci err = amdtp_stream_init(s, unit, dir, 2448c2ecf20Sopenharmony_ci CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, 2458c2ecf20Sopenharmony_ci process_ctx_payloads, sizeof(struct amdtp_tscm)); 2468c2ecf20Sopenharmony_ci if (err < 0) 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (dir == AMDTP_OUT_STREAM) { 2508c2ecf20Sopenharmony_ci // Use fixed value for FDF field. 2518c2ecf20Sopenharmony_ci s->ctx_data.rx.fdf = 0x00; 2528c2ecf20Sopenharmony_ci // Not used. 2538c2ecf20Sopenharmony_ci s->ctx_data.rx.syt_override = 0x0000; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* This protocol uses fixed number of data channels for PCM samples. */ 2578c2ecf20Sopenharmony_ci p = s->protocol; 2588c2ecf20Sopenharmony_ci p->pcm_channels = pcm_channels; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 262