Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *   ALSA Driver for the PT2258 volume controller.
0004  *
0005  *  Copyright (c) 2006  Jochen Voss <voss@seehuhn.de>
0006  */      
0007 
0008 #include <sound/core.h>
0009 #include <sound/control.h>
0010 #include <sound/tlv.h>
0011 #include <sound/i2c.h>
0012 #include <sound/pt2258.h>
0013 #include <linux/module.h>
0014 
0015 MODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>");
0016 MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)");
0017 MODULE_LICENSE("GPL");
0018 
0019 #define PT2258_CMD_RESET 0xc0
0020 #define PT2258_CMD_UNMUTE 0xf8
0021 #define PT2258_CMD_MUTE 0xf9
0022 
0023 static const unsigned char pt2258_channel_code[12] = {
0024     0x80, 0x90,     /* channel 1: -10dB, -1dB */
0025     0x40, 0x50,     /* channel 2: -10dB, -1dB */
0026     0x00, 0x10,     /* channel 3: -10dB, -1dB */
0027     0x20, 0x30,     /* channel 4: -10dB, -1dB */
0028     0x60, 0x70,     /* channel 5: -10dB, -1dB */
0029     0xa0, 0xb0      /* channel 6: -10dB, -1dB */
0030 };
0031 
0032 int snd_pt2258_reset(struct snd_pt2258 *pt)
0033 {
0034     unsigned char bytes[2];
0035     int i;
0036 
0037     /* reset chip */
0038     bytes[0] = PT2258_CMD_RESET;
0039     snd_i2c_lock(pt->i2c_bus);
0040     if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
0041         goto __error;
0042     snd_i2c_unlock(pt->i2c_bus);
0043 
0044     /* mute all channels */
0045     pt->mute = 1;
0046     bytes[0] = PT2258_CMD_MUTE;
0047     snd_i2c_lock(pt->i2c_bus);
0048     if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
0049         goto __error;
0050     snd_i2c_unlock(pt->i2c_bus);
0051 
0052     /* set all channels to 0dB */
0053     for (i = 0; i < 6; ++i)
0054         pt->volume[i] = 0;
0055     bytes[0] = 0xd0;
0056     bytes[1] = 0xe0;
0057     snd_i2c_lock(pt->i2c_bus);
0058     if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
0059         goto __error;
0060     snd_i2c_unlock(pt->i2c_bus);
0061 
0062     return 0;
0063 
0064       __error:
0065     snd_i2c_unlock(pt->i2c_bus);
0066     snd_printk(KERN_ERR "PT2258 reset failed\n");
0067     return -EIO;
0068 }
0069 
0070 static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol,
0071                      struct snd_ctl_elem_info *uinfo)
0072 {
0073     uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
0074     uinfo->count = 2;
0075     uinfo->value.integer.min = 0;
0076     uinfo->value.integer.max = 79;
0077     return 0;
0078 }
0079 
0080 static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
0081                     struct snd_ctl_elem_value *ucontrol)
0082 {
0083     struct snd_pt2258 *pt = kcontrol->private_data;
0084     int base = kcontrol->private_value;
0085 
0086     /* chip does not support register reads */
0087     ucontrol->value.integer.value[0] = 79 - pt->volume[base];
0088     ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1];
0089     return 0;
0090 }
0091 
0092 static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
0093                     struct snd_ctl_elem_value *ucontrol)
0094 {
0095     struct snd_pt2258 *pt = kcontrol->private_data;
0096     int base = kcontrol->private_value;
0097     unsigned char bytes[2];
0098     int val0, val1;
0099 
0100     val0 = 79 - ucontrol->value.integer.value[0];
0101     val1 = 79 - ucontrol->value.integer.value[1];
0102     if (val0 < 0 || val0 > 79 || val1 < 0 || val1 > 79)
0103         return -EINVAL;
0104     if (val0 == pt->volume[base] && val1 == pt->volume[base + 1])
0105         return 0;
0106 
0107     pt->volume[base] = val0;
0108     bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10);
0109     bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10);
0110     snd_i2c_lock(pt->i2c_bus);
0111     if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
0112         goto __error;
0113     snd_i2c_unlock(pt->i2c_bus);
0114 
0115     pt->volume[base + 1] = val1;
0116     bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10);
0117     bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10);
0118     snd_i2c_lock(pt->i2c_bus);
0119     if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
0120         goto __error;
0121     snd_i2c_unlock(pt->i2c_bus);
0122 
0123     return 1;
0124 
0125       __error:
0126     snd_i2c_unlock(pt->i2c_bus);
0127     snd_printk(KERN_ERR "PT2258 access failed\n");
0128     return -EIO;
0129 }
0130 
0131 #define pt2258_switch_info  snd_ctl_boolean_mono_info
0132 
0133 static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
0134                  struct snd_ctl_elem_value *ucontrol)
0135 {
0136     struct snd_pt2258 *pt = kcontrol->private_data;
0137 
0138     ucontrol->value.integer.value[0] = !pt->mute;
0139     return 0;
0140 }
0141 
0142 static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
0143                  struct snd_ctl_elem_value *ucontrol)
0144 {
0145     struct snd_pt2258 *pt = kcontrol->private_data;
0146     unsigned char bytes[2];
0147     int val;
0148 
0149     val = !ucontrol->value.integer.value[0];
0150     if (pt->mute == val)
0151         return 0;
0152 
0153     pt->mute = val;
0154     bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE;
0155     snd_i2c_lock(pt->i2c_bus);
0156     if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
0157         goto __error;
0158     snd_i2c_unlock(pt->i2c_bus);
0159 
0160     return 1;
0161 
0162       __error:
0163     snd_i2c_unlock(pt->i2c_bus);
0164     snd_printk(KERN_ERR "PT2258 access failed 2\n");
0165     return -EIO;
0166 }
0167 
0168 static const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0);
0169 
0170 int snd_pt2258_build_controls(struct snd_pt2258 *pt)
0171 {
0172     struct snd_kcontrol_new knew;
0173     char *names[3] = {
0174         "Mic Loopback Playback Volume",
0175         "Line Loopback Playback Volume",
0176         "CD Loopback Playback Volume"
0177     };
0178     int i, err;
0179 
0180     for (i = 0; i < 3; ++i) {
0181         memset(&knew, 0, sizeof(knew));
0182         knew.name = names[i];
0183         knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0184         knew.count = 1;
0185         knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
0186             SNDRV_CTL_ELEM_ACCESS_TLV_READ;
0187         knew.private_value = 2 * i;
0188         knew.info = pt2258_stereo_volume_info;
0189         knew.get = pt2258_stereo_volume_get;
0190         knew.put = pt2258_stereo_volume_put;
0191         knew.tlv.p = pt2258_db_scale;
0192 
0193         err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
0194         if (err < 0)
0195             return err;
0196     }
0197 
0198     memset(&knew, 0, sizeof(knew));
0199     knew.name = "Loopback Switch";
0200     knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0201     knew.info = pt2258_switch_info;
0202     knew.get = pt2258_switch_get;
0203     knew.put = pt2258_switch_put;
0204     knew.access = 0;
0205     err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
0206     if (err < 0)
0207         return err;
0208 
0209     return 0;
0210 }
0211 
0212 EXPORT_SYMBOL(snd_pt2258_reset);
0213 EXPORT_SYMBOL(snd_pt2258_build_controls);