^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * ams-delta.c -- SoC audio for Amstrad E3 (Delta) videophone
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Initially based on sound/soc/omap/osk5912.x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright (C) 2008 Mistral Solutions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/gpio/consumer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/tty.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <sound/soc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <sound/jack.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <asm/mach-types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <linux/platform_data/asoc-ti-mcbsp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include "omap-mcbsp.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include "../codecs/cx20442.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static struct gpio_desc *handset_mute;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static struct gpio_desc *handsfree_mute;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static int ams_delta_event_handset(struct snd_soc_dapm_widget *w,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) struct snd_kcontrol *k, int event)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) gpiod_set_value_cansleep(handset_mute, !SND_SOC_DAPM_EVENT_ON(event));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static int ams_delta_event_handsfree(struct snd_soc_dapm_widget *w,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct snd_kcontrol *k, int event)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) gpiod_set_value_cansleep(handsfree_mute, !SND_SOC_DAPM_EVENT_ON(event));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) /* Board specific DAPM widgets */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) /* Handset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) SND_SOC_DAPM_MIC("Mouthpiece", NULL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) SND_SOC_DAPM_HP("Earpiece", ams_delta_event_handset),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) /* Handsfree/Speakerphone */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) SND_SOC_DAPM_MIC("Microphone", NULL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) SND_SOC_DAPM_SPK("Speaker", ams_delta_event_handsfree),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) /* How they are connected to codec pins */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static const struct snd_soc_dapm_route ams_delta_audio_map[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) {"TELIN", NULL, "Mouthpiece"},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) {"Earpiece", NULL, "TELOUT"},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) {"MIC", NULL, "Microphone"},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) {"Speaker", NULL, "SPKOUT"},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * Controls, functional after the modem line discipline is activated.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) /* Virtual switch: audio input/output constellations */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) static const char *ams_delta_audio_mode[] =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) {"Mixed", "Handset", "Handsfree", "Speakerphone"};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /* Selection <-> pin translation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) #define AMS_DELTA_MOUTHPIECE 0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) #define AMS_DELTA_EARPIECE 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) #define AMS_DELTA_MICROPHONE 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) #define AMS_DELTA_SPEAKER 3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) #define AMS_DELTA_AGC 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) #define AMS_DELTA_MIXED ((1 << AMS_DELTA_EARPIECE) | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) (1 << AMS_DELTA_MICROPHONE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) #define AMS_DELTA_HANDSET ((1 << AMS_DELTA_MOUTHPIECE) | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) (1 << AMS_DELTA_EARPIECE))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) #define AMS_DELTA_HANDSFREE ((1 << AMS_DELTA_MICROPHONE) | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) (1 << AMS_DELTA_SPEAKER))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) #define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) static const unsigned short ams_delta_audio_mode_pins[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) AMS_DELTA_MIXED,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) AMS_DELTA_HANDSET,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) AMS_DELTA_HANDSFREE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) AMS_DELTA_SPEAKERPHONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) static unsigned short ams_delta_audio_agc;
^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) * Used for passing a codec structure pointer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) * from the board initialization code to the tty line discipline.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static struct snd_soc_component *cx20442_codec;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) struct snd_ctl_elem_value *ucontrol)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct snd_soc_dapm_context *dapm = &card->dapm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) struct soc_enum *control = (struct soc_enum *)kcontrol->private_value;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) unsigned short pins;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) int pin, changed = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /* Refuse any mode changes if we are not able to control the codec. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (!cx20442_codec->card->pop_time)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return -EUNATCH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) if (ucontrol->value.enumerated.item[0] >= control->items)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) snd_soc_dapm_mutex_lock(dapm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) /* Translate selection to bitmap */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) /* Setup pins after corresponding bits if changed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) changed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (pin)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) snd_soc_dapm_enable_pin_unlocked(dapm, "Mouthpiece");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) pin = !!(pins & (1 << AMS_DELTA_EARPIECE));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) changed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) if (pin)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) snd_soc_dapm_disable_pin_unlocked(dapm, "Earpiece");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) pin = !!(pins & (1 << AMS_DELTA_MICROPHONE));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) changed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) if (pin)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) snd_soc_dapm_disable_pin_unlocked(dapm, "Microphone");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) pin = !!(pins & (1 << AMS_DELTA_SPEAKER));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) changed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) if (pin)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) pin = !!(pins & (1 << AMS_DELTA_AGC));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) if (pin != ams_delta_audio_agc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) ams_delta_audio_agc = pin;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) changed = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if (pin)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) snd_soc_dapm_enable_pin_unlocked(dapm, "AGCIN");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (changed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) snd_soc_dapm_sync_unlocked(dapm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) snd_soc_dapm_mutex_unlock(dapm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) return changed;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) struct snd_ctl_elem_value *ucontrol)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) struct snd_soc_dapm_context *dapm = &card->dapm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) unsigned short pins, mode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) pins = ((snd_soc_dapm_get_pin_status(dapm, "Mouthpiece") <<
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) AMS_DELTA_MOUTHPIECE) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) (snd_soc_dapm_get_pin_status(dapm, "Earpiece") <<
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) AMS_DELTA_EARPIECE));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (pins)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) pins |= (snd_soc_dapm_get_pin_status(dapm, "Microphone") <<
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) AMS_DELTA_MICROPHONE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) pins = ((snd_soc_dapm_get_pin_status(dapm, "Microphone") <<
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) AMS_DELTA_MICROPHONE) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) (snd_soc_dapm_get_pin_status(dapm, "Speaker") <<
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) AMS_DELTA_SPEAKER) |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) (ams_delta_audio_agc << AMS_DELTA_AGC));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) if (pins == ams_delta_audio_mode_pins[mode])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) if (mode >= ARRAY_SIZE(ams_delta_audio_mode))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) ucontrol->value.enumerated.item[0] = mode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) static SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) ams_delta_audio_mode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) static const struct snd_kcontrol_new ams_delta_audio_controls[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) ams_delta_get_audio_mode, ams_delta_set_audio_mode),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) /* Hook switch */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) static struct snd_soc_jack ams_delta_hook_switch;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) .name = "hook_switch",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) .report = SND_JACK_HEADSET,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) .invert = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) .debounce_time = 150,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) /* After we are able to control the codec over the modem,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) * the hook switch can be used for dynamic DAPM reconfiguration. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) /* Handset */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) .pin = "Mouthpiece",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) .mask = SND_JACK_MICROPHONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) .pin = "Earpiece",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) .mask = SND_JACK_HEADPHONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) /* Handsfree */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) .pin = "Microphone",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) .mask = SND_JACK_MICROPHONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) .invert = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) .pin = "Speaker",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) .mask = SND_JACK_HEADPHONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) .invert = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) * Modem line discipline, required for making above controls functional.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) * Activated from userspace with ldattach, possibly invoked from udev rule.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) /* To actually apply any modem controlled configuration changes to the codec,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) * we must connect codec DAI pins to the modem for a moment. Be careful not
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) * to interfere with our digital mute function that shares the same hardware. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) static struct timer_list cx81801_timer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) static bool cx81801_cmd_pending;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) static bool ams_delta_muted;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) static DEFINE_SPINLOCK(ams_delta_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) static struct gpio_desc *gpiod_modem_codec;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) static void cx81801_timeout(struct timer_list *unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) int muted;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) spin_lock(&ams_delta_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) cx81801_cmd_pending = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) muted = ams_delta_muted;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) spin_unlock(&ams_delta_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) /* Reconnect the codec DAI back from the modem to the CPU DAI
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) * only if digital mute still off */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) if (!muted)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) gpiod_set_value(gpiod_modem_codec, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) /* Line discipline .open() */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) static int cx81801_open(struct tty_struct *tty)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) if (!cx20442_codec)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) * Pass the codec structure pointer for use by other ldisc callbacks,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) * both the card and the codec specific parts.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) tty->disc_data = cx20442_codec;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) ret = v253_ops.open(tty);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) if (ret < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) tty->disc_data = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) /* Line discipline .close() */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) static void cx81801_close(struct tty_struct *tty)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) struct snd_soc_component *component = tty->disc_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) struct snd_soc_dapm_context *dapm = &component->card->dapm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) del_timer_sync(&cx81801_timer);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) /* Prevent the hook switch from further changing the DAPM pins */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) INIT_LIST_HEAD(&ams_delta_hook_switch.pins);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) if (!component)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) v253_ops.close(tty);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) /* Revert back to default audio input/output constellation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) snd_soc_dapm_mutex_lock(dapm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) snd_soc_dapm_sync_unlocked(dapm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) snd_soc_dapm_mutex_unlock(dapm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) /* Line discipline .hangup() */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) static int cx81801_hangup(struct tty_struct *tty)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) cx81801_close(tty);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) /* Line discipline .receive_buf() */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) static void cx81801_receive(struct tty_struct *tty,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) const unsigned char *cp, char *fp, int count)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) struct snd_soc_component *component = tty->disc_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) const unsigned char *c;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) int apply, ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) if (!component)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) if (!component->card->pop_time) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) /* First modem response, complete setup procedure */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) /* Initialize timer used for config pulse generation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) timer_setup(&cx81801_timer, cx81801_timeout, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) v253_ops.receive_buf(tty, cp, fp, count);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) /* Link hook switch to DAPM pins */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) ret = snd_soc_jack_add_pins(&ams_delta_hook_switch,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) ARRAY_SIZE(ams_delta_hook_switch_pins),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) ams_delta_hook_switch_pins);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) dev_warn(component->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) "Failed to link hook switch to DAPM pins, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) "will continue with hook switch unlinked.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) v253_ops.receive_buf(tty, cp, fp, count);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) for (c = &cp[count - 1]; c >= cp; c--) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) if (*c != '\r')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) /* Complete modem response received, apply config to codec */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) spin_lock_bh(&ams_delta_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) apply = !ams_delta_muted && !cx81801_cmd_pending;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) cx81801_cmd_pending = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) spin_unlock_bh(&ams_delta_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) /* Apply config pulse by connecting the codec to the modem
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) * if not already done */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) if (apply)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) gpiod_set_value(gpiod_modem_codec, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) /* Line discipline .write_wakeup() */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392) static void cx81801_wakeup(struct tty_struct *tty)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) v253_ops.write_wakeup(tty);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) static struct tty_ldisc_ops cx81801_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) .magic = TTY_LDISC_MAGIC,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) .name = "cx81801",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) .open = cx81801_open,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) .close = cx81801_close,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) .hangup = cx81801_hangup,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) .receive_buf = cx81801_receive,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) .write_wakeup = cx81801_wakeup,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) * Even if not very useful, the sound card can still work without any of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) * above functonality activated. You can still control its audio input/output
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) * constellation and speakerphone gain from userspace by issuing AT commands
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) * over the modem port.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) static struct snd_soc_ops ams_delta_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419) /* Digital mute implemented using modem/CPU multiplexer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) * Shares hardware with codec config pulse generation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) static bool ams_delta_muted = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) static int ams_delta_mute(struct snd_soc_dai *dai, int mute, int direction)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) int apply;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) if (ams_delta_muted == mute)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) spin_lock_bh(&ams_delta_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) ams_delta_muted = mute;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) apply = !cx81801_cmd_pending;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) spin_unlock_bh(&ams_delta_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) if (apply)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) gpiod_set_value(gpiod_modem_codec, !!mute);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) /* Our codec DAI probably doesn't have its own .ops structure */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441) static const struct snd_soc_dai_ops ams_delta_dai_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) .mute_stream = ams_delta_mute,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) .no_capture_mute = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) /* Will be used if the codec ever has its own digital_mute function */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) static int ams_delta_startup(struct snd_pcm_substream *substream)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449) return ams_delta_mute(NULL, 0, substream->stream);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452) static void ams_delta_shutdown(struct snd_pcm_substream *substream)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454) ams_delta_mute(NULL, 1, substream->stream);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459) * Card initialization
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465) struct snd_soc_card *card = rtd->card;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) struct snd_soc_dapm_context *dapm = &card->dapm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) /* Codec is ready, now add/activate board specific controls */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) /* Store a pointer to the codec structure for tty ldisc use */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471) cx20442_codec = asoc_rtd_to_codec(rtd, 0)->component;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) /* Add hook switch - can be used to control the codec from userspace
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) * even if line discipline fails */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476) &ams_delta_hook_switch, NULL, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) dev_warn(card->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) "Failed to allocate resources for hook switch, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) "will continue without one.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) ret = snd_soc_jack_add_gpiods(card->dev, &ams_delta_hook_switch,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483) ARRAY_SIZE(ams_delta_hook_switch_gpios),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) ams_delta_hook_switch_gpios);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) dev_warn(card->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) "Failed to set up hook switch GPIO line, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) "will continue with hook switch inactive.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491) gpiod_modem_codec = devm_gpiod_get(card->dev, "modem_codec",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) GPIOD_OUT_HIGH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) if (IS_ERR(gpiod_modem_codec)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) dev_warn(card->dev, "Failed to obtain modem_codec GPIO\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) /* Set up digital mute if not provided by the codec */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499) if (!codec_dai->driver->ops) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) codec_dai->driver->ops = &ams_delta_dai_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502) ams_delta_ops.startup = ams_delta_startup;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) ams_delta_ops.shutdown = ams_delta_shutdown;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) /* Register optional line discipline for over the modem control */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) ret = tty_register_ldisc(N_V253, &cx81801_ops);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) dev_warn(card->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) "Failed to register line discipline, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511) "will continue without any controls.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) /* Set up initial pin constellation */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) snd_soc_dapm_disable_pin(dapm, "Mouthpiece");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) snd_soc_dapm_disable_pin(dapm, "Speaker");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) snd_soc_dapm_disable_pin(dapm, "AGCIN");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) snd_soc_dapm_disable_pin(dapm, "AGCOUT");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) /* DAI glue - connects codec <--> CPU */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) SND_SOC_DAILINK_DEFS(cx20442,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.1")),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) DAILINK_COMP_ARRAY(COMP_CODEC("cx20442-codec", "cx20442-voice")),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.1")));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) static struct snd_soc_dai_link ams_delta_dai_link = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) .name = "CX20442",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) .stream_name = "CX20442",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) .init = ams_delta_cx20442_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) .ops = &ams_delta_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536) SND_SOC_DAIFMT_CBM_CFM,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) SND_SOC_DAILINK_REG(cx20442),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) /* Audio card driver */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541) static struct snd_soc_card ams_delta_audio_card = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) .name = "AMS_DELTA",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) .dai_link = &ams_delta_dai_link,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) .num_links = 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) .controls = ams_delta_audio_controls,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) .num_controls = ARRAY_SIZE(ams_delta_audio_controls),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) .dapm_widgets = ams_delta_dapm_widgets,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) .num_dapm_widgets = ARRAY_SIZE(ams_delta_dapm_widgets),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) .dapm_routes = ams_delta_audio_map,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) .num_dapm_routes = ARRAY_SIZE(ams_delta_audio_map),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) /* Module init/exit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) static int ams_delta_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) struct snd_soc_card *card = &ams_delta_audio_card;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) card->dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563) handset_mute = devm_gpiod_get(card->dev, "handset_mute",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) GPIOD_OUT_HIGH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) if (IS_ERR(handset_mute))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566) return PTR_ERR(handset_mute);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) handsfree_mute = devm_gpiod_get(card->dev, "handsfree_mute",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) GPIOD_OUT_HIGH);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) if (IS_ERR(handsfree_mute))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) return PTR_ERR(handsfree_mute);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) ret = snd_soc_register_card(card);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) card->dev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) static int ams_delta_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) struct snd_soc_card *card = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) if (tty_unregister_ldisc(N_V253) != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) dev_warn(&pdev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) "failed to unregister V253 line discipline\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) snd_soc_unregister_card(card);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) card->dev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) #define DRV_NAME "ams-delta-audio"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) static struct platform_driver ams_delta_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) .name = DRV_NAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) .probe = ams_delta_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) .remove = ams_delta_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) module_platform_driver(ams_delta_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610) MODULE_ALIAS("platform:" DRV_NAME);