linux/sound/ppc/daca.c
<<
>>
Prefs
   1/*
   2 * PMac DACA lowlevel functions
   3 *
   4 * Copyright (c) by Takashi Iwai <tiwai@suse.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 <linux/init.h>
  23#include <linux/i2c.h>
  24#include <linux/kmod.h>
  25#include <linux/slab.h>
  26#include <sound/core.h>
  27#include "pmac.h"
  28
  29/* i2c address */
  30#define DACA_I2C_ADDR   0x4d
  31
  32/* registers */
  33#define DACA_REG_SR     0x01
  34#define DACA_REG_AVOL   0x02
  35#define DACA_REG_GCFG   0x03
  36
  37/* maximum volume value */
  38#define DACA_VOL_MAX    0x38
  39
  40
  41struct pmac_daca {
  42        struct pmac_keywest i2c;
  43        int left_vol, right_vol;
  44        unsigned int deemphasis : 1;
  45        unsigned int amp_on : 1;
  46};
  47
  48
  49/*
  50 * initialize / detect DACA
  51 */
  52static int daca_init_client(struct pmac_keywest *i2c)
  53{
  54        unsigned short wdata = 0x00;
  55        /* SR: no swap, 1bit delay, 32-48kHz */
  56        /* GCFG: power amp inverted, DAC on */
  57        if (i2c_smbus_write_byte_data(i2c->client, DACA_REG_SR, 0x08) < 0 ||
  58            i2c_smbus_write_byte_data(i2c->client, DACA_REG_GCFG, 0x05) < 0)
  59                return -EINVAL;
  60        return i2c_smbus_write_block_data(i2c->client, DACA_REG_AVOL,
  61                                          2, (unsigned char*)&wdata);
  62}
  63
  64/*
  65 * update volume
  66 */
  67static int daca_set_volume(struct pmac_daca *mix)
  68{
  69        unsigned char data[2];
  70  
  71        if (! mix->i2c.client)
  72                return -ENODEV;
  73  
  74        if (mix->left_vol > DACA_VOL_MAX)
  75                data[0] = DACA_VOL_MAX;
  76        else
  77                data[0] = mix->left_vol;
  78        if (mix->right_vol > DACA_VOL_MAX)
  79                data[1] = DACA_VOL_MAX;
  80        else
  81                data[1] = mix->right_vol;
  82        data[1] |= mix->deemphasis ? 0x40 : 0;
  83        if (i2c_smbus_write_block_data(mix->i2c.client, DACA_REG_AVOL,
  84                                       2, data) < 0) {
  85                snd_printk(KERN_ERR "failed to set volume \n");
  86                return -EINVAL;
  87        }
  88        return 0;
  89}
  90
  91
  92/* deemphasis switch */
  93#define daca_info_deemphasis            snd_ctl_boolean_mono_info
  94
  95static int daca_get_deemphasis(struct snd_kcontrol *kcontrol,
  96                               struct snd_ctl_elem_value *ucontrol)
  97{
  98        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  99        struct pmac_daca *mix;
 100        if (! (mix = chip->mixer_data))
 101                return -ENODEV;
 102        ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0;
 103        return 0;
 104}
 105
 106static int daca_put_deemphasis(struct snd_kcontrol *kcontrol,
 107                               struct snd_ctl_elem_value *ucontrol)
 108{
 109        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 110        struct pmac_daca *mix;
 111        int change;
 112
 113        if (! (mix = chip->mixer_data))
 114                return -ENODEV;
 115        change = mix->deemphasis != ucontrol->value.integer.value[0];
 116        if (change) {
 117                mix->deemphasis = !!ucontrol->value.integer.value[0];
 118                daca_set_volume(mix);
 119        }
 120        return change;
 121}
 122
 123/* output volume */
 124static int daca_info_volume(struct snd_kcontrol *kcontrol,
 125                            struct snd_ctl_elem_info *uinfo)
 126{
 127        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 128        uinfo->count = 2;
 129        uinfo->value.integer.min = 0;
 130        uinfo->value.integer.max = DACA_VOL_MAX;
 131        return 0;
 132}
 133
 134static int daca_get_volume(struct snd_kcontrol *kcontrol,
 135                           struct snd_ctl_elem_value *ucontrol)
 136{
 137        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 138        struct pmac_daca *mix;
 139        if (! (mix = chip->mixer_data))
 140                return -ENODEV;
 141        ucontrol->value.integer.value[0] = mix->left_vol;
 142        ucontrol->value.integer.value[1] = mix->right_vol;
 143        return 0;
 144}
 145
 146static int daca_put_volume(struct snd_kcontrol *kcontrol,
 147                           struct snd_ctl_elem_value *ucontrol)
 148{
 149        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 150        struct pmac_daca *mix;
 151        unsigned int vol[2];
 152        int change;
 153
 154        if (! (mix = chip->mixer_data))
 155                return -ENODEV;
 156        vol[0] = ucontrol->value.integer.value[0];
 157        vol[1] = ucontrol->value.integer.value[1];
 158        if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX)
 159                return -EINVAL;
 160        change = mix->left_vol != vol[0] ||
 161                mix->right_vol != vol[1];
 162        if (change) {
 163                mix->left_vol = vol[0];
 164                mix->right_vol = vol[1];
 165                daca_set_volume(mix);
 166        }
 167        return change;
 168}
 169
 170/* amplifier switch */
 171#define daca_info_amp   daca_info_deemphasis
 172
 173static int daca_get_amp(struct snd_kcontrol *kcontrol,
 174                        struct snd_ctl_elem_value *ucontrol)
 175{
 176        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 177        struct pmac_daca *mix;
 178        if (! (mix = chip->mixer_data))
 179                return -ENODEV;
 180        ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0;
 181        return 0;
 182}
 183
 184static int daca_put_amp(struct snd_kcontrol *kcontrol,
 185                        struct snd_ctl_elem_value *ucontrol)
 186{
 187        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 188        struct pmac_daca *mix;
 189        int change;
 190
 191        if (! (mix = chip->mixer_data))
 192                return -ENODEV;
 193        change = mix->amp_on != ucontrol->value.integer.value[0];
 194        if (change) {
 195                mix->amp_on = !!ucontrol->value.integer.value[0];
 196                i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
 197                                          mix->amp_on ? 0x05 : 0x04);
 198        }
 199        return change;
 200}
 201
 202static struct snd_kcontrol_new daca_mixers[] = {
 203        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 204          .name = "Deemphasis Switch",
 205          .info = daca_info_deemphasis,
 206          .get = daca_get_deemphasis,
 207          .put = daca_put_deemphasis
 208        },
 209        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 210          .name = "Master Playback Volume",
 211          .info = daca_info_volume,
 212          .get = daca_get_volume,
 213          .put = daca_put_volume
 214        },
 215        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 216          .name = "Power Amplifier Switch",
 217          .info = daca_info_amp,
 218          .get = daca_get_amp,
 219          .put = daca_put_amp
 220        },
 221};
 222
 223
 224#ifdef CONFIG_PM
 225static void daca_resume(struct snd_pmac *chip)
 226{
 227        struct pmac_daca *mix = chip->mixer_data;
 228        i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_SR, 0x08);
 229        i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG,
 230                                  mix->amp_on ? 0x05 : 0x04);
 231        daca_set_volume(mix);
 232}
 233#endif /* CONFIG_PM */
 234
 235
 236static void daca_cleanup(struct snd_pmac *chip)
 237{
 238        struct pmac_daca *mix = chip->mixer_data;
 239        if (! mix)
 240                return;
 241        snd_pmac_keywest_cleanup(&mix->i2c);
 242        kfree(mix);
 243        chip->mixer_data = NULL;
 244}
 245
 246/* exported */
 247int __devinit snd_pmac_daca_init(struct snd_pmac *chip)
 248{
 249        int i, err;
 250        struct pmac_daca *mix;
 251
 252        request_module("i2c-powermac");
 253
 254        mix = kzalloc(sizeof(*mix), GFP_KERNEL);
 255        if (! mix)
 256                return -ENOMEM;
 257        chip->mixer_data = mix;
 258        chip->mixer_free = daca_cleanup;
 259        mix->amp_on = 1; /* default on */
 260
 261        mix->i2c.addr = DACA_I2C_ADDR;
 262        mix->i2c.init_client = daca_init_client;
 263        mix->i2c.name = "DACA";
 264        if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0)
 265                return err;
 266
 267        /*
 268         * build mixers
 269         */
 270        strcpy(chip->card->mixername, "PowerMac DACA");
 271
 272        for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) {
 273                if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0)
 274                        return err;
 275        }
 276
 277#ifdef CONFIG_PM
 278        chip->resume = daca_resume;
 279#endif
 280
 281        return 0;
 282}
 283
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.