linux/sound/i2c/other/pt2258.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *   ALSA Driver for the PT2258 volume controller.
   4 *
   5 *      Copyright (c) 2006  Jochen Voss <voss@seehuhn.de>
   6 */      
   7
   8#include <sound/core.h>
   9#include <sound/control.h>
  10#include <sound/tlv.h>
  11#include <sound/i2c.h>
  12#include <sound/pt2258.h>
  13#include <linux/module.h>
  14
  15MODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>");
  16MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)");
  17MODULE_LICENSE("GPL");
  18
  19#define PT2258_CMD_RESET 0xc0
  20#define PT2258_CMD_UNMUTE 0xf8
  21#define PT2258_CMD_MUTE 0xf9
  22
  23static const unsigned char pt2258_channel_code[12] = {
  24        0x80, 0x90,             /* channel 1: -10dB, -1dB */
  25        0x40, 0x50,             /* channel 2: -10dB, -1dB */
  26        0x00, 0x10,             /* channel 3: -10dB, -1dB */
  27        0x20, 0x30,             /* channel 4: -10dB, -1dB */
  28        0x60, 0x70,             /* channel 5: -10dB, -1dB */
  29        0xa0, 0xb0              /* channel 6: -10dB, -1dB */
  30};
  31
  32int snd_pt2258_reset(struct snd_pt2258 *pt)
  33{
  34        unsigned char bytes[2];
  35        int i;
  36
  37        /* reset chip */
  38        bytes[0] = PT2258_CMD_RESET;
  39        snd_i2c_lock(pt->i2c_bus);
  40        if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
  41                goto __error;
  42        snd_i2c_unlock(pt->i2c_bus);
  43
  44        /* mute all channels */
  45        pt->mute = 1;
  46        bytes[0] = PT2258_CMD_MUTE;
  47        snd_i2c_lock(pt->i2c_bus);
  48        if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
  49                goto __error;
  50        snd_i2c_unlock(pt->i2c_bus);
  51
  52        /* set all channels to 0dB */
  53        for (i = 0; i < 6; ++i)
  54                pt->volume[i] = 0;
  55        bytes[0] = 0xd0;
  56        bytes[1] = 0xe0;
  57        snd_i2c_lock(pt->i2c_bus);
  58        if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
  59                goto __error;
  60        snd_i2c_unlock(pt->i2c_bus);
  61
  62        return 0;
  63
  64      __error:
  65        snd_i2c_unlock(pt->i2c_bus);
  66        snd_printk(KERN_ERR "PT2258 reset failed\n");
  67        return -EIO;
  68}
  69
  70static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol,
  71                                     struct snd_ctl_elem_info *uinfo)
  72{
  73        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  74        uinfo->count = 2;
  75        uinfo->value.integer.min = 0;
  76        uinfo->value.integer.max = 79;
  77        return 0;
  78}
  79
  80static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
  81                                    struct snd_ctl_elem_value *ucontrol)
  82{
  83        struct snd_pt2258 *pt = kcontrol->private_data;
  84        int base = kcontrol->private_value;
  85
  86        /* chip does not support register reads */
  87        ucontrol->value.integer.value[0] = 79 - pt->volume[base];
  88        ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1];
  89        return 0;
  90}
  91
  92static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
  93                                    struct snd_ctl_elem_value *ucontrol)
  94{
  95        struct snd_pt2258 *pt = kcontrol->private_data;
  96        int base = kcontrol->private_value;
  97        unsigned char bytes[2];
  98        int val0, val1;
  99
 100        val0 = 79 - ucontrol->value.integer.value[0];
 101        val1 = 79 - ucontrol->value.integer.value[1];
 102        if (val0 < 0 || val0 > 79 || val1 < 0 || val1 > 79)
 103                return -EINVAL;
 104        if (val0 == pt->volume[base] && val1 == pt->volume[base + 1])
 105                return 0;
 106
 107        pt->volume[base] = val0;
 108        bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10);
 109        bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10);
 110        snd_i2c_lock(pt->i2c_bus);
 111        if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
 112                goto __error;
 113        snd_i2c_unlock(pt->i2c_bus);
 114
 115        pt->volume[base + 1] = val1;
 116        bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10);
 117        bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10);
 118        snd_i2c_lock(pt->i2c_bus);
 119        if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
 120                goto __error;
 121        snd_i2c_unlock(pt->i2c_bus);
 122
 123        return 1;
 124
 125      __error:
 126        snd_i2c_unlock(pt->i2c_bus);
 127        snd_printk(KERN_ERR "PT2258 access failed\n");
 128        return -EIO;
 129}
 130
 131#define pt2258_switch_info      snd_ctl_boolean_mono_info
 132
 133static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
 134                             struct snd_ctl_elem_value *ucontrol)
 135{
 136        struct snd_pt2258 *pt = kcontrol->private_data;
 137
 138        ucontrol->value.integer.value[0] = !pt->mute;
 139        return 0;
 140}
 141
 142static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
 143                             struct snd_ctl_elem_value *ucontrol)
 144{
 145        struct snd_pt2258 *pt = kcontrol->private_data;
 146        unsigned char bytes[2];
 147        int val;
 148
 149        val = !ucontrol->value.integer.value[0];
 150        if (pt->mute == val)
 151                return 0;
 152
 153        pt->mute = val;
 154        bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE;
 155        snd_i2c_lock(pt->i2c_bus);
 156        if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
 157                goto __error;
 158        snd_i2c_unlock(pt->i2c_bus);
 159
 160        return 1;
 161
 162      __error:
 163        snd_i2c_unlock(pt->i2c_bus);
 164        snd_printk(KERN_ERR "PT2258 access failed 2\n");
 165        return -EIO;
 166}
 167
 168static const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0);
 169
 170int snd_pt2258_build_controls(struct snd_pt2258 *pt)
 171{
 172        struct snd_kcontrol_new knew;
 173        char *names[3] = {
 174                "Mic Loopback Playback Volume",
 175                "Line Loopback Playback Volume",
 176                "CD Loopback Playback Volume"
 177        };
 178        int i, err;
 179
 180        for (i = 0; i < 3; ++i) {
 181                memset(&knew, 0, sizeof(knew));
 182                knew.name = names[i];
 183                knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 184                knew.count = 1;
 185                knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 186                    SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 187                knew.private_value = 2 * i;
 188                knew.info = pt2258_stereo_volume_info;
 189                knew.get = pt2258_stereo_volume_get;
 190                knew.put = pt2258_stereo_volume_put;
 191                knew.tlv.p = pt2258_db_scale;
 192
 193                err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
 194                if (err < 0)
 195                        return err;
 196        }
 197
 198        memset(&knew, 0, sizeof(knew));
 199        knew.name = "Loopback Switch";
 200        knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 201        knew.info = pt2258_switch_info;
 202        knew.get = pt2258_switch_get;
 203        knew.put = pt2258_switch_put;
 204        knew.access = 0;
 205        err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
 206        if (err < 0)
 207                return err;
 208
 209        return 0;
 210}
 211
 212EXPORT_SYMBOL(snd_pt2258_reset);
 213EXPORT_SYMBOL(snd_pt2258_build_controls);
 214