Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^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);