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 */
sdca_jack_process(struct sdca_interrupt * interrupt)35 int 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 struct soc_enum *soc_enum;
45 unsigned int reg, val;
46 int ret;
47
48 guard(rwsem_write)(rwsem);
49
50 reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
51 interrupt->control->sel, 0);
52
53 ret = regmap_read(interrupt->function_regmap, reg, &val);
54 if (ret < 0) {
55 dev_err(dev, "failed to read detected mode: %d\n", ret);
56 return ret;
57 }
58
59 reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
60 SDCA_CTL_GE_SELECTED_MODE, 0);
61
62 switch (val) {
63 case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
64 case SDCA_DETECTED_MODE_JACK_UNKNOWN:
65 /*
66 * Selected mode is not normally marked as volatile register
67 * (RW), but here force a read from the hardware. If the
68 * detected mode is unknown we need to see what the device
69 * selected as a "safe" option.
70 */
71 regcache_drop_region(interrupt->function_regmap, reg, reg);
72
73 ret = regmap_read(interrupt->function_regmap, reg, &val);
74 if (ret) {
75 dev_err(dev, "failed to re-check selected mode: %d\n", ret);
76 return ret;
77 }
78 break;
79 default:
80 break;
81 }
82
83 dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
84
85 ucontrol = kzalloc_obj(*ucontrol);
86 if (!ucontrol)
87 return -ENOMEM;
88
89 soc_enum = (struct soc_enum *)kctl->private_value;
90 ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
91
92 ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
93 if (ret < 0) {
94 dev_err(dev, "failed to update selected mode: %d\n", ret);
95 return ret;
96 }
97
98 snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
99
100 return sdca_jack_report(interrupt);
101 }
102 EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
103
104 /**
105 * sdca_jack_alloc_state - allocate state for a jack interrupt
106 * @interrupt: SDCA interrupt structure.
107 *
108 * Return: Zero on success or a negative error code.
109 */
sdca_jack_alloc_state(struct sdca_interrupt * interrupt)110 int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
111 {
112 struct jack_state *jack_state;
113
114 jack_state = kzalloc_obj(*jack_state);
115 if (!jack_state)
116 return -ENOMEM;
117
118 interrupt->priv = jack_state;
119
120 return 0;
121 }
122 EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
123
124 /**
125 * sdca_jack_free_state - free state for a jack interrupt
126 * @interrupt: SDCA interrupt structure.
127 */
sdca_jack_free_state(struct sdca_interrupt * interrupt)128 void sdca_jack_free_state(struct sdca_interrupt *interrupt)
129 {
130 kfree(interrupt->priv);
131 }
132 EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA");
133
134 /**
135 * sdca_jack_init_state - Initialise transient state for a jack interrupt
136 * @interrupt: SDCA interrupt structure.
137 *
138 * Return: Zero on success or a negative error code.
139 */
sdca_jack_init_state(struct sdca_interrupt * interrupt)140 int sdca_jack_init_state(struct sdca_interrupt *interrupt)
141 {
142 struct jack_state *jack_state = interrupt->priv;
143 const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
144 interrupt->entity->label,
145 SDCA_CTL_SELECTED_MODE_NAME);
146
147 if (!name)
148 return -ENOMEM;
149
150 jack_state->kctl = snd_soc_component_get_kcontrol(interrupt->component, name);
151 if (!jack_state->kctl) {
152 dev_err(interrupt->dev, "control not found: %s\n", name);
153 return -ENODEV;
154 }
155
156 return 0;
157 }
158 EXPORT_SYMBOL_NS_GPL(sdca_jack_init_state, "SND_SOC_SDCA");
159
type_get_mask(enum sdca_terminal_type type)160 static int type_get_mask(enum sdca_terminal_type type)
161 {
162 switch (type) {
163 case SDCA_TERM_TYPE_LINEIN_STEREO:
164 case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
165 case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
166 case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
167 case SDCA_TERM_TYPE_LINEIN_REAR_LR:
168 return SND_JACK_LINEIN;
169 case SDCA_TERM_TYPE_LINEOUT_STEREO:
170 case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
171 case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
172 case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
173 case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
174 return SND_JACK_LINEOUT;
175 case SDCA_TERM_TYPE_MIC_JACK:
176 return SND_JACK_MICROPHONE;
177 case SDCA_TERM_TYPE_HEADPHONE_JACK:
178 return SND_JACK_HEADPHONE;
179 case SDCA_TERM_TYPE_HEADSET_JACK:
180 return SND_JACK_HEADSET;
181 default:
182 return 0;
183 }
184 }
185
186 /**
187 * sdca_jack_set_jack - attach an ASoC jack to SDCA
188 * @info: SDCA interrupt information.
189 * @jack: ASoC jack to be attached.
190 *
191 * Return: Zero on success or a negative error code.
192 */
sdca_jack_set_jack(struct sdca_interrupt_info * info,struct snd_soc_jack * jack)193 int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack)
194 {
195 int i, j;
196 int ret;
197
198 guard(mutex)(&info->irq_lock);
199
200 for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
201 struct sdca_interrupt *interrupt = &info->irqs[i];
202 struct sdca_control *control = interrupt->control;
203 struct sdca_entity *entity = interrupt->entity;
204 struct sdca_control_range *range;
205 struct jack_state *jack_state;
206
207 if (!interrupt->dev)
208 continue;
209
210 switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
211 case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
212 range = sdca_selector_find_range(interrupt->dev, entity,
213 SDCA_CTL_GE_SELECTED_MODE,
214 SDCA_SELECTED_MODE_NCOLS, 0);
215 if (!range)
216 return -EINVAL;
217
218 jack_state = interrupt->priv;
219
220 for (j = 0; j < range->rows; j++) {
221 enum sdca_terminal_type type;
222
223 type = sdca_range(range, SDCA_SELECTED_MODE_TERM_TYPE, j);
224
225 jack_state->mask |= type_get_mask(type);
226 }
227
228 jack_state->jack = jack;
229
230 /* Report initial state in case IRQ was already handled */
231 ret = sdca_jack_report(interrupt);
232 if (ret)
233 return ret;
234 break;
235 default:
236 break;
237 }
238 }
239
240 return 0;
241 }
242 EXPORT_SYMBOL_NS_GPL(sdca_jack_set_jack, "SND_SOC_SDCA");
243
sdca_jack_report(struct sdca_interrupt * interrupt)244 int sdca_jack_report(struct sdca_interrupt *interrupt)
245 {
246 struct jack_state *jack_state = interrupt->priv;
247 struct sdca_control_range *range;
248 enum sdca_terminal_type type;
249 unsigned int reg, val;
250 int ret;
251
252 reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
253 SDCA_CTL_GE_SELECTED_MODE, 0);
254
255 ret = regmap_read(interrupt->function_regmap, reg, &val);
256 if (ret) {
257 dev_err(interrupt->dev, "failed to read selected mode: %d\n", ret);
258 return ret;
259 }
260
261 range = sdca_selector_find_range(interrupt->dev, interrupt->entity,
262 SDCA_CTL_GE_SELECTED_MODE,
263 SDCA_SELECTED_MODE_NCOLS, 0);
264 if (!range)
265 return -EINVAL;
266
267 type = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
268 val, SDCA_SELECTED_MODE_TERM_TYPE);
269
270 snd_soc_jack_report(jack_state->jack, type_get_mask(type), jack_state->mask);
271
272 return 0;
273 }
274 EXPORT_SYMBOL_NS_GPL(sdca_jack_report, "SND_SOC_SDCA");
275