GNU Linux-libre 4.19.304-gnu1
[releases.git] / sound / firewire / motu / motu-transaction.c
1 /*
2  * motu-transaction.c - a part of driver for MOTU FireWire series
3  *
4  * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
5  *
6  * Licensed under the terms of the GNU General Public License, version 2.
7  */
8
9
10 #include "motu.h"
11
12 #define SND_MOTU_ADDR_BASE      0xfffff0000000ULL
13 #define ASYNC_ADDR_HI  0x0b04
14 #define ASYNC_ADDR_LO  0x0b08
15
16 int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
17                               size_t size)
18 {
19         int tcode;
20
21         if (size % sizeof(__be32) > 0 || size <= 0)
22                 return -EINVAL;
23         if (size == sizeof(__be32))
24                 tcode = TCODE_READ_QUADLET_REQUEST;
25         else
26                 tcode = TCODE_READ_BLOCK_REQUEST;
27
28         return snd_fw_transaction(motu->unit, tcode,
29                                   SND_MOTU_ADDR_BASE + offset, reg, size, 0);
30 }
31
32 int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
33                                size_t size)
34 {
35         int tcode;
36
37         if (size % sizeof(__be32) > 0 || size <= 0)
38                 return -EINVAL;
39         if (size == sizeof(__be32))
40                 tcode = TCODE_WRITE_QUADLET_REQUEST;
41         else
42                 tcode = TCODE_WRITE_BLOCK_REQUEST;
43
44         return snd_fw_transaction(motu->unit, tcode,
45                                   SND_MOTU_ADDR_BASE + offset, reg, size, 0);
46 }
47
48 static void handle_message(struct fw_card *card, struct fw_request *request,
49                            int tcode, int destination, int source,
50                            int generation, unsigned long long offset,
51                            void *data, size_t length, void *callback_data)
52 {
53         struct snd_motu *motu = callback_data;
54         __be32 *buf = (__be32 *)data;
55         unsigned long flags;
56
57         if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
58                 fw_send_response(card, request, RCODE_COMPLETE);
59                 return;
60         }
61
62         if (offset != motu->async_handler.offset || length != 4) {
63                 fw_send_response(card, request, RCODE_ADDRESS_ERROR);
64                 return;
65         }
66
67         spin_lock_irqsave(&motu->lock, flags);
68         motu->msg = be32_to_cpu(*buf);
69         spin_unlock_irqrestore(&motu->lock, flags);
70
71         fw_send_response(card, request, RCODE_COMPLETE);
72
73         wake_up(&motu->hwdep_wait);
74 }
75
76 int snd_motu_transaction_reregister(struct snd_motu *motu)
77 {
78         struct fw_device *device = fw_parent_device(motu->unit);
79         __be32 data;
80         int err;
81
82         if (motu->async_handler.callback_data == NULL)
83                 return -EINVAL;
84
85         /* Register messaging address. Block transaction is not allowed. */
86         data = cpu_to_be32((device->card->node_id << 16) |
87                            (motu->async_handler.offset >> 32));
88         err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data,
89                                          sizeof(data));
90         if (err < 0)
91                 return err;
92
93         data = cpu_to_be32(motu->async_handler.offset);
94         return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data,
95                                           sizeof(data));
96 }
97
98 int snd_motu_transaction_register(struct snd_motu *motu)
99 {
100         static const struct fw_address_region resp_register_region = {
101                 .start  = 0xffffe0000000ull,
102                 .end    = 0xffffe000ffffull,
103         };
104         int err;
105
106         /* Perhaps, 4 byte messages are transferred. */
107         motu->async_handler.length = 4;
108         motu->async_handler.address_callback = handle_message;
109         motu->async_handler.callback_data = motu;
110
111         err = fw_core_add_address_handler(&motu->async_handler,
112                                           &resp_register_region);
113         if (err < 0)
114                 return err;
115
116         err = snd_motu_transaction_reregister(motu);
117         if (err < 0) {
118                 fw_core_remove_address_handler(&motu->async_handler);
119                 motu->async_handler.address_callback = NULL;
120         }
121
122         return err;
123 }
124
125 void snd_motu_transaction_unregister(struct snd_motu *motu)
126 {
127         __be32 data;
128
129         if (motu->async_handler.address_callback != NULL)
130                 fw_core_remove_address_handler(&motu->async_handler);
131         motu->async_handler.address_callback = NULL;
132
133         /* Unregister the address. */
134         data = cpu_to_be32(0x00000000);
135         snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data));
136         snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data));
137 }