/* How they are connected to codec pins */ staticconststruct snd_soc_dapm_route ams_delta_audio_map[] = {
{"TELIN", NULL, "Mouthpiece"},
{"Earpiece", NULL, "TELOUT"},
/* * Used for passing a codec structure pointer * from the board initialization code to the tty line discipline.
*/ staticstruct snd_soc_component *cx20442_codec;
/* After we are able to control the codec over the modem,
* the hook switch can be used for dynamic DAPM reconfiguration. */ staticstruct snd_soc_jack_pin ams_delta_hook_switch_pins[] = { /* Handset */
{
.pin = "Mouthpiece",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Earpiece",
.mask = SND_JACK_HEADPHONE,
}, /* Handsfree */
{
.pin = "Microphone",
.mask = SND_JACK_MICROPHONE,
.invert = 1,
},
{
.pin = "Speaker",
.mask = SND_JACK_HEADPHONE,
.invert = 1,
},
};
/* * Modem line discipline, required for making above controls functional. * Activated from userspace with ldattach, possibly invoked from udev rule.
*/
/* To actually apply any modem controlled configuration changes to the codec, * we must connect codec DAI pins to the modem for a moment. Be careful not
* to interfere with our digital mute function that shares the same hardware. */ staticstruct timer_list cx81801_timer; staticbool cx81801_cmd_pending; staticbool ams_delta_muted; static DEFINE_SPINLOCK(ams_delta_lock); staticstruct gpio_desc *gpiod_modem_codec;
staticvoid cx81801_timeout(struct timer_list *unused)
{ int muted;
/* Reconnect the codec DAI back from the modem to the CPU DAI
* only if digital mute still off */ if (!muted)
gpiod_set_value(gpiod_modem_codec, 0);
}
/* Line discipline .open() */ staticint cx81801_open(struct tty_struct *tty)
{ int ret;
if (!cx20442_codec) return -ENODEV;
/* * Pass the codec structure pointer for use by other ldisc callbacks, * both the card and the codec specific parts.
*/
tty->disc_data = cx20442_codec;
if (!component->card->pop_time) { /* First modem response, complete setup procedure */
/* Initialize timer used for config pulse generation */
timer_setup(&cx81801_timer, cx81801_timeout, 0);
v253_ops.receive_buf(tty, cp, fp, count);
/* Link hook switch to DAPM pins */
ret = snd_soc_jack_add_pins(&ams_delta_hook_switch,
ARRAY_SIZE(ams_delta_hook_switch_pins),
ams_delta_hook_switch_pins); if (ret)
dev_warn(component->dev, "Failed to link hook switch to DAPM pins, " "will continue with hook switch unlinked.\n");
return;
}
v253_ops.receive_buf(tty, cp, fp, count);
for (c = &cp[count - 1]; c >= cp; c--) { if (*c != '\r') continue; /* Complete modem response received, apply config to codec */
/* * Even if not very useful, the sound card can still work without any of the * above functionality activated. You can still control its audio input/output * constellation and speakerphone gain from userspace by issuing AT commands * over the modem port.
*/
staticstruct snd_soc_ops ams_delta_ops;
/* Digital mute implemented using modem/CPU multiplexer.
* Shares hardware with codec config pulse generation */ staticbool ams_delta_muted = 1;
staticint ams_delta_mute(struct snd_soc_dai *dai, int mute, int direction)
{ int apply;
if (apply)
gpiod_set_value(gpiod_modem_codec, !!mute); return 0;
}
/* Our codec DAI probably doesn't have its own .ops structure */ staticconststruct snd_soc_dai_ops ams_delta_dai_ops = {
.mute_stream = ams_delta_mute,
.no_capture_mute = 1,
};
/* Will be used if the codec ever has its own digital_mute function */ staticint ams_delta_startup(struct snd_pcm_substream *substream)
{ return ams_delta_mute(NULL, 0, substream->stream);
}
staticint ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
{ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); struct snd_soc_card *card = rtd->card; struct snd_soc_dapm_context *dapm = &card->dapm; int ret; /* Codec is ready, now add/activate board specific controls */
/* Store a pointer to the codec structure for tty ldisc use */
cx20442_codec = snd_soc_rtd_to_codec(rtd, 0)->component;
/* Add hook switch - can be used to control the codec from userspace
* even if line discipline fails */
ret = snd_soc_card_jack_new_pins(card, "hook_switch", SND_JACK_HEADSET,
&ams_delta_hook_switch, NULL, 0); if (ret)
dev_warn(card->dev, "Failed to allocate resources for hook switch, " "will continue without one.\n"); else {
ret = snd_soc_jack_add_gpiods(card->dev, &ams_delta_hook_switch,
ARRAY_SIZE(ams_delta_hook_switch_gpios),
ams_delta_hook_switch_gpios); if (ret)
dev_warn(card->dev, "Failed to set up hook switch GPIO line, " "will continue with hook switch inactive.\n");
}
gpiod_modem_codec = devm_gpiod_get(card->dev, "modem_codec",
GPIOD_OUT_HIGH); if (IS_ERR(gpiod_modem_codec)) {
dev_warn(card->dev, "Failed to obtain modem_codec GPIO\n"); return 0;
}
/* Set up digital mute if not provided by the codec */ if (!codec_dai->driver->ops) {
codec_dai->driver->ops = &ams_delta_dai_ops;
} else {
ams_delta_ops.startup = ams_delta_startup;
ams_delta_ops.shutdown = ams_delta_shutdown;
}
/* Register optional line discipline for over the modem control */
ret = tty_register_ldisc(&cx81801_ops); if (ret) {
dev_warn(card->dev, "Failed to register line discipline, " "will continue without any controls.\n"); return 0;
}
/* Set up initial pin constellation */
snd_soc_dapm_disable_pin(dapm, "Mouthpiece");
snd_soc_dapm_disable_pin(dapm, "Speaker");
snd_soc_dapm_disable_pin(dapm, "AGCIN");
snd_soc_dapm_disable_pin(dapm, "AGCOUT");
return 0;
}
/* DAI glue - connects codec <--> CPU */
SND_SOC_DAILINK_DEFS(cx20442,
DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.1")),
DAILINK_COMP_ARRAY(COMP_CODEC("cx20442-codec", "cx20442-voice")),
DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.1")));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.