File Index
Files and directories
Sign in
FILE

sdca_jack.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sdca/sdca_jack.c C
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (C) 2025 Cirrus Logic, Inc. and
  3//                    Cirrus Logic International Semiconductor Ltd.
  4
  5/*
  6 * The MIPI SDCA specification is available for public downloads at
  7 * https://www.mipi.org/mipi-sdca-v1-0-download
  8 */
  9
 10#include <linux/cleanup.h>
 11#include <linux/device.h>
 12#include <linux/dev_printk.h>
 13#include <linux/soundwire/sdw.h>
 14#include <linux/soundwire/sdw_registers.h>
 15#include <linux/sprintf.h>
 16#include <linux/regmap.h>
 17#include <linux/rwsem.h>
 18#include <sound/asound.h>
 19#include <sound/control.h>
 20#include <sound/jack.h>
 21#include <sound/sdca.h>
 22#include <sound/sdca_function.h>
 23#include <sound/sdca_interrupts.h>
 24#include <sound/sdca_jack.h>
 25#include <sound/soc-component.h>
 26#include <sound/soc-jack.h>
 27#include <sound/soc.h>
 28
 29/**
 30 * sdca_jack_process - Process an SDCA jack event
 31 * @interrupt: SDCA interrupt structure
 32 *
 33 * Return: Zero on success or a negative error code.
 34 */
 35int sdca_jack_process(struct sdca_interrupt *interrupt)
 36{
 37	struct device *dev = interrupt->dev;
 38	struct snd_soc_component *component = interrupt->component;
 39	struct snd_soc_card *card = component->card;
 40	struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem;
 41	struct jack_state *state = interrupt->priv;
 42	struct snd_kcontrol *kctl = state->kctl;
 43	struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
 44	unsigned int reg, val;
 45	int ret;
 46
 47	guard(rwsem_write)(rwsem);
 48
 49	if (!kctl) {
 50		const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
 51							   interrupt->entity->label,
 52							   SDCA_CTL_SELECTED_MODE_NAME);
 53
 54		if (!name)
 55			return -ENOMEM;
 56
 57		kctl = snd_soc_component_get_kcontrol(component, name);
 58		if (!kctl)
 59			dev_dbg(dev, "control not found: %s\n", name);
 60		else
 61			state->kctl = kctl;
 62	}
 63
 64	reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
 65			   interrupt->control->sel, 0);
 66
 67	ret = regmap_read(interrupt->function_regmap, reg, &val);
 68	if (ret < 0) {
 69		dev_err(dev, "failed to read detected mode: %d\n", ret);
 70		return ret;
 71	}
 72
 73	reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
 74			   SDCA_CTL_GE_SELECTED_MODE, 0);
 75
 76	switch (val) {
 77	case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
 78	case SDCA_DETECTED_MODE_JACK_UNKNOWN:
 79		/*
 80		 * Selected mode is not normally marked as volatile register
 81		 * (RW), but here force a read from the hardware. If the
 82		 * detected mode is unknown we need to see what the device
 83		 * selected as a "safe" option.
 84		 */
 85		regcache_drop_region(interrupt->function_regmap, reg, reg);
 86
 87		ret = regmap_read(interrupt->function_regmap, reg, &val);
 88		if (ret) {
 89			dev_err(dev, "failed to re-check selected mode: %d\n", ret);
 90			return ret;
 91		}
 92		break;
 93	default:
 94		break;
 95	}
 96
 97	dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
 98
 99	if (kctl) {
100		struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value;
101
102		ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
103		if (!ucontrol)
104			return -ENOMEM;
105
106		ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
107
108		ret = kctl->put(kctl, ucontrol);
109		if (ret < 0) {
110			dev_err(dev, "failed to update selected mode: %d\n", ret);
111			return ret;
112		}
113
114		snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
115	} else {
116		ret = regmap_write(interrupt->function_regmap, reg, val);
117		if (ret) {
118			dev_err(dev, "failed to write selected mode: %d\n", ret);
119			return ret;
120		}
121	}
122
123	return sdca_jack_report(interrupt);
124}
125EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
126
127/**
128 * sdca_jack_alloc_state - allocate state for a jack interrupt
129 * @interrupt: SDCA interrupt structure.
130 *
131 * Return: Zero on success or a negative error code.
132 */
133int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
134{
135	struct device *dev = interrupt->dev;
136	struct jack_state *jack_state;
137
138	jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL);
139	if (!jack_state)
140		return -ENOMEM;
141
142	interrupt->priv = jack_state;
143
144	return 0;
145}
146EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
147
148/**
149 * sdca_jack_set_jack - attach an ASoC jack to SDCA
150 * @info: SDCA interrupt information.
151 * @jack: ASoC jack to be attached.
152 *
153 * Return: Zero on success or a negative error code.
154 */
155int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack)
156{
157	int i, ret;
158
159	guard(mutex)(&info->irq_lock);
160
161	for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
162		struct sdca_interrupt *interrupt = &info->irqs[i];
163		struct sdca_control *control = interrupt->control;
164		struct sdca_entity *entity = interrupt->entity;
165		struct jack_state *jack_state;
166
167		if (!interrupt->irq)
168			continue;
169
170		switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
171		case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
172			jack_state = interrupt->priv;
173			jack_state->jack = jack;
174
175			/* Report initial state in case IRQ was already handled */
176			ret = sdca_jack_report(interrupt);
177			if (ret)
178				return ret;
179			break;
180		default:
181			break;
182		}
183	}
184
185	return 0;
186}
187EXPORT_SYMBOL_NS_GPL(sdca_jack_set_jack, "SND_SOC_SDCA");
188
189int sdca_jack_report(struct sdca_interrupt *interrupt)
190{
191	struct jack_state *jack_state = interrupt->priv;
192	struct sdca_control_range *range;
193	enum sdca_terminal_type type;
194	unsigned int report = 0;
195	unsigned int reg, val;
196	int ret;
197
198	reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
199			   SDCA_CTL_GE_SELECTED_MODE, 0);
200
201	ret = regmap_read(interrupt->function_regmap, reg, &val);
202	if (ret) {
203		dev_err(interrupt->dev, "failed to read selected mode: %d\n", ret);
204		return ret;
205	}
206
207	range = sdca_selector_find_range(interrupt->dev, interrupt->entity,
208					 SDCA_CTL_GE_SELECTED_MODE,
209					 SDCA_SELECTED_MODE_NCOLS, 0);
210	if (!range)
211		return -EINVAL;
212
213	type = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
214				 val, SDCA_SELECTED_MODE_TERM_TYPE);
215
216	switch (type) {
217	case SDCA_TERM_TYPE_LINEIN_STEREO:
218	case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
219	case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
220	case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
221	case SDCA_TERM_TYPE_LINEIN_REAR_LR:
222		report = SND_JACK_LINEIN;
223		break;
224	case SDCA_TERM_TYPE_LINEOUT_STEREO:
225	case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
226	case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
227	case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
228	case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
229		report = SND_JACK_LINEOUT;
230		break;
231	case SDCA_TERM_TYPE_MIC_JACK:
232		report = SND_JACK_MICROPHONE;
233		break;
234	case SDCA_TERM_TYPE_HEADPHONE_JACK:
235		report = SND_JACK_HEADPHONE;
236		break;
237	case SDCA_TERM_TYPE_HEADSET_JACK:
238		report = SND_JACK_HEADSET;
239		break;
240	default:
241		break;
242	}
243
244	snd_soc_jack_report(jack_state->jack, report, 0xFFFF);
245
246	return 0;
247}
248EXPORT_SYMBOL_NS_GPL(sdca_jack_report, "SND_SOC_SDCA");
~/ Read-only file preview.