diff options
Diffstat (limited to 'sound/i2c/other/pt2258.c')
-rw-r--r-- | sound/i2c/other/pt2258.c | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c new file mode 100644 index 000000000000..e91cc3b44de5 --- /dev/null +++ b/sound/i2c/other/pt2258.c | |||
@@ -0,0 +1,233 @@ | |||
1 | /* | ||
2 | * ALSA Driver for the PT2258 volume controller. | ||
3 | * | ||
4 | * Copyright (c) 2006 Jochen Voss <voss@seehuhn.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/control.h> | ||
25 | #include <sound/tlv.h> | ||
26 | #include <sound/i2c.h> | ||
27 | #include <sound/pt2258.h> | ||
28 | |||
29 | MODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>"); | ||
30 | MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)"); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | |||
33 | #define PT2258_CMD_RESET 0xc0 | ||
34 | #define PT2258_CMD_UNMUTE 0xf8 | ||
35 | #define PT2258_CMD_MUTE 0xf9 | ||
36 | |||
37 | static const unsigned char pt2258_channel_code[12] = { | ||
38 | 0x80, 0x90, /* channel 1: -10dB, -1dB */ | ||
39 | 0x40, 0x50, /* channel 2: -10dB, -1dB */ | ||
40 | 0x00, 0x10, /* channel 3: -10dB, -1dB */ | ||
41 | 0x20, 0x30, /* channel 4: -10dB, -1dB */ | ||
42 | 0x60, 0x70, /* channel 5: -10dB, -1dB */ | ||
43 | 0xa0, 0xb0 /* channel 6: -10dB, -1dB */ | ||
44 | }; | ||
45 | |||
46 | int snd_pt2258_reset(struct snd_pt2258 *pt) | ||
47 | { | ||
48 | unsigned char bytes[2]; | ||
49 | int i; | ||
50 | |||
51 | /* reset chip */ | ||
52 | bytes[0] = PT2258_CMD_RESET; | ||
53 | snd_i2c_lock(pt->i2c_bus); | ||
54 | if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) | ||
55 | goto __error; | ||
56 | snd_i2c_unlock(pt->i2c_bus); | ||
57 | |||
58 | /* mute all channels */ | ||
59 | pt->mute = 1; | ||
60 | bytes[0] = PT2258_CMD_MUTE; | ||
61 | snd_i2c_lock(pt->i2c_bus); | ||
62 | if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) | ||
63 | goto __error; | ||
64 | snd_i2c_unlock(pt->i2c_bus); | ||
65 | |||
66 | /* set all channels to 0dB */ | ||
67 | for (i = 0; i < 6; ++i) | ||
68 | pt->volume[i] = 0; | ||
69 | bytes[0] = 0xd0; | ||
70 | bytes[1] = 0xe0; | ||
71 | snd_i2c_lock(pt->i2c_bus); | ||
72 | if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) | ||
73 | goto __error; | ||
74 | snd_i2c_unlock(pt->i2c_bus); | ||
75 | |||
76 | return 0; | ||
77 | |||
78 | __error: | ||
79 | snd_i2c_unlock(pt->i2c_bus); | ||
80 | snd_printk(KERN_ERR "PT2258 reset failed\n"); | ||
81 | return -EIO; | ||
82 | } | ||
83 | |||
84 | static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol, | ||
85 | struct snd_ctl_elem_info *uinfo) | ||
86 | { | ||
87 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
88 | uinfo->count = 2; | ||
89 | uinfo->value.integer.min = 0; | ||
90 | uinfo->value.integer.max = 79; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol, | ||
95 | struct snd_ctl_elem_value *ucontrol) | ||
96 | { | ||
97 | struct snd_pt2258 *pt = kcontrol->private_data; | ||
98 | int base = kcontrol->private_value; | ||
99 | |||
100 | /* chip does not support register reads */ | ||
101 | ucontrol->value.integer.value[0] = 79 - pt->volume[base]; | ||
102 | ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1]; | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol, | ||
107 | struct snd_ctl_elem_value *ucontrol) | ||
108 | { | ||
109 | struct snd_pt2258 *pt = kcontrol->private_data; | ||
110 | int base = kcontrol->private_value; | ||
111 | unsigned char bytes[2]; | ||
112 | int val0, val1; | ||
113 | |||
114 | val0 = 79 - ucontrol->value.integer.value[0]; | ||
115 | val1 = 79 - ucontrol->value.integer.value[1]; | ||
116 | if (val0 == pt->volume[base] && val1 == pt->volume[base + 1]) | ||
117 | return 0; | ||
118 | |||
119 | pt->volume[base] = val0; | ||
120 | bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10); | ||
121 | bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10); | ||
122 | snd_i2c_lock(pt->i2c_bus); | ||
123 | if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) | ||
124 | goto __error; | ||
125 | snd_i2c_unlock(pt->i2c_bus); | ||
126 | |||
127 | pt->volume[base + 1] = val1; | ||
128 | bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10); | ||
129 | bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10); | ||
130 | snd_i2c_lock(pt->i2c_bus); | ||
131 | if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) | ||
132 | goto __error; | ||
133 | snd_i2c_unlock(pt->i2c_bus); | ||
134 | |||
135 | return 1; | ||
136 | |||
137 | __error: | ||
138 | snd_i2c_unlock(pt->i2c_bus); | ||
139 | snd_printk(KERN_ERR "PT2258 access failed\n"); | ||
140 | return -EIO; | ||
141 | } | ||
142 | |||
143 | static int pt2258_switch_info(struct snd_kcontrol *kcontrol, | ||
144 | struct snd_ctl_elem_info *uinfo) | ||
145 | { | ||
146 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
147 | uinfo->count = 1; | ||
148 | uinfo->value.integer.min = 0; | ||
149 | uinfo->value.integer.max = 1; | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int pt2258_switch_get(struct snd_kcontrol *kcontrol, | ||
154 | struct snd_ctl_elem_value *ucontrol) | ||
155 | { | ||
156 | struct snd_pt2258 *pt = kcontrol->private_data; | ||
157 | |||
158 | ucontrol->value.integer.value[0] = !pt->mute; | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int pt2258_switch_put(struct snd_kcontrol *kcontrol, | ||
163 | struct snd_ctl_elem_value *ucontrol) | ||
164 | { | ||
165 | struct snd_pt2258 *pt = kcontrol->private_data; | ||
166 | unsigned char bytes[2]; | ||
167 | int val; | ||
168 | |||
169 | val = !ucontrol->value.integer.value[0]; | ||
170 | if (pt->mute == val) | ||
171 | return 0; | ||
172 | |||
173 | pt->mute = val; | ||
174 | bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE; | ||
175 | snd_i2c_lock(pt->i2c_bus); | ||
176 | if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) | ||
177 | goto __error; | ||
178 | snd_i2c_unlock(pt->i2c_bus); | ||
179 | |||
180 | return 1; | ||
181 | |||
182 | __error: | ||
183 | snd_i2c_unlock(pt->i2c_bus); | ||
184 | snd_printk(KERN_ERR "PT2258 access failed 2\n"); | ||
185 | return -EIO; | ||
186 | } | ||
187 | |||
188 | static const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0); | ||
189 | |||
190 | int snd_pt2258_build_controls(struct snd_pt2258 *pt) | ||
191 | { | ||
192 | struct snd_kcontrol_new knew; | ||
193 | char *names[3] = { | ||
194 | "Mic Loopback Playback Volume", | ||
195 | "Line Loopback Playback Volume", | ||
196 | "CD Loopback Playback Volume" | ||
197 | }; | ||
198 | int i, err; | ||
199 | |||
200 | for (i = 0; i < 3; ++i) { | ||
201 | memset(&knew, 0, sizeof(knew)); | ||
202 | knew.name = names[i]; | ||
203 | knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
204 | knew.count = 1; | ||
205 | knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
206 | SNDRV_CTL_ELEM_ACCESS_TLV_READ; | ||
207 | knew.private_value = 2 * i; | ||
208 | knew.info = pt2258_stereo_volume_info; | ||
209 | knew.get = pt2258_stereo_volume_get; | ||
210 | knew.put = pt2258_stereo_volume_put; | ||
211 | knew.tlv.p = pt2258_db_scale; | ||
212 | |||
213 | err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt)); | ||
214 | if (err < 0) | ||
215 | return err; | ||
216 | } | ||
217 | |||
218 | memset(&knew, 0, sizeof(knew)); | ||
219 | knew.name = "Loopback Switch"; | ||
220 | knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
221 | knew.info = pt2258_switch_info; | ||
222 | knew.get = pt2258_switch_get; | ||
223 | knew.put = pt2258_switch_put; | ||
224 | knew.access = 0; | ||
225 | err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt)); | ||
226 | if (err < 0) | ||
227 | return err; | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | EXPORT_SYMBOL(snd_pt2258_reset); | ||
233 | EXPORT_SYMBOL(snd_pt2258_build_controls); | ||