18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA Driver for the PT2258 volume controller. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2006 Jochen Voss <voss@seehuhn.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <sound/core.h> 98c2ecf20Sopenharmony_ci#include <sound/control.h> 108c2ecf20Sopenharmony_ci#include <sound/tlv.h> 118c2ecf20Sopenharmony_ci#include <sound/i2c.h> 128c2ecf20Sopenharmony_ci#include <sound/pt2258.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>"); 168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)"); 178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define PT2258_CMD_RESET 0xc0 208c2ecf20Sopenharmony_ci#define PT2258_CMD_UNMUTE 0xf8 218c2ecf20Sopenharmony_ci#define PT2258_CMD_MUTE 0xf9 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const unsigned char pt2258_channel_code[12] = { 248c2ecf20Sopenharmony_ci 0x80, 0x90, /* channel 1: -10dB, -1dB */ 258c2ecf20Sopenharmony_ci 0x40, 0x50, /* channel 2: -10dB, -1dB */ 268c2ecf20Sopenharmony_ci 0x00, 0x10, /* channel 3: -10dB, -1dB */ 278c2ecf20Sopenharmony_ci 0x20, 0x30, /* channel 4: -10dB, -1dB */ 288c2ecf20Sopenharmony_ci 0x60, 0x70, /* channel 5: -10dB, -1dB */ 298c2ecf20Sopenharmony_ci 0xa0, 0xb0 /* channel 6: -10dB, -1dB */ 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ciint snd_pt2258_reset(struct snd_pt2258 *pt) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci unsigned char bytes[2]; 358c2ecf20Sopenharmony_ci int i; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* reset chip */ 388c2ecf20Sopenharmony_ci bytes[0] = PT2258_CMD_RESET; 398c2ecf20Sopenharmony_ci snd_i2c_lock(pt->i2c_bus); 408c2ecf20Sopenharmony_ci if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) 418c2ecf20Sopenharmony_ci goto __error; 428c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* mute all channels */ 458c2ecf20Sopenharmony_ci pt->mute = 1; 468c2ecf20Sopenharmony_ci bytes[0] = PT2258_CMD_MUTE; 478c2ecf20Sopenharmony_ci snd_i2c_lock(pt->i2c_bus); 488c2ecf20Sopenharmony_ci if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) 498c2ecf20Sopenharmony_ci goto __error; 508c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* set all channels to 0dB */ 538c2ecf20Sopenharmony_ci for (i = 0; i < 6; ++i) 548c2ecf20Sopenharmony_ci pt->volume[i] = 0; 558c2ecf20Sopenharmony_ci bytes[0] = 0xd0; 568c2ecf20Sopenharmony_ci bytes[1] = 0xe0; 578c2ecf20Sopenharmony_ci snd_i2c_lock(pt->i2c_bus); 588c2ecf20Sopenharmony_ci if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) 598c2ecf20Sopenharmony_ci goto __error; 608c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci __error: 658c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 668c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "PT2258 reset failed\n"); 678c2ecf20Sopenharmony_ci return -EIO; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol, 718c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 748c2ecf20Sopenharmony_ci uinfo->count = 2; 758c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 768c2ecf20Sopenharmony_ci uinfo->value.integer.max = 79; 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol, 818c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct snd_pt2258 *pt = kcontrol->private_data; 848c2ecf20Sopenharmony_ci int base = kcontrol->private_value; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* chip does not support register reads */ 878c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 79 - pt->volume[base]; 888c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1]; 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol, 938c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct snd_pt2258 *pt = kcontrol->private_data; 968c2ecf20Sopenharmony_ci int base = kcontrol->private_value; 978c2ecf20Sopenharmony_ci unsigned char bytes[2]; 988c2ecf20Sopenharmony_ci int val0, val1; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci val0 = 79 - ucontrol->value.integer.value[0]; 1018c2ecf20Sopenharmony_ci val1 = 79 - ucontrol->value.integer.value[1]; 1028c2ecf20Sopenharmony_ci if (val0 < 0 || val0 > 79 || val1 < 0 || val1 > 79) 1038c2ecf20Sopenharmony_ci return -EINVAL; 1048c2ecf20Sopenharmony_ci if (val0 == pt->volume[base] && val1 == pt->volume[base + 1]) 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pt->volume[base] = val0; 1088c2ecf20Sopenharmony_ci bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10); 1098c2ecf20Sopenharmony_ci bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10); 1108c2ecf20Sopenharmony_ci snd_i2c_lock(pt->i2c_bus); 1118c2ecf20Sopenharmony_ci if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) 1128c2ecf20Sopenharmony_ci goto __error; 1138c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci pt->volume[base + 1] = val1; 1168c2ecf20Sopenharmony_ci bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10); 1178c2ecf20Sopenharmony_ci bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10); 1188c2ecf20Sopenharmony_ci snd_i2c_lock(pt->i2c_bus); 1198c2ecf20Sopenharmony_ci if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2) 1208c2ecf20Sopenharmony_ci goto __error; 1218c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 1; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci __error: 1268c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 1278c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "PT2258 access failed\n"); 1288c2ecf20Sopenharmony_ci return -EIO; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define pt2258_switch_info snd_ctl_boolean_mono_info 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int pt2258_switch_get(struct snd_kcontrol *kcontrol, 1348c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct snd_pt2258 *pt = kcontrol->private_data; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = !pt->mute; 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int pt2258_switch_put(struct snd_kcontrol *kcontrol, 1438c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct snd_pt2258 *pt = kcontrol->private_data; 1468c2ecf20Sopenharmony_ci unsigned char bytes[2]; 1478c2ecf20Sopenharmony_ci int val; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci val = !ucontrol->value.integer.value[0]; 1508c2ecf20Sopenharmony_ci if (pt->mute == val) 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci pt->mute = val; 1548c2ecf20Sopenharmony_ci bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE; 1558c2ecf20Sopenharmony_ci snd_i2c_lock(pt->i2c_bus); 1568c2ecf20Sopenharmony_ci if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1) 1578c2ecf20Sopenharmony_ci goto __error; 1588c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 1; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci __error: 1638c2ecf20Sopenharmony_ci snd_i2c_unlock(pt->i2c_bus); 1648c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "PT2258 access failed 2\n"); 1658c2ecf20Sopenharmony_ci return -EIO; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciint snd_pt2258_build_controls(struct snd_pt2258 *pt) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct snd_kcontrol_new knew; 1738c2ecf20Sopenharmony_ci char *names[3] = { 1748c2ecf20Sopenharmony_ci "Mic Loopback Playback Volume", 1758c2ecf20Sopenharmony_ci "Line Loopback Playback Volume", 1768c2ecf20Sopenharmony_ci "CD Loopback Playback Volume" 1778c2ecf20Sopenharmony_ci }; 1788c2ecf20Sopenharmony_ci int i, err; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci for (i = 0; i < 3; ++i) { 1818c2ecf20Sopenharmony_ci memset(&knew, 0, sizeof(knew)); 1828c2ecf20Sopenharmony_ci knew.name = names[i]; 1838c2ecf20Sopenharmony_ci knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1848c2ecf20Sopenharmony_ci knew.count = 1; 1858c2ecf20Sopenharmony_ci knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 1868c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ; 1878c2ecf20Sopenharmony_ci knew.private_value = 2 * i; 1888c2ecf20Sopenharmony_ci knew.info = pt2258_stereo_volume_info; 1898c2ecf20Sopenharmony_ci knew.get = pt2258_stereo_volume_get; 1908c2ecf20Sopenharmony_ci knew.put = pt2258_stereo_volume_put; 1918c2ecf20Sopenharmony_ci knew.tlv.p = pt2258_db_scale; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt)); 1948c2ecf20Sopenharmony_ci if (err < 0) 1958c2ecf20Sopenharmony_ci return err; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci memset(&knew, 0, sizeof(knew)); 1998c2ecf20Sopenharmony_ci knew.name = "Loopback Switch"; 2008c2ecf20Sopenharmony_ci knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 2018c2ecf20Sopenharmony_ci knew.info = pt2258_switch_info; 2028c2ecf20Sopenharmony_ci knew.get = pt2258_switch_get; 2038c2ecf20Sopenharmony_ci knew.put = pt2258_switch_put; 2048c2ecf20Sopenharmony_ci knew.access = 0; 2058c2ecf20Sopenharmony_ci err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt)); 2068c2ecf20Sopenharmony_ci if (err < 0) 2078c2ecf20Sopenharmony_ci return err; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pt2258_reset); 2138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pt2258_build_controls); 214