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