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