1/* 2 * amdtp-tascam.c - a part of driver for TASCAM FireWire series 3 * 4 * Copyright (c) 2015 Takashi Sakamoto 5 * 6 * Licensed under the terms of the GNU General Public License, version 2. 7 */ 8 9#include <sound/pcm.h> 10#include "tascam.h" 11 12#define AMDTP_FMT_TSCM_TX 0x1e 13#define AMDTP_FMT_TSCM_RX 0x3e 14 15struct amdtp_tscm { 16 unsigned int pcm_channels; 17 18 void (*transfer_samples)(struct amdtp_stream *s, 19 struct snd_pcm_substream *pcm, 20 __be32 *buffer, unsigned int frames); 21}; 22 23int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) 24{ 25 struct amdtp_tscm *p = s->protocol; 26 unsigned int data_channels; 27 28 if (amdtp_stream_running(s)) 29 return -EBUSY; 30 31 data_channels = p->pcm_channels; 32 33 /* Packets in in-stream have extra 2 data channels. */ 34 if (s->direction == AMDTP_IN_STREAM) 35 data_channels += 2; 36 37 return amdtp_stream_set_parameters(s, rate, data_channels); 38} 39 40static void write_pcm_s32(struct amdtp_stream *s, 41 struct snd_pcm_substream *pcm, 42 __be32 *buffer, unsigned int frames) 43{ 44 struct amdtp_tscm *p = s->protocol; 45 struct snd_pcm_runtime *runtime = pcm->runtime; 46 unsigned int channels, remaining_frames, i, c; 47 const u32 *src; 48 49 channels = p->pcm_channels; 50 src = (void *)runtime->dma_area + 51 frames_to_bytes(runtime, s->pcm_buffer_pointer); 52 remaining_frames = runtime->buffer_size - s->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 write_pcm_s16(struct amdtp_stream *s, 66 struct snd_pcm_substream *pcm, 67 __be32 *buffer, unsigned int frames) 68{ 69 struct amdtp_tscm *p = s->protocol; 70 struct snd_pcm_runtime *runtime = pcm->runtime; 71 unsigned int channels, remaining_frames, i, c; 72 const u16 *src; 73 74 channels = p->pcm_channels; 75 src = (void *)runtime->dma_area + 76 frames_to_bytes(runtime, s->pcm_buffer_pointer); 77 remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; 78 79 for (i = 0; i < frames; ++i) { 80 for (c = 0; c < channels; ++c) { 81 buffer[c] = cpu_to_be32(*src << 16); 82 src++; 83 } 84 buffer += s->data_block_quadlets; 85 if (--remaining_frames == 0) 86 src = (void *)runtime->dma_area; 87 } 88} 89 90static void read_pcm_s32(struct amdtp_stream *s, 91 struct snd_pcm_substream *pcm, 92 __be32 *buffer, unsigned int frames) 93{ 94 struct amdtp_tscm *p = s->protocol; 95 struct snd_pcm_runtime *runtime = pcm->runtime; 96 unsigned int channels, remaining_frames, i, c; 97 u32 *dst; 98 99 channels = p->pcm_channels; 100 dst = (void *)runtime->dma_area + 101 frames_to_bytes(runtime, s->pcm_buffer_pointer); 102 remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; 103 104 /* The first data channel is for event counter. */ 105 buffer += 1; 106 107 for (i = 0; i < frames; ++i) { 108 for (c = 0; c < channels; ++c) { 109 *dst = be32_to_cpu(buffer[c]); 110 dst++; 111 } 112 buffer += s->data_block_quadlets; 113 if (--remaining_frames == 0) 114 dst = (void *)runtime->dma_area; 115 } 116} 117 118static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer, 119 unsigned int data_blocks) 120{ 121 struct amdtp_tscm *p = s->protocol; 122 unsigned int channels, i, c; 123 124 channels = p->pcm_channels; 125 126 for (i = 0; i < data_blocks; ++i) { 127 for (c = 0; c < channels; ++c) 128 buffer[c] = 0x00000000; 129 buffer += s->data_block_quadlets; 130 } 131} 132 133int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, 134 struct snd_pcm_runtime *runtime) 135{ 136 int err; 137 138 /* 139 * Our implementation allows this protocol to deliver 24 bit sample in 140 * 32bit data channel. 141 */ 142 err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); 143 if (err < 0) 144 return err; 145 146 return amdtp_stream_add_pcm_hw_constraints(s, runtime); 147} 148 149void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) 150{ 151 struct amdtp_tscm *p = s->protocol; 152 153 if (WARN_ON(amdtp_stream_pcm_running(s))) 154 return; 155 156 switch (format) { 157 default: 158 WARN_ON(1); 159 /* fall through */ 160 case SNDRV_PCM_FORMAT_S16: 161 if (s->direction == AMDTP_OUT_STREAM) { 162 p->transfer_samples = write_pcm_s16; 163 break; 164 } 165 WARN_ON(1); 166 /* fall through */ 167 case SNDRV_PCM_FORMAT_S32: 168 if (s->direction == AMDTP_OUT_STREAM) 169 p->transfer_samples = write_pcm_s32; 170 else 171 p->transfer_samples = read_pcm_s32; 172 break; 173 } 174} 175 176static unsigned int process_tx_data_blocks(struct amdtp_stream *s, 177 __be32 *buffer, 178 unsigned int data_blocks, 179 unsigned int *syt) 180{ 181 struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol; 182 struct snd_pcm_substream *pcm; 183 184 pcm = ACCESS_ONCE(s->pcm); 185 if (data_blocks > 0 && pcm) 186 p->transfer_samples(s, pcm, buffer, data_blocks); 187 188 /* A place holder for control messages. */ 189 190 return data_blocks; 191} 192 193static unsigned int process_rx_data_blocks(struct amdtp_stream *s, 194 __be32 *buffer, 195 unsigned int data_blocks, 196 unsigned int *syt) 197{ 198 struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol; 199 struct snd_pcm_substream *pcm; 200 201 /* This field is not used. */ 202 *syt = 0x0000; 203 204 pcm = ACCESS_ONCE(s->pcm); 205 if (pcm) 206 p->transfer_samples(s, pcm, buffer, data_blocks); 207 else 208 write_pcm_silence(s, buffer, data_blocks); 209 210 return data_blocks; 211} 212 213int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, 214 enum amdtp_stream_direction dir, unsigned int pcm_channels) 215{ 216 amdtp_stream_process_data_blocks_t process_data_blocks; 217 struct amdtp_tscm *p; 218 unsigned int fmt; 219 int err; 220 221 if (dir == AMDTP_IN_STREAM) { 222 fmt = AMDTP_FMT_TSCM_TX; 223 process_data_blocks = process_tx_data_blocks; 224 } else { 225 fmt = AMDTP_FMT_TSCM_RX; 226 process_data_blocks = process_rx_data_blocks; 227 } 228 229 err = amdtp_stream_init(s, unit, dir, 230 CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, 231 process_data_blocks, sizeof(struct amdtp_tscm)); 232 if (err < 0) 233 return 0; 234 235 /* Use fixed value for FDF field. */ 236 s->fdf = 0x00; 237 238 /* This protocol uses fixed number of data channels for PCM samples. */ 239 p = s->protocol; 240 p->pcm_channels = pcm_channels; 241 242 return 0; 243} 244