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 */ 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 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 = snd_soc_dapm_put_enum_double(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 } 125 EXPORT_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 */ 133 int 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 } 146 EXPORT_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 */ 155 int 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 } 187 EXPORT_SYMBOL_NS_GPL(sdca_jack_set_jack, "SND_SOC_SDCA"); 188 189 int 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 } 248 EXPORT_SYMBOL_NS_GPL(sdca_jack_report, "SND_SOC_SDCA"); 249