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