-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathcontrol.c
191 lines (157 loc) · 5.2 KB
/
control.c
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
* Linux driver for Mytek Digital Stereo192-DSD DAC USB2
*
* Mixer control
*
* Based on 6fire usb driver
*
* Adapted for Mytek by : Jurgen Kramer
* Last updated : Feb 28, 2014
* Copyright : (C) Jurgen Kramer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/interrupt.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include "control.h"
#include "comm.h"
#include "chip.h"
/*
* Init data that needs to be sent to device.
* Taken from snooping the WinXP USB driver
*/
static const struct {
u8 type;
u8 reg;
u8 valh;
u8 vall;
}
init_data_mytek[] = {
{ 0x02, 0x09, 0x00, 0x01 }, /* Set mystery reg 9 to 1 */
{ 0x22, 0x00, 0x00, 0xff }, /* Set Pin=0 to low */
{ 0x20, 0x00, 0x08, 0xff }, /* Pin PC0 to output */
{ 0x22, 0x03, 0x00, 0xff }, /* Set Pin=3 to low */
{ 0x20, 0x03, 0x08, 0xff }, /* Set GPIO pin PC3 to output */
{ 0x02, 0x01, 0x00, 0x10 }, /* Set rate = 88.2k */
{ 0x02, 0x81, 0x00, 0x00 }, /* Read rate */
{ 0x02, 0x00, 0x00, 0x00 }, /* Disable streaming */
{ 0x02, 0x80, 0x00, 0x00 }, /* Check it */
{ 0x02, 0x05, 0x00, 0x00 }, /* Out Sample Delay = 0 */
{ 0x02, 0x02, 0x07, 0x07 }, /* Enable all I2S_I and I2S_O */
{ 0x02, 0x04, 0x00, 0x06 }, /* Set DSD_IO[7..0] -> 6 = 0000 0110 */
{ 0x02, 0x03, 0x00, 0x00 }, /* Disable SPDIF_I and SPDIF_O */
{ 0x02, 0x09, 0x00, 0x01 }, /* Set mystery reg 9 to 1 */
{ 0x22, 0x00, 0x01, 0xff }, /* Set GPIO pin 0 to high */
{ 0x02, 0x00, 0x00, 0x01 }, /* Enable streaming */
{ }
};
static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
/* values to write to soundcard register for all supported samplerates */
static const u16 rates_mytek_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
static const u16 rates_mytek_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
static int mytek_control_set_rate(struct control_runtime *rt, int rate)
{
int ret;
struct usb_device *device = rt->chip->dev;
struct comm_runtime *comm_rt = rt->chip->comm;
if (rate < 0 || rate >= CONTROL_N_RATES)
return -EINVAL;
ret = usb_set_interface(device, 1, rates_altsetting[rate]);
if (ret < 0) {
dev_err(&rt->chip->dev->dev,
"mytek_control_set_rate: usb_set_interface failed\n");
return ret;
}
comm_rt->write8(comm_rt, 0x22, 0x00, 0x00); /* Set Pin 0 to low state */
comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00); /* Disable streaming */
/* set soundcard clock */
ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_mytek_vl[rate],
rates_mytek_vh[rate]);
if (ret < 0) {
dev_err(&rt->chip->dev->dev,
"mytek_control_set_rate: setting rate failed\n");
return ret;
}
comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00); /* Disable streaming */
comm_rt->write16(comm_rt, 0x02, 0x05, 0x00, 0x00); /* Set out sample delay to 0 */
comm_rt->write16(comm_rt, 0x02, 0x02, 0x07, 0x07); /* Enable all I2S I and O */
comm_rt->write16(comm_rt, 0x02, 0x04, 0x00, 0x06); /* Enable DSD */
comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); /* Disable SPDIF O and I */
comm_rt->write16(comm_rt, 0x02, 0x09, 0x00, 0x01); /* Set mystery reg 9 to 1 */
comm_rt->write8(comm_rt, 0x22, 0x00, 0x01); /* Set GPIO pin 0 to high state */
msleep(15); /* Let the Mytek's display catch up with the new rate */
return 0;
}
static int mytek_control_set_channels(
struct control_runtime *rt, int n_analog_out,
int n_analog_in, bool spdif_out, bool spdif_in)
{
int ret;
struct comm_runtime *comm_rt = rt->chip->comm;
/* Enable USBPAL I2S Inputs and Outputs
*
* The windows driver enables all IS2 Inputs and Outputs
* If we do that here, output gets noisy. For now only
* enable I2S_I 2,1 and 0.
* TODO: Fix/verify
*/
ret = comm_rt->write16(comm_rt, 0x02, 0x02, 0x07, 0x03);
if (ret < 0)
return ret;
/* disable all USBPAL SPDIF inputs and outputs */
ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
if (ret < 0)
return ret;
return 0;
}
static int mytek_control_streaming_update(struct control_runtime *rt)
{
struct comm_runtime *comm_rt = rt->chip->comm;
if (comm_rt) {
return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
(rt->usb_streaming ? 0x01 : 0x00) );
}
return -EINVAL;
}
int mytek_control_init(struct mytek_chip *chip)
{
int i;
struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),
GFP_KERNEL);
struct comm_runtime *comm_rt = chip->comm;
if (!rt)
return -ENOMEM;
rt->chip = chip;
rt->update_streaming = mytek_control_streaming_update;
rt->set_rate = mytek_control_set_rate;
rt->set_channels = mytek_control_set_channels;
i = 0;
while (init_data_mytek[i].type) {
if (init_data_mytek[i].type != 0x02) {
comm_rt->write8(comm_rt, init_data_mytek[i].type,
init_data_mytek[i].reg,
init_data_mytek[i].valh);
} else {
comm_rt->write16(comm_rt, init_data_mytek[i].type,
init_data_mytek[i].reg,
init_data_mytek[i].valh,
init_data_mytek[i].vall);
}
i++;
}
mytek_control_streaming_update(rt);
chip->control = rt;
return 0;
}
void mytek_control_abort(struct mytek_chip *chip)
{
}
void mytek_control_destroy(struct mytek_chip *chip)
{
kfree(chip->control);
chip->control = NULL;
}