Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * OLPC XO-1 additional sound features
0004  *
0005  * Copyright © 2006  Jaya Kumar <jayakumar.lkml@gmail.com>
0006  * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
0007  */
0008 #include <sound/core.h>
0009 #include <sound/info.h>
0010 #include <sound/control.h>
0011 #include <sound/ac97_codec.h>
0012 #include <linux/gpio.h>
0013 
0014 #include <asm/olpc.h>
0015 #include "cs5535audio.h"
0016 
0017 #define DRV_NAME "cs5535audio-olpc"
0018 
0019 /*
0020  * OLPC has an additional feature on top of the regular AD1888 codec features.
0021  * It has an Analog Input mode that is switched into (after disabling the
0022  * High Pass Filter) via GPIO.  It is supported on B2 and later models.
0023  */
0024 void olpc_analog_input(struct snd_ac97 *ac97, int on)
0025 {
0026     int err;
0027 
0028     if (!machine_is_olpc())
0029         return;
0030 
0031     /* update the High Pass Filter (via AC97_AD_TEST2) */
0032     err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
0033             1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
0034     if (err < 0) {
0035         dev_err(ac97->bus->card->dev,
0036             "setting High Pass Filter - %d\n", err);
0037         return;
0038     }
0039 
0040     /* set Analog Input through GPIO */
0041     gpio_set_value(OLPC_GPIO_MIC_AC, on);
0042 }
0043 
0044 /*
0045  * OLPC XO-1's V_REFOUT is a mic bias enable.
0046  */
0047 void olpc_mic_bias(struct snd_ac97 *ac97, int on)
0048 {
0049     int err;
0050 
0051     if (!machine_is_olpc())
0052         return;
0053 
0054     on = on ? 0 : 1;
0055     err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
0056             1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
0057     if (err < 0)
0058         dev_err(ac97->bus->card->dev, "setting MIC Bias - %d\n", err);
0059 }
0060 
0061 static int olpc_dc_info(struct snd_kcontrol *kctl,
0062         struct snd_ctl_elem_info *uinfo)
0063 {
0064     uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
0065     uinfo->count = 1;
0066     uinfo->value.integer.min = 0;
0067     uinfo->value.integer.max = 1;
0068     return 0;
0069 }
0070 
0071 static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
0072 {
0073     v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC);
0074     return 0;
0075 }
0076 
0077 static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
0078 {
0079     struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
0080 
0081     olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
0082     return 1;
0083 }
0084 
0085 static int olpc_mic_info(struct snd_kcontrol *kctl,
0086         struct snd_ctl_elem_info *uinfo)
0087 {
0088     uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
0089     uinfo->count = 1;
0090     uinfo->value.integer.min = 0;
0091     uinfo->value.integer.max = 1;
0092     return 0;
0093 }
0094 
0095 static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
0096 {
0097     struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
0098     struct snd_ac97 *ac97 = cs5535au->ac97;
0099     int i;
0100 
0101     i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;
0102     v->value.integer.value[0] = i ? 0 : 1;
0103     return 0;
0104 }
0105 
0106 static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
0107 {
0108     struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
0109 
0110     olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);
0111     return 1;
0112 }
0113 
0114 static const struct snd_kcontrol_new olpc_cs5535audio_ctls[] = {
0115 {
0116     .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0117     .name = "DC Mode Enable",
0118     .info = olpc_dc_info,
0119     .get = olpc_dc_get,
0120     .put = olpc_dc_put,
0121     .private_value = 0,
0122 },
0123 {
0124     .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0125     .name = "MIC Bias Enable",
0126     .info = olpc_mic_info,
0127     .get = olpc_mic_get,
0128     .put = olpc_mic_put,
0129     .private_value = 0,
0130 },
0131 };
0132 
0133 void olpc_prequirks(struct snd_card *card,
0134             struct snd_ac97_template *ac97)
0135 {
0136     if (!machine_is_olpc())
0137         return;
0138 
0139     /* invert EAPD if on an OLPC B3 or higher */
0140     if (olpc_board_at_least(olpc_board_pre(0xb3)))
0141         ac97->scaps |= AC97_SCAP_INV_EAPD;
0142 }
0143 
0144 int olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
0145 {
0146     struct snd_ctl_elem_id elem;
0147     int i, err;
0148 
0149     if (!machine_is_olpc())
0150         return 0;
0151 
0152     if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {
0153         dev_err(card->dev, "unable to allocate MIC GPIO\n");
0154         return -EIO;
0155     }
0156     gpio_direction_output(OLPC_GPIO_MIC_AC, 0);
0157 
0158     /* drop the original AD1888 HPF control */
0159     memset(&elem, 0, sizeof(elem));
0160     elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0161     strscpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
0162     snd_ctl_remove_id(card, &elem);
0163 
0164     /* drop the original V_REFOUT control */
0165     memset(&elem, 0, sizeof(elem));
0166     elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0167     strscpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
0168     snd_ctl_remove_id(card, &elem);
0169 
0170     /* add the OLPC-specific controls */
0171     for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
0172         err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
0173                 ac97->private_data));
0174         if (err < 0)
0175             return err;
0176     }
0177 
0178     /* turn off the mic by default */
0179     olpc_mic_bias(ac97, 0);
0180     return 0;
0181 }
0182 
0183 void olpc_quirks_cleanup(void)
0184 {
0185     if (machine_is_olpc())
0186         gpio_free(OLPC_GPIO_MIC_AC);
0187 }