Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ALSA] Enable the analog loopback of the Revolution 5.1

Enable the analog loopback of the Revolution 5.1 card.
This patch adds support for the PT2258 volume controller and modifies
the Revolution 5.1 driver to make use of this facility. This allows
to control the analog loopback of the card.

Signed-off-by: Jochen Voss <voss@seehuhn.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>

authored by

Jochen Voss and committed by
Jaroslav Kysela
feaa6a74 a58e7cb1

+413 -14
+37
include/sound/pt2258.h
··· 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 + #ifndef __SOUND_PT2258_H 23 + #define __SOUND_PT2258_H 24 + 25 + struct snd_pt2258 { 26 + struct snd_card *card; 27 + struct snd_i2c_bus *i2c_bus; 28 + struct snd_i2c_device *i2c_dev; 29 + 30 + unsigned char volume[6]; 31 + int mute; 32 + }; 33 + 34 + extern int snd_pt2258_reset(struct snd_pt2258 *pt); 35 + extern int snd_pt2258_build_controls(struct snd_pt2258 *pt); 36 + 37 + #endif /* __SOUND_PT2258_H */
+1
sound/i2c/Makefile
··· 16 16 # Toplevel Module Dependency 17 17 obj-$(CONFIG_SND_INTERWAVE_STB) += snd-tea6330t.o snd-i2c.o 18 18 obj-$(CONFIG_SND_ICE1712) += snd-cs8427.o snd-i2c.o 19 + obj-$(CONFIG_SND_ICE1724) += snd-i2c.o
+2 -2
sound/i2c/other/Makefile
··· 6 6 snd-ak4114-objs := ak4114.o 7 7 snd-ak4117-objs := ak4117.o 8 8 snd-ak4xxx-adda-objs := ak4xxx-adda.o 9 + snd-pt2258-objs := pt2258.o 9 10 snd-tea575x-tuner-objs := tea575x-tuner.o 10 11 11 12 # Module Dependency 12 13 obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o 13 14 obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o 14 - obj-$(CONFIG_SND_ICE1724) += snd-ak4xxx-adda.o 15 - obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o 15 + obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o 16 16 obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
+233
sound/i2c/other/pt2258.c
··· 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 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);
+14
sound/pci/ice1712/ice1712.h
··· 28 28 #include <sound/i2c.h> 29 29 #include <sound/ak4xxx-adda.h> 30 30 #include <sound/ak4114.h> 31 + #include <sound/pt2258.h> 31 32 #include <sound/pcm.h> 32 33 #include <sound/mpu401.h> 33 34 ··· 382 381 unsigned short master[2]; 383 382 unsigned short vol[8]; 384 383 } phase28; 384 + /* a non-standard I2C device for revo51 */ 385 + struct revo51_spec { 386 + struct snd_i2c_device *dev; 387 + struct snd_pt2258 *pt2258; 388 + } revo51; 385 389 /* Hoontech-specific setting */ 386 390 struct hoontech_spec { 387 391 unsigned char boxbits[4]; ··· 466 460 snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); 467 461 snd_ice1712_gpio_set_mask(ice, ~mask); 468 462 snd_ice1712_gpio_write(ice, mask & bits); 463 + } 464 + 465 + static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice, 466 + unsigned int mask) 467 + { 468 + ice->gpio.direction &= ~mask; 469 + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); 470 + return (snd_ice1712_gpio_read(ice) & mask); 469 471 } 470 472 471 473 int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
+122 -10
sound/pci/ice1712/revo.c
··· 84 84 } 85 85 86 86 /* 87 + * I2C access to the PT2258 volume controller on GPIO 6/7 (Revolution 5.1) 88 + */ 89 + 90 + static void revo_i2c_start(struct snd_i2c_bus *bus) 91 + { 92 + struct snd_ice1712 *ice = bus->private_data; 93 + snd_ice1712_save_gpio_status(ice); 94 + } 95 + 96 + static void revo_i2c_stop(struct snd_i2c_bus *bus) 97 + { 98 + struct snd_ice1712 *ice = bus->private_data; 99 + snd_ice1712_restore_gpio_status(ice); 100 + } 101 + 102 + static void revo_i2c_direction(struct snd_i2c_bus *bus, int clock, int data) 103 + { 104 + struct snd_ice1712 *ice = bus->private_data; 105 + unsigned int mask, val; 106 + 107 + val = 0; 108 + if (clock) 109 + val |= VT1724_REVO_I2C_CLOCK; /* write SCL */ 110 + if (data) 111 + val |= VT1724_REVO_I2C_DATA; /* write SDA */ 112 + mask = VT1724_REVO_I2C_CLOCK | VT1724_REVO_I2C_DATA; 113 + ice->gpio.direction &= ~mask; 114 + ice->gpio.direction |= val; 115 + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); 116 + snd_ice1712_gpio_set_mask(ice, ~mask); 117 + } 118 + 119 + static void revo_i2c_setlines(struct snd_i2c_bus *bus, int clk, int data) 120 + { 121 + struct snd_ice1712 *ice = bus->private_data; 122 + unsigned int val = 0; 123 + 124 + if (clk) 125 + val |= VT1724_REVO_I2C_CLOCK; 126 + if (data) 127 + val |= VT1724_REVO_I2C_DATA; 128 + snd_ice1712_gpio_write_bits(ice, 129 + VT1724_REVO_I2C_DATA | 130 + VT1724_REVO_I2C_CLOCK, val); 131 + udelay(5); 132 + } 133 + 134 + static int revo_i2c_getdata(struct snd_i2c_bus *bus, int ack) 135 + { 136 + struct snd_ice1712 *ice = bus->private_data; 137 + int bit; 138 + 139 + if (ack) 140 + udelay(5); 141 + bit = snd_ice1712_gpio_read_bits(ice, VT1724_REVO_I2C_DATA) ? 1 : 0; 142 + return bit; 143 + } 144 + 145 + static struct snd_i2c_bit_ops revo51_bit_ops = { 146 + .start = revo_i2c_start, 147 + .stop = revo_i2c_stop, 148 + .direction = revo_i2c_direction, 149 + .setlines = revo_i2c_setlines, 150 + .getdata = revo_i2c_getdata, 151 + }; 152 + 153 + static int revo51_i2c_init(struct snd_ice1712 *ice, 154 + struct snd_pt2258 *pt) 155 + { 156 + int err; 157 + 158 + /* create the I2C bus */ 159 + err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c); 160 + if (err < 0) 161 + return err; 162 + 163 + ice->i2c->private_data = ice; 164 + ice->i2c->hw_ops.bit = &revo51_bit_ops; 165 + 166 + /* create the I2C device */ 167 + err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, 168 + &ice->spec.revo51.dev); 169 + if (err < 0) 170 + return err; 171 + 172 + pt->card = ice->card; 173 + pt->i2c_bus = ice->i2c; 174 + pt->i2c_dev = ice->spec.revo51.dev; 175 + ice->spec.revo51.pt2258 = pt; 176 + 177 + snd_pt2258_reset(pt); 178 + 179 + return 0; 180 + } 181 + 182 + /* 87 183 * initialize the chips on M-Audio Revolution cards 88 184 */ 89 185 ··· 276 180 .cif = 0, 277 181 .data_mask = VT1724_REVO_CDOUT, 278 182 .clk_mask = VT1724_REVO_CCLK, 279 - .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, 280 - .cs_addr = VT1724_REVO_CS1 | VT1724_REVO_CS2, 281 - .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, 183 + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1, 184 + .cs_addr = VT1724_REVO_CS1, 185 + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1, 282 186 .add_flags = VT1724_REVO_CCLK, /* high at init */ 283 187 .mask_flags = 0, 284 188 }; ··· 294 198 .cif = 0, 295 199 .data_mask = VT1724_REVO_CDOUT, 296 200 .clk_mask = VT1724_REVO_CCLK, 297 - .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, 298 - .cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2, 299 - .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2, 201 + .cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1, 202 + .cs_addr = VT1724_REVO_CS0, 203 + .cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1, 300 204 .add_flags = VT1724_REVO_CCLK, /* high at init */ 301 205 .mask_flags = 0, 302 206 }; 207 + 208 + static struct snd_pt2258 ptc_revo51_volume; 303 209 304 210 static int __devinit revo_init(struct snd_ice1712 *ice) 305 211 { ··· 341 243 break; 342 244 case VT1724_SUBDEVICE_REVOLUTION51: 343 245 ice->akm_codecs = 2; 344 - if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, &akm_revo51_priv, ice)) < 0) 246 + err = snd_ice1712_akm4xxx_init(ak, &akm_revo51, 247 + &akm_revo51_priv, ice); 248 + if (err < 0) 345 249 return err; 346 - err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo51_adc, 250 + err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo51_adc, 347 251 &akm_revo51_adc_priv, ice); 348 252 if (err < 0) 349 253 return err; 350 - /* unmute all codecs - needed! */ 351 - snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); 254 + err = revo51_i2c_init(ice, &ptc_revo51_volume); 255 + if (err < 0) 256 + return err; 257 + /* unmute all codecs */ 258 + snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, 259 + VT1724_REVO_MUTE); 352 260 break; 353 261 } 354 262 ··· 368 264 369 265 switch (ice->eeprom.subvendor) { 370 266 case VT1724_SUBDEVICE_REVOLUTION71: 267 + err = snd_ice1712_akm4xxx_build_controls(ice); 268 + if (err < 0) 269 + return err; 270 + break; 371 271 case VT1724_SUBDEVICE_REVOLUTION51: 372 272 err = snd_ice1712_akm4xxx_build_controls(ice); 373 273 if (err < 0) 374 274 return err; 275 + err = snd_pt2258_build_controls(ice->spec.revo51.pt2258); 276 + if (err < 0) 277 + return err; 278 + break; 375 279 } 376 280 return 0; 377 281 }
+4 -2
sound/pci/ice1712/revo.h
··· 42 42 #define VT1724_REVO_CCLK 0x02 43 43 #define VT1724_REVO_CDIN 0x04 /* not used */ 44 44 #define VT1724_REVO_CDOUT 0x08 45 - #define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for Rev. 5.1 */ 45 + #define VT1724_REVO_CS0 0x10 /* AK5365 chipselect for (revo51) */ 46 46 #define VT1724_REVO_CS1 0x20 /* front AKM4381 chipselect */ 47 - #define VT1724_REVO_CS2 0x40 /* surround AKM4355 chipselect */ 47 + #define VT1724_REVO_CS2 0x40 /* surround AKM4355 CS (revo71) */ 48 + #define VT1724_REVO_I2C_DATA 0x40 /* I2C: PT 2258 SDA (on revo51) */ 49 + #define VT1724_REVO_I2C_CLOCK 0x80 /* I2C: PT 2258 SCL (on revo51) */ 48 50 #define VT1724_REVO_MUTE (1<<22) /* 0 = all mute, 1 = normal operation */ 49 51 50 52 #endif /* __SOUND_REVO_H */