]> asedeno.scripts.mit.edu Git - linux.git/blob - sound/firewire/fireface/ff-protocol-ff400.c
ALSA: fireface: share register for async transaction of MIDI messages
[linux.git] / sound / firewire / fireface / ff-protocol-ff400.c
1 /*
2  * ff-protocol-ff400.c - a part of driver for RME Fireface series
3  *
4  * Copyright (c) 2015-2017 Takashi Sakamoto
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8
9 #include <linux/delay.h>
10 #include "ff.h"
11
12 #define FF400_STF               0x000080100500ull
13 #define FF400_RX_PACKET_FORMAT  0x000080100504ull
14 #define FF400_ISOC_COMM_START   0x000080100508ull
15 #define FF400_TX_PACKET_FORMAT  0x00008010050cull
16 #define FF400_ISOC_COMM_STOP    0x000080100510ull
17
18 #define FF400_MIDI_HIGH_ADDR    0x0000801003f4ull
19
20 static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
21 {
22         __le32 reg;
23         int i, err;
24
25         /* Check whether the given value is supported or not. */
26         for (i = 0; i < CIP_SFC_COUNT; i++) {
27                 if (amdtp_rate_table[i] == rate)
28                         break;
29         }
30         if (i == CIP_SFC_COUNT)
31                 return -EINVAL;
32
33         /* Set the number of data blocks transferred in a second. */
34         reg = cpu_to_le32(rate);
35         err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
36                                  FF400_STF, &reg, sizeof(reg), 0);
37         if (err < 0)
38                 return err;
39
40         msleep(100);
41
42         /*
43          * Set isochronous channel and the number of quadlets of received
44          * packets.
45          */
46         reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
47                           ff->rx_resources.channel);
48         err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
49                                  FF400_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
50         if (err < 0)
51                 return err;
52
53         /*
54          * Set isochronous channel and the number of quadlets of transmitted
55          * packet.
56          */
57         /* TODO: investigate the purpose of this 0x80. */
58         reg = cpu_to_le32((0x80 << 24) |
59                           (ff->tx_resources.channel << 5) |
60                           (ff->tx_stream.data_block_quadlets));
61         err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
62                                  FF400_TX_PACKET_FORMAT, &reg, sizeof(reg), 0);
63         if (err < 0)
64                 return err;
65
66         /* Allow to transmit packets. */
67         reg = cpu_to_le32(0x00000001);
68         return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
69                                  FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
70 }
71
72 static void ff400_finish_session(struct snd_ff *ff)
73 {
74         __le32 reg;
75
76         reg = cpu_to_le32(0x80000000);
77         snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
78                            FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
79 }
80
81 static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable)
82 {
83         __le32 *reg;
84         int i;
85         int err;
86
87         reg = kcalloc(18, sizeof(__le32), GFP_KERNEL);
88         if (reg == NULL)
89                 return -ENOMEM;
90
91         if (enable) {
92                 /*
93                  * Each quadlet is corresponding to data channels in a data
94                  * blocks in reverse order. Precisely, quadlets for available
95                  * data channels should be enabled. Here, I take second best
96                  * to fetch PCM frames from all of data channels regardless of
97                  * stf.
98                  */
99                 for (i = 0; i < 18; ++i)
100                         reg[i] = cpu_to_le32(0x00000001);
101         }
102
103         err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
104                                  SND_FF_REG_FETCH_PCM_FRAMES, reg,
105                                  sizeof(__le32) * 18, 0);
106         kfree(reg);
107         return err;
108 }
109
110 const struct snd_ff_protocol snd_ff_protocol_ff400 = {
111         .begin_session          = ff400_begin_session,
112         .finish_session         = ff400_finish_session,
113         .switch_fetching_mode   = ff400_switch_fetching_mode,
114
115         .midi_high_addr_reg     = FF400_MIDI_HIGH_ADDR,
116 };