254 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * amdtp-tascam.c - a part of driver for TASCAM FireWire series4 *5 * Copyright (c) 2015 Takashi Sakamoto6 */7 8#include <sound/pcm.h>9#include "tascam.h"10 11#define AMDTP_FMT_TSCM_TX 0x1e12#define AMDTP_FMT_TSCM_RX 0x3e13 14struct amdtp_tscm {15 unsigned int pcm_channels;16};17 18int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)19{20 struct amdtp_tscm *p = s->protocol;21 unsigned int data_channels;22 23 if (amdtp_stream_running(s))24 return -EBUSY;25 26 data_channels = p->pcm_channels;27 28 /* Packets in in-stream have extra 2 data channels. */29 if (s->direction == AMDTP_IN_STREAM)30 data_channels += 2;31 32 return amdtp_stream_set_parameters(s, rate, data_channels, 1);33}34 35static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,36 __be32 *buffer, unsigned int frames,37 unsigned int pcm_frames)38{39 struct amdtp_tscm *p = s->protocol;40 unsigned int channels = p->pcm_channels;41 struct snd_pcm_runtime *runtime = pcm->runtime;42 unsigned int pcm_buffer_pointer;43 int remaining_frames;44 const u32 *src;45 int i, c;46 47 pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;48 pcm_buffer_pointer %= runtime->buffer_size;49 50 src = (void *)runtime->dma_area +51 frames_to_bytes(runtime, pcm_buffer_pointer);52 remaining_frames = runtime->buffer_size - pcm_buffer_pointer;53 54 for (i = 0; i < frames; ++i) {55 for (c = 0; c < channels; ++c) {56 buffer[c] = cpu_to_be32(*src);57 src++;58 }59 buffer += s->data_block_quadlets;60 if (--remaining_frames == 0)61 src = (void *)runtime->dma_area;62 }63}64 65static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,66 __be32 *buffer, unsigned int frames,67 unsigned int pcm_frames)68{69 struct amdtp_tscm *p = s->protocol;70 unsigned int channels = p->pcm_channels;71 struct snd_pcm_runtime *runtime = pcm->runtime;72 unsigned int pcm_buffer_pointer;73 int remaining_frames;74 u32 *dst;75 int i, c;76 77 pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;78 pcm_buffer_pointer %= runtime->buffer_size;79 80 dst = (void *)runtime->dma_area +81 frames_to_bytes(runtime, pcm_buffer_pointer);82 remaining_frames = runtime->buffer_size - pcm_buffer_pointer;83 84 /* The first data channel is for event counter. */85 buffer += 1;86 87 for (i = 0; i < frames; ++i) {88 for (c = 0; c < channels; ++c) {89 *dst = be32_to_cpu(buffer[c]);90 dst++;91 }92 buffer += s->data_block_quadlets;93 if (--remaining_frames == 0)94 dst = (void *)runtime->dma_area;95 }96}97 98static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,99 unsigned int data_blocks)100{101 struct amdtp_tscm *p = s->protocol;102 unsigned int channels, i, c;103 104 channels = p->pcm_channels;105 106 for (i = 0; i < data_blocks; ++i) {107 for (c = 0; c < channels; ++c)108 buffer[c] = 0x00000000;109 buffer += s->data_block_quadlets;110 }111}112 113int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,114 struct snd_pcm_runtime *runtime)115{116 int err;117 118 /*119 * Our implementation allows this protocol to deliver 24 bit sample in120 * 32bit data channel.121 */122 err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);123 if (err < 0)124 return err;125 126 return amdtp_stream_add_pcm_hw_constraints(s, runtime);127}128 129static void read_status_messages(struct amdtp_stream *s,130 __be32 *buffer, unsigned int data_blocks)131{132 struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream);133 bool used = READ_ONCE(tscm->hwdep->used);134 int i;135 136 for (i = 0; i < data_blocks; i++) {137 unsigned int index;138 __be32 before;139 __be32 after;140 141 index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT;142 before = tscm->state[index];143 after = buffer[s->data_block_quadlets - 1];144 145 if (used && index > 4 && index < 16) {146 __be32 mask;147 148 if (index == 5)149 mask = cpu_to_be32(~0x0000ffff);150 else if (index == 6)151 mask = cpu_to_be32(~0x0000ffff);152 else if (index == 8)153 mask = cpu_to_be32(~0x000f0f00);154 else155 mask = cpu_to_be32(~0x00000000);156 157 if ((before ^ after) & mask) {158 struct snd_firewire_tascam_change *entry =159 &tscm->queue[tscm->push_pos];160 unsigned long flag;161 162 spin_lock_irqsave(&tscm->lock, flag);163 entry->index = index;164 entry->before = before;165 entry->after = after;166 if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)167 tscm->push_pos = 0;168 spin_unlock_irqrestore(&tscm->lock, flag);169 170 wake_up(&tscm->hwdep_wait);171 }172 }173 174 tscm->state[index] = after;175 buffer += s->data_block_quadlets;176 }177}178 179static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,180 unsigned int count, struct snd_pcm_substream *pcm)181{182 unsigned int pcm_frames = 0;183 int i;184 185 for (i = 0; i < count; ++i) {186 __be32 *buf = desc->ctx_payload;187 unsigned int data_blocks = desc->data_blocks;188 189 if (pcm) {190 read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);191 pcm_frames += data_blocks;192 }193 194 read_status_messages(s, buf, data_blocks);195 196 desc = amdtp_stream_next_packet_desc(s, desc);197 }198}199 200static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,201 unsigned int count, struct snd_pcm_substream *pcm)202{203 unsigned int pcm_frames = 0;204 int i;205 206 for (i = 0; i < count; ++i) {207 __be32 *buf = desc->ctx_payload;208 unsigned int data_blocks = desc->data_blocks;209 210 if (pcm) {211 write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);212 pcm_frames += data_blocks;213 } else {214 write_pcm_silence(s, buf, data_blocks);215 }216 217 desc = amdtp_stream_next_packet_desc(s, desc);218 }219}220 221int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,222 enum amdtp_stream_direction dir, unsigned int pcm_channels)223{224 amdtp_stream_process_ctx_payloads_t process_ctx_payloads;225 unsigned int flags = CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK | CIP_UNAWARE_SYT;226 struct amdtp_tscm *p;227 unsigned int fmt;228 int err;229 230 if (dir == AMDTP_IN_STREAM) {231 fmt = AMDTP_FMT_TSCM_TX;232 process_ctx_payloads = process_ir_ctx_payloads;233 } else {234 fmt = AMDTP_FMT_TSCM_RX;235 process_ctx_payloads = process_it_ctx_payloads;236 }237 238 err = amdtp_stream_init(s, unit, dir, flags, fmt,239 process_ctx_payloads, sizeof(struct amdtp_tscm));240 if (err < 0)241 return err;242 243 if (dir == AMDTP_OUT_STREAM) {244 // Use fixed value for FDF field.245 s->ctx_data.rx.fdf = 0x00;246 }247 248 /* This protocol uses fixed number of data channels for PCM samples. */249 p = s->protocol;250 p->pcm_channels = pcm_channels;251 252 return 0;253}254