Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/ppc/daca.c
26381 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* PMac DACA lowlevel functions
4
*
5
* Copyright (c) by Takashi Iwai <[email protected]>
6
*/
7
8
9
#include <linux/init.h>
10
#include <linux/i2c.h>
11
#include <linux/kmod.h>
12
#include <linux/slab.h>
13
#include <sound/core.h>
14
#include "pmac.h"
15
16
/* i2c address */
17
#define DACA_I2C_ADDR 0x4d
18
19
/* registers */
20
#define DACA_REG_SR 0x01
21
#define DACA_REG_AVOL 0x02
22
#define DACA_REG_GCFG 0x03
23
24
/* maximum volume value */
25
#define DACA_VOL_MAX 0x38
26
27
28
struct pmac_daca {
29
struct pmac_keywest i2c;
30
int left_vol, right_vol;
31
unsigned int deemphasis : 1;
32
unsigned int amp_on : 1;
33
};
34
35
36
/*
37
* initialize / detect DACA
38
*/
39
static int daca_init_client(struct pmac_keywest *i2c)
40
{
41
unsigned short wdata = 0x00;
42
/* SR: no swap, 1bit delay, 32-48kHz */
43
/* GCFG: power amp inverted, DAC on */
44
if (i2c_smbus_write_byte_data(i2c->client, DACA_REG_SR, 0x08) < 0 ||
45
i2c_smbus_write_byte_data(i2c->client, DACA_REG_GCFG, 0x05) < 0)
46
return -EINVAL;
47
return i2c_smbus_write_block_data(i2c->client, DACA_REG_AVOL,
48
2, (unsigned char*)&wdata);
49
}
50
51
/*
52
* update volume
53
*/
54
static int daca_set_volume(struct pmac_daca *mix)
55
{
56
unsigned char data[2];
57
58
if (! mix->i2c.client)
59
return -ENODEV;
60
61
if (mix->left_vol > DACA_VOL_MAX)
62
data[0] = DACA_VOL_MAX;
63
else
64
data[0] = mix->left_vol;
65
if (mix->right_vol > DACA_VOL_MAX)
66
data[1] = DACA_VOL_MAX;
67
else
68
data[1] = mix->right_vol;
69
data[1] |= mix->deemphasis ? 0x40 : 0;
70
if (i2c_smbus_write_block_data(mix->i2c.client, DACA_REG_AVOL,
71
2, data) < 0) {
72
dev_err(&mix->i2c.client->dev, "failed to set volume\n");
73
return -EINVAL;
74
}
75
return 0;
76
}
77
78
79
/* deemphasis switch */
80
#define daca_info_deemphasis snd_ctl_boolean_mono_info
81
82
static int daca_get_deemphasis(struct snd_kcontrol *kcontrol,
83
struct snd_ctl_elem_value *ucontrol)
84
{
85
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
86
struct pmac_daca *mix;
87
mix = chip->mixer_data;
88
if (!mix)
89
return -ENODEV;
90
ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0;
91
return 0;
92
}
93
94
static int daca_put_deemphasis(struct snd_kcontrol *kcontrol,
95
struct snd_ctl_elem_value *ucontrol)
96
{
97
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
98
struct pmac_daca *mix;
99
int change;
100
101
mix = chip->mixer_data;
102
if (!mix)
103
return -ENODEV;
104
change = mix->deemphasis != ucontrol->value.integer.value[0];
105
if (change) {
106
mix->deemphasis = !!ucontrol->value.integer.value[0];
107
daca_set_volume(mix);
108
}
109
return change;
110
}
111
112
/* output volume */
113
static int daca_info_volume(struct snd_kcontrol *kcontrol,
114
struct snd_ctl_elem_info *uinfo)
115
{
116
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
117
uinfo->count = 2;
118
uinfo->value.integer.min = 0;
119
uinfo->value.integer.max = DACA_VOL_MAX;
120
return 0;
121
}
122
123
static int daca_get_volume(struct snd_kcontrol *kcontrol,
124
struct snd_ctl_elem_value *ucontrol)
125
{
126
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
127
struct pmac_daca *mix;
128
mix = chip->mixer_data;
129
if (!mix)
130
return -ENODEV;
131
ucontrol->value.integer.value[0] = mix->left_vol;
132
ucontrol->value.integer.value[1] = mix->right_vol;
133
return 0;
134
}
135
136
static int daca_put_volume(struct snd_kcontrol *kcontrol,
137
struct snd_ctl_elem_value *ucontrol)
138
{
139
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
140
struct pmac_daca *mix;
141
unsigned int vol[2];
142
int change;
143
144
mix = chip->mixer_data;
145
if (!mix)
146
return -ENODEV;
147
vol[0] = ucontrol->value.integer.value[0];
148
vol[1] = ucontrol->value.integer.value[1];
149
if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX)
150
return -EINVAL;
151
change = mix->left_vol != vol[0] ||
152
mix->right_vol != vol[1];
153
if (change) {
154
mix->left_vol = vol[0];
155
mix->right_vol = vol[1];
156
daca_set_volume(mix);
157
}
158
return change;
159
}
160
161
/* amplifier switch */
162
#define daca_info_amp daca_info_deemphasis
163
164
static int daca_get_amp(struct snd_kcontrol *kcontrol,
165
struct snd_ctl_elem_value *ucontrol)
166
{
167
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
168
struct pmac_daca *mix;
169
mix = chip->mixer_data;
170
if (!mix)
171
return -ENODEV;
172
ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0;
173
return 0;
174
}
175
176
static int daca_put_amp(struct snd_kcontrol *kcontrol,
177
struct snd_ctl_elem_value *ucontrol)
178
{
179
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
180
struct pmac_daca *mix;
181
int change;
182
183
mix = chip->mixer_data;
184
if (!mix)
185
return -ENODEV;
186
change = mix->amp_on != ucontrol->value.integer.value[0];
187
if (change) {
188
mix->amp_on = !!ucontrol->value.integer.value[0];
189
i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
190
mix->amp_on ? 0x05 : 0x04);
191
}
192
return change;
193
}
194
195
static const struct snd_kcontrol_new daca_mixers[] = {
196
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
197
.name = "Deemphasis Switch",
198
.info = daca_info_deemphasis,
199
.get = daca_get_deemphasis,
200
.put = daca_put_deemphasis
201
},
202
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
203
.name = "Master Playback Volume",
204
.info = daca_info_volume,
205
.get = daca_get_volume,
206
.put = daca_put_volume
207
},
208
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
209
.name = "Power Amplifier Switch",
210
.info = daca_info_amp,
211
.get = daca_get_amp,
212
.put = daca_put_amp
213
},
214
};
215
216
217
#ifdef CONFIG_PM
218
static void daca_resume(struct snd_pmac *chip)
219
{
220
struct pmac_daca *mix = chip->mixer_data;
221
i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_SR, 0x08);
222
i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
223
mix->amp_on ? 0x05 : 0x04);
224
daca_set_volume(mix);
225
}
226
#endif /* CONFIG_PM */
227
228
229
static void daca_cleanup(struct snd_pmac *chip)
230
{
231
struct pmac_daca *mix = chip->mixer_data;
232
if (! mix)
233
return;
234
snd_pmac_keywest_cleanup(&mix->i2c);
235
kfree(mix);
236
chip->mixer_data = NULL;
237
}
238
239
/* exported */
240
int snd_pmac_daca_init(struct snd_pmac *chip)
241
{
242
int i, err;
243
struct pmac_daca *mix;
244
245
request_module("i2c-powermac");
246
247
mix = kzalloc(sizeof(*mix), GFP_KERNEL);
248
if (! mix)
249
return -ENOMEM;
250
chip->mixer_data = mix;
251
chip->mixer_free = daca_cleanup;
252
mix->amp_on = 1; /* default on */
253
254
mix->i2c.addr = DACA_I2C_ADDR;
255
mix->i2c.init_client = daca_init_client;
256
mix->i2c.name = "DACA";
257
err = snd_pmac_keywest_init(&mix->i2c);
258
if (err < 0)
259
return err;
260
261
/*
262
* build mixers
263
*/
264
strscpy(chip->card->mixername, "PowerMac DACA");
265
266
for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) {
267
err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip));
268
if (err < 0)
269
return err;
270
}
271
272
#ifdef CONFIG_PM
273
chip->resume = daca_resume;
274
#endif
275
276
return 0;
277
}
278
279