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