linux/sound/firewire/tascam/amdtp-tascam.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * amdtp-tascam.c - a part of driver for TASCAM FireWire series
   4 *
   5 * Copyright (c) 2015 Takashi Sakamoto
   6 */
   7
   8#include <sound/pcm.h>
   9#include "tascam.h"
  10
  11#define AMDTP_FMT_TSCM_TX       0x1e
  12#define AMDTP_FMT_TSCM_RX       0x3e
  13
  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);
  33}
  34
  35static void write_pcm_s32(struct amdtp_stream *s,
  36                          struct snd_pcm_substream *pcm,
  37                          __be32 *buffer, unsigned int frames)
  38{
  39        struct amdtp_tscm *p = s->protocol;
  40        struct snd_pcm_runtime *runtime = pcm->runtime;
  41        unsigned int channels, remaining_frames, i, c;
  42        const u32 *src;
  43
  44        channels = p->pcm_channels;
  45        src = (void *)runtime->dma_area +
  46                        frames_to_bytes(runtime, s->pcm_buffer_pointer);
  47        remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
  48
  49        for (i = 0; i < frames; ++i) {
  50                for (c = 0; c < channels; ++c) {
  51                        buffer[c] = cpu_to_be32(*src);
  52                        src++;
  53                }
  54                buffer += s->data_block_quadlets;
  55                if (--remaining_frames == 0)
  56                        src = (void *)runtime->dma_area;
  57        }
  58}
  59
  60static void read_pcm_s32(struct amdtp_stream *s,
  61                         struct snd_pcm_substream *pcm,
  62                         __be32 *buffer, unsigned int frames)
  63{
  64        struct amdtp_tscm *p = s->protocol;
  65        struct snd_pcm_runtime *runtime = pcm->runtime;
  66        unsigned int channels, remaining_frames, i, c;
  67        u32 *dst;
  68
  69        channels = p->pcm_channels;
  70        dst  = (void *)runtime->dma_area +
  71                        frames_to_bytes(runtime, s->pcm_buffer_pointer);
  72        remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
  73
  74        /* The first data channel is for event counter. */
  75        buffer += 1;
  76
  77        for (i = 0; i < frames; ++i) {
  78                for (c = 0; c < channels; ++c) {
  79                        *dst = be32_to_cpu(buffer[c]);
  80                        dst++;
  81                }
  82                buffer += s->data_block_quadlets;
  83                if (--remaining_frames == 0)
  84                        dst = (void *)runtime->dma_area;
  85        }
  86}
  87
  88static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
  89                              unsigned int data_blocks)
  90{
  91        struct amdtp_tscm *p = s->protocol;
  92        unsigned int channels, i, c;
  93
  94        channels = p->pcm_channels;
  95
  96        for (i = 0; i < data_blocks; ++i) {
  97                for (c = 0; c < channels; ++c)
  98                        buffer[c] = 0x00000000;
  99                buffer += s->data_block_quadlets;
 100        }
 101}
 102
 103int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
 104                                      struct snd_pcm_runtime *runtime)
 105{
 106        int err;
 107
 108        /*
 109         * Our implementation allows this protocol to deliver 24 bit sample in
 110         * 32bit data channel.
 111         */
 112        err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
 113        if (err < 0)
 114                return err;
 115
 116        return amdtp_stream_add_pcm_hw_constraints(s, runtime);
 117}
 118
 119static void read_status_messages(struct amdtp_stream *s,
 120                                 __be32 *buffer, unsigned int data_blocks)
 121{
 122        struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream);
 123        bool used = READ_ONCE(tscm->hwdep->used);
 124        int i;
 125
 126        for (i = 0; i < data_blocks; i++) {
 127                unsigned int index;
 128                __be32 before;
 129                __be32 after;
 130
 131                index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT;
 132                before = tscm->state[index];
 133                after = buffer[s->data_block_quadlets - 1];
 134
 135                if (used && index > 4 && index < 16) {
 136                        __be32 mask;
 137
 138                        if (index == 5)
 139                                mask = cpu_to_be32(~0x0000ffff);
 140                        else if (index == 6)
 141                                mask = cpu_to_be32(~0x0000ffff);
 142                        else if (index == 8)
 143                                mask = cpu_to_be32(~0x000f0f00);
 144                        else
 145                                mask = cpu_to_be32(~0x00000000);
 146
 147                        if ((before ^ after) & mask) {
 148                                struct snd_firewire_tascam_change *entry =
 149                                                &tscm->queue[tscm->push_pos];
 150
 151                                spin_lock_irq(&tscm->lock);
 152                                entry->index = index;
 153                                entry->before = before;
 154                                entry->after = after;
 155                                if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)
 156                                        tscm->push_pos = 0;
 157                                spin_unlock_irq(&tscm->lock);
 158
 159                                wake_up(&tscm->hwdep_wait);
 160                        }
 161                }
 162
 163                tscm->state[index] = after;
 164                buffer += s->data_block_quadlets;
 165        }
 166}
 167
 168static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
 169                                           __be32 *buffer,
 170                                           unsigned int data_blocks,
 171                                           unsigned int *syt)
 172{
 173        struct snd_pcm_substream *pcm;
 174
 175        pcm = READ_ONCE(s->pcm);
 176        if (data_blocks > 0 && pcm)
 177                read_pcm_s32(s, pcm, buffer, data_blocks);
 178
 179        read_status_messages(s, buffer, data_blocks);
 180
 181        return data_blocks;
 182}
 183
 184static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
 185                                           __be32 *buffer,
 186                                           unsigned int data_blocks,
 187                                           unsigned int *syt)
 188{
 189        struct snd_pcm_substream *pcm;
 190
 191        /* This field is not used. */
 192        *syt = 0x0000;
 193
 194        pcm = READ_ONCE(s->pcm);
 195        if (pcm)
 196                write_pcm_s32(s, pcm, buffer, data_blocks);
 197        else
 198                write_pcm_silence(s, buffer, data_blocks);
 199
 200        return data_blocks;
 201}
 202
 203int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
 204                    enum amdtp_stream_direction dir, unsigned int pcm_channels)
 205{
 206        amdtp_stream_process_data_blocks_t process_data_blocks;
 207        struct amdtp_tscm *p;
 208        unsigned int fmt;
 209        int err;
 210
 211        if (dir == AMDTP_IN_STREAM) {
 212                fmt = AMDTP_FMT_TSCM_TX;
 213                process_data_blocks = process_tx_data_blocks;
 214        } else {
 215                fmt = AMDTP_FMT_TSCM_RX;
 216                process_data_blocks = process_rx_data_blocks;
 217        }
 218
 219        err = amdtp_stream_init(s, unit, dir,
 220                                CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
 221                                process_data_blocks, sizeof(struct amdtp_tscm));
 222        if (err < 0)
 223                return 0;
 224
 225        /* Use fixed value for FDF field. */
 226        s->ctx_data.rx.fdf = 0x00;
 227
 228        /* This protocol uses fixed number of data channels for PCM samples. */
 229        p = s->protocol;
 230        p->pcm_channels = pcm_channels;
 231
 232        return 0;
 233}
 234