linux/sound/pci/cs5535audio/cs5535audio_olpc.c
<<
>>
Prefs
   1/*
   2 * OLPC XO-1 additional sound features
   3 *
   4 * Copyright © 2006  Jaya Kumar <jayakumar.lkml@gmail.com>
   5 * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 */
  12#include <sound/core.h>
  13#include <sound/info.h>
  14#include <sound/control.h>
  15#include <sound/ac97_codec.h>
  16#include <linux/gpio.h>
  17
  18#include <asm/olpc.h>
  19#include "cs5535audio.h"
  20
  21#define DRV_NAME "cs5535audio-olpc"
  22
  23/*
  24 * OLPC has an additional feature on top of the regular AD1888 codec features.
  25 * It has an Analog Input mode that is switched into (after disabling the
  26 * High Pass Filter) via GPIO.  It is supported on B2 and later models.
  27 */
  28void olpc_analog_input(struct snd_ac97 *ac97, int on)
  29{
  30        int err;
  31
  32        if (!machine_is_olpc())
  33                return;
  34
  35        /* update the High Pass Filter (via AC97_AD_TEST2) */
  36        err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
  37                        1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
  38        if (err < 0) {
  39                snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
  40                return;
  41        }
  42
  43        /* set Analog Input through GPIO */
  44        gpio_set_value(OLPC_GPIO_MIC_AC, on);
  45}
  46
  47/*
  48 * OLPC XO-1's V_REFOUT is a mic bias enable.
  49 */
  50void olpc_mic_bias(struct snd_ac97 *ac97, int on)
  51{
  52        int err;
  53
  54        if (!machine_is_olpc())
  55                return;
  56
  57        on = on ? 0 : 1;
  58        err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
  59                        1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
  60        if (err < 0)
  61                snd_printk(KERN_ERR "setting MIC Bias - %d\n", err);
  62}
  63
  64static int olpc_dc_info(struct snd_kcontrol *kctl,
  65                struct snd_ctl_elem_info *uinfo)
  66{
  67        uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  68        uinfo->count = 1;
  69        uinfo->value.integer.min = 0;
  70        uinfo->value.integer.max = 1;
  71        return 0;
  72}
  73
  74static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
  75{
  76        v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC);
  77        return 0;
  78}
  79
  80static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
  81{
  82        struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
  83
  84        olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
  85        return 1;
  86}
  87
  88static int olpc_mic_info(struct snd_kcontrol *kctl,
  89                struct snd_ctl_elem_info *uinfo)
  90{
  91        uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  92        uinfo->count = 1;
  93        uinfo->value.integer.min = 0;
  94        uinfo->value.integer.max = 1;
  95        return 0;
  96}
  97
  98static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
  99{
 100        struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
 101        struct snd_ac97 *ac97 = cs5535au->ac97;
 102        int i;
 103
 104        i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;
 105        v->value.integer.value[0] = i ? 0 : 1;
 106        return 0;
 107}
 108
 109static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
 110{
 111        struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
 112
 113        olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);
 114        return 1;
 115}
 116
 117static struct snd_kcontrol_new olpc_cs5535audio_ctls[] = {
 118{
 119        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 120        .name = "DC Mode Enable",
 121        .info = olpc_dc_info,
 122        .get = olpc_dc_get,
 123        .put = olpc_dc_put,
 124        .private_value = 0,
 125},
 126{
 127        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 128        .name = "MIC Bias Enable",
 129        .info = olpc_mic_info,
 130        .get = olpc_mic_get,
 131        .put = olpc_mic_put,
 132        .private_value = 0,
 133},
 134};
 135
 136void olpc_prequirks(struct snd_card *card,
 137                    struct snd_ac97_template *ac97)
 138{
 139        if (!machine_is_olpc())
 140                return;
 141
 142        /* invert EAPD if on an OLPC B3 or higher */
 143        if (olpc_board_at_least(olpc_board_pre(0xb3)))
 144                ac97->scaps |= AC97_SCAP_INV_EAPD;
 145}
 146
 147int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
 148{
 149        struct snd_ctl_elem_id elem;
 150        int i, err;
 151
 152        if (!machine_is_olpc())
 153                return 0;
 154
 155        if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {
 156                printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO\n");
 157                return -EIO;
 158        }
 159        gpio_direction_output(OLPC_GPIO_MIC_AC, 0);
 160
 161        /* drop the original AD1888 HPF control */
 162        memset(&elem, 0, sizeof(elem));
 163        elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 164        strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
 165        snd_ctl_remove_id(card, &elem);
 166
 167        /* drop the original V_REFOUT control */
 168        memset(&elem, 0, sizeof(elem));
 169        elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 170        strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
 171        snd_ctl_remove_id(card, &elem);
 172
 173        /* add the OLPC-specific controls */
 174        for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
 175                err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
 176                                ac97->private_data));
 177                if (err < 0) {
 178                        gpio_free(OLPC_GPIO_MIC_AC);
 179                        return err;
 180                }
 181        }
 182
 183        /* turn off the mic by default */
 184        olpc_mic_bias(ac97, 0);
 185        return 0;
 186}
 187
 188void olpc_quirks_cleanup(void)
 189{
 190        gpio_free(OLPC_GPIO_MIC_AC);
 191}
 192