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
  17#include <asm/olpc.h>
  18#include "cs5535audio.h"
  19
  20/*
  21 * OLPC has an additional feature on top of the regular AD1888 codec features.
  22 * It has an Analog Input mode that is switched into (after disabling the
  23 * High Pass Filter) via GPIO.  It is supported on B2 and later models.
  24 */
  25void olpc_analog_input(struct snd_ac97 *ac97, int on)
  26{
  27        int err;
  28
  29        if (!machine_is_olpc())
  30                return;
  31
  32        /* update the High Pass Filter (via AC97_AD_TEST2) */
  33        err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
  34                        1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
  35        if (err < 0) {
  36                snd_printk(KERN_ERR "setting High Pass Filter - %d\n", err);
  37                return;
  38        }
  39
  40        /* set Analog Input through GPIO */
  41        if (on)
  42                geode_gpio_set(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
  43        else
  44                geode_gpio_clear(OLPC_GPIO_MIC_AC, GPIO_OUTPUT_VAL);
  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] = geode_gpio_isset(OLPC_GPIO_MIC_AC,
  77                        GPIO_OUTPUT_VAL);
  78        return 0;
  79}
  80
  81static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
  82{
  83        struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
  84
  85        olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
  86        return 1;
  87}
  88
  89static int olpc_mic_info(struct snd_kcontrol *kctl,
  90                struct snd_ctl_elem_info *uinfo)
  91{
  92        uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  93        uinfo->count = 1;
  94        uinfo->value.integer.min = 0;
  95        uinfo->value.integer.max = 1;
  96        return 0;
  97}
  98
  99static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
 100{
 101        struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
 102        struct snd_ac97 *ac97 = cs5535au->ac97;
 103        int i;
 104
 105        i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;
 106        v->value.integer.value[0] = i ? 0 : 1;
 107        return 0;
 108}
 109
 110static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
 111{
 112        struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
 113
 114        olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);
 115        return 1;
 116}
 117
 118static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
 119{
 120        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 121        .name = "DC Mode Enable",
 122        .info = olpc_dc_info,
 123        .get = olpc_dc_get,
 124        .put = olpc_dc_put,
 125        .private_value = 0,
 126},
 127{
 128        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 129        .name = "MIC Bias Enable",
 130        .info = olpc_mic_info,
 131        .get = olpc_mic_get,
 132        .put = olpc_mic_put,
 133        .private_value = 0,
 134},
 135};
 136
 137void __devinit olpc_prequirks(struct snd_card *card,
 138                struct snd_ac97_template *ac97)
 139{
 140        if (!machine_is_olpc())
 141                return;
 142
 143        /* invert EAPD if on an OLPC B3 or higher */
 144        if (olpc_board_at_least(olpc_board_pre(0xb3)))
 145                ac97->scaps |= AC97_SCAP_INV_EAPD;
 146}
 147
 148int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
 149{
 150        struct snd_ctl_elem_id elem;
 151        int i, err;
 152
 153        if (!machine_is_olpc())
 154                return 0;
 155
 156        /* drop the original AD1888 HPF control */
 157        memset(&elem, 0, sizeof(elem));
 158        elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 159        strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
 160        snd_ctl_remove_id(card, &elem);
 161
 162        /* drop the original V_REFOUT control */
 163        memset(&elem, 0, sizeof(elem));
 164        elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 165        strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
 166        snd_ctl_remove_id(card, &elem);
 167
 168        /* add the OLPC-specific controls */
 169        for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
 170                err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
 171                                ac97->private_data));
 172                if (err < 0)
 173                        return err;
 174        }
 175
 176        /* turn off the mic by default */
 177        olpc_mic_bias(ac97, 0);
 178        return 0;
 179}
 180