cs5535audio_olpc.c (4598B)
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}