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