linux/sound/firewire/tascam/amdtp-tascam.c
<<
>>
Prefs
   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