xref: /linux/sound/firewire/dice/dice-extension.c (revision a4eb44a6435d6d8f9e642407a4a06f65eb90ca04)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * dice-extension.c - a part of driver for DICE based devices
4  *
5  * Copyright (c) 2018 Takashi Sakamoto
6  */
7 
8 #include "dice.h"
9 
10 /* For TCD2210/2220, TCAT defines extension of application protocol. */
11 
12 #define DICE_EXT_APP_SPACE		0xffffe0200000uLL
13 
14 #define DICE_EXT_APP_CAPS_OFFSET	0x00
15 #define DICE_EXT_APP_CAPS_SIZE		0x04
16 #define DICE_EXT_APP_CMD_OFFSET		0x08
17 #define DICE_EXT_APP_CMD_SIZE		0x0c
18 #define DICE_EXT_APP_MIXER_OFFSET	0x10
19 #define DICE_EXT_APP_MIXER_SIZE		0x14
20 #define DICE_EXT_APP_PEAK_OFFSET	0x18
21 #define DICE_EXT_APP_PEAK_SIZE		0x1c
22 #define DICE_EXT_APP_ROUTER_OFFSET	0x20
23 #define DICE_EXT_APP_ROUTER_SIZE	0x24
24 #define DICE_EXT_APP_STREAM_OFFSET	0x28
25 #define DICE_EXT_APP_STREAM_SIZE	0x2c
26 #define DICE_EXT_APP_CURRENT_OFFSET	0x30
27 #define DICE_EXT_APP_CURRENT_SIZE	0x34
28 #define DICE_EXT_APP_STANDALONE_OFFSET	0x38
29 #define DICE_EXT_APP_STANDALONE_SIZE	0x3c
30 #define DICE_EXT_APP_APPLICATION_OFFSET	0x40
31 #define DICE_EXT_APP_APPLICATION_SIZE	0x44
32 
33 #define EXT_APP_STREAM_TX_NUMBER	0x0000
34 #define EXT_APP_STREAM_RX_NUMBER	0x0004
35 #define EXT_APP_STREAM_ENTRIES		0x0008
36 #define EXT_APP_STREAM_ENTRY_SIZE	0x010c
37 #define  EXT_APP_NUMBER_AUDIO		0x0000
38 #define  EXT_APP_NUMBER_MIDI		0x0004
39 #define  EXT_APP_NAMES			0x0008
40 #define   EXT_APP_NAMES_SIZE		256
41 #define  EXT_APP_AC3			0x0108
42 
43 #define EXT_APP_CONFIG_LOW_ROUTER	0x0000
44 #define EXT_APP_CONFIG_LOW_STREAM	0x1000
45 #define EXT_APP_CONFIG_MIDDLE_ROUTER	0x2000
46 #define EXT_APP_CONFIG_MIDDLE_STREAM	0x3000
47 #define EXT_APP_CONFIG_HIGH_ROUTER	0x4000
48 #define EXT_APP_CONFIG_HIGH_STREAM	0x5000
49 
50 static inline int read_transaction(struct snd_dice *dice, u64 section_addr,
51 				   u32 offset, void *buf, size_t len)
52 {
53 	return snd_fw_transaction(dice->unit,
54 				  len == 4 ? TCODE_READ_QUADLET_REQUEST :
55 					     TCODE_READ_BLOCK_REQUEST,
56 				  section_addr + offset, buf, len, 0);
57 }
58 
59 static int read_stream_entries(struct snd_dice *dice, u64 section_addr,
60 			       u32 base_offset, unsigned int stream_count,
61 			       unsigned int mode,
62 			       unsigned int pcm_channels[MAX_STREAMS][3],
63 			       unsigned int midi_ports[MAX_STREAMS])
64 {
65 	u32 entry_offset;
66 	__be32 reg[2];
67 	int err;
68 	int i;
69 
70 	for (i = 0; i < stream_count; ++i) {
71 		entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE;
72 		err = read_transaction(dice, section_addr,
73 				    entry_offset + EXT_APP_NUMBER_AUDIO,
74 				    reg, sizeof(reg));
75 		if (err < 0)
76 			return err;
77 		pcm_channels[i][mode] = be32_to_cpu(reg[0]);
78 		midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1]));
79 	}
80 
81 	return 0;
82 }
83 
84 static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
85 {
86 	u32 base_offset;
87 	__be32 reg[2];
88 	unsigned int stream_count;
89 	int mode;
90 	int err = 0;
91 
92 	for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) {
93 		unsigned int cap;
94 
95 		/*
96 		 * Some models report stream formats at highest mode, however
97 		 * they don't support the mode. Check clock capabilities.
98 		 */
99 		if (mode == 2) {
100 			cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000;
101 		} else if (mode == 1) {
102 			cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000;
103 		} else {
104 			cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 |
105 			      CLOCK_CAP_RATE_48000;
106 		}
107 		if (!(cap & dice->clock_caps))
108 			continue;
109 
110 		base_offset = 0x2000 * mode + 0x1000;
111 
112 		err = read_transaction(dice, section_addr,
113 				       base_offset + EXT_APP_STREAM_TX_NUMBER,
114 				       &reg, sizeof(reg));
115 		if (err < 0)
116 			break;
117 
118 		base_offset += EXT_APP_STREAM_ENTRIES;
119 		stream_count = be32_to_cpu(reg[0]);
120 		err = read_stream_entries(dice, section_addr, base_offset,
121 					  stream_count, mode,
122 					  dice->tx_pcm_chs,
123 					  dice->tx_midi_ports);
124 		if (err < 0)
125 			break;
126 
127 		base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
128 		stream_count = be32_to_cpu(reg[1]);
129 		err = read_stream_entries(dice, section_addr, base_offset,
130 					  stream_count,
131 					  mode, dice->rx_pcm_chs,
132 					  dice->rx_midi_ports);
133 		if (err < 0)
134 			break;
135 	}
136 
137 	return err;
138 }
139 
140 int snd_dice_detect_extension_formats(struct snd_dice *dice)
141 {
142 	__be32 *pointers;
143 	unsigned int i;
144 	u64 section_addr;
145 	int err;
146 
147 	pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL);
148 	if (pointers == NULL)
149 		return -ENOMEM;
150 
151 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
152 				 DICE_EXT_APP_SPACE, pointers,
153 				 9 * sizeof(__be32) * 2, 0);
154 	if (err < 0)
155 		goto end;
156 
157 	/* Check two of them for offset have the same value or not. */
158 	for (i = 0; i < 9; ++i) {
159 		int j;
160 
161 		for (j = i + 1; j < 9; ++j) {
162 			if (pointers[i * 2] == pointers[j * 2]) {
163 				// Fallback to limited functionality.
164 				err = -ENXIO;
165 				goto end;
166 			}
167 		}
168 	}
169 
170 	section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4;
171 	err = detect_stream_formats(dice, section_addr);
172 end:
173 	kfree(pointers);
174 	return err;
175 }
176