FILE
acp3x-es83xx.c
File preview with syntax highlighting.
linux-6.19.14/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
1// SPDX-License-Identifier: GPL-2.0+ 2// 3// Machine driver for AMD ACP Audio engine using ES8336 codec. 4// 5// Copyright 2023 Marian Postevca <posteuca@mutex.one> 6#include <sound/core.h> 7#include <sound/soc.h> 8#include <sound/pcm.h> 9#include <sound/pcm_params.h> 10#include <sound/soc-dapm.h> 11#include <sound/jack.h> 12#include <sound/soc-acpi.h> 13#include <linux/clk.h> 14#include <linux/gpio.h> 15#include <linux/gpio/consumer.h> 16#include <linux/module.h> 17#include <linux/i2c.h> 18#include <linux/input.h> 19#include <linux/io.h> 20#include <linux/acpi.h> 21#include <linux/dmi.h> 22#include <linux/string_choices.h> 23#include "../acp-mach.h" 24#include "acp3x-es83xx.h" 25 26#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv)) 27 28#define DUAL_CHANNEL 2 29 30#define ES83XX_ENABLE_DMIC BIT(4) 31#define ES83XX_48_MHZ_MCLK BIT(5) 32 33struct acp3x_es83xx_private { 34 bool speaker_on; 35 bool headphone_on; 36 unsigned long quirk; 37 struct snd_soc_component *codec; 38 struct device *codec_dev; 39 struct gpio_desc *gpio_speakers, *gpio_headphone; 40 struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio; 41 struct acpi_gpio_mapping gpio_mapping[3]; 42 struct snd_soc_dapm_route mic_map[2]; 43}; 44 45static const unsigned int channels[] = { 46 DUAL_CHANNEL, 47}; 48 49static const struct snd_pcm_hw_constraint_list constraints_channels = { 50 .count = ARRAY_SIZE(channels), 51 .list = channels, 52 .mask = 0, 53}; 54 55#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256) 56#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000) 57 58static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w, 59 struct snd_kcontrol *kcontrol, int event); 60static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w, 61 struct snd_kcontrol *kcontrol, int event); 62 63static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream) 64{ 65 struct snd_pcm_runtime *runtime; 66 struct snd_soc_pcm_runtime *rtd; 67 struct snd_soc_dai *codec_dai; 68 struct acp3x_es83xx_private *priv; 69 unsigned int freq; 70 int ret; 71 72 runtime = substream->runtime; 73 rtd = snd_soc_substream_to_rtd(substream); 74 codec_dai = snd_soc_rtd_to_codec(rtd, 0); 75 priv = get_mach_priv(rtd->card); 76 77 if (priv->quirk & ES83XX_48_MHZ_MCLK) { 78 dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n"); 79 freq = ES83xx_48_MHZ_MCLK_FREQ; 80 } else { 81 dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n"); 82 freq = ES83xx_12288_KHZ_MCLK_FREQ; 83 } 84 85 ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_OUT); 86 if (ret < 0) { 87 dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); 88 return ret; 89 } 90 91 runtime->hw.channels_max = DUAL_CHANNEL; 92 snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 93 &constraints_channels); 94 95 return 0; 96} 97 98static struct snd_soc_jack es83xx_jack; 99 100static struct snd_soc_jack_pin es83xx_jack_pins[] = { 101 { 102 .pin = "Headphone", 103 .mask = SND_JACK_HEADPHONE, 104 }, 105 { 106 .pin = "Headset Mic", 107 .mask = SND_JACK_MICROPHONE, 108 }, 109}; 110 111static const struct snd_soc_dapm_widget acp3x_es83xx_widgets[] = { 112 SND_SOC_DAPM_SPK("Speaker", NULL), 113 SND_SOC_DAPM_HP("Headphone", NULL), 114 SND_SOC_DAPM_MIC("Headset Mic", NULL), 115 SND_SOC_DAPM_MIC("Internal Mic", NULL), 116 117 SND_SOC_DAPM_SUPPLY("Headphone Power", SND_SOC_NOPM, 0, 0, 118 acp3x_es83xx_headphone_power_event, 119 SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), 120 SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0, 121 acp3x_es83xx_speaker_power_event, 122 SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), 123}; 124 125static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = { 126 {"Headphone", NULL, "HPOL"}, 127 {"Headphone", NULL, "HPOR"}, 128 {"Headphone", NULL, "Headphone Power"}, 129 130 /* 131 * There is no separate speaker output instead the speakers are muxed to 132 * the HP outputs. The mux is controlled Speaker and/or headphone switch. 133 */ 134 {"Speaker", NULL, "HPOL"}, 135 {"Speaker", NULL, "HPOR"}, 136 {"Speaker", NULL, "Speaker Power"}, 137}; 138 139 140static const struct snd_kcontrol_new acp3x_es83xx_controls[] = { 141 SOC_DAPM_PIN_SWITCH("Speaker"), 142 SOC_DAPM_PIN_SWITCH("Headphone"), 143 SOC_DAPM_PIN_SWITCH("Headset Mic"), 144 SOC_DAPM_PIN_SWITCH("Internal Mic"), 145}; 146 147static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card) 148{ 149 card->dapm_widgets = acp3x_es83xx_widgets; 150 card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets); 151 card->controls = acp3x_es83xx_controls; 152 card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls); 153 card->dapm_routes = acp3x_es83xx_audio_map; 154 card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map); 155 156 return 0; 157} 158 159static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w, 160 struct snd_kcontrol *kcontrol, int event) 161{ 162 struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); 163 struct acp3x_es83xx_private *priv = get_mach_priv(card); 164 165 dev_dbg(priv->codec_dev, "headphone power event = %d\n", event); 166 if (SND_SOC_DAPM_EVENT_ON(event)) 167 priv->headphone_on = true; 168 else 169 priv->headphone_on = false; 170 171 gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on); 172 gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on); 173 174 return 0; 175} 176 177static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w, 178 struct snd_kcontrol *kcontrol, int event) 179{ 180 struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm); 181 struct acp3x_es83xx_private *priv = get_mach_priv(card); 182 183 dev_dbg(priv->codec_dev, "speaker power event: %d\n", event); 184 if (SND_SOC_DAPM_EVENT_ON(event)) 185 priv->speaker_on = true; 186 else 187 priv->speaker_on = false; 188 189 gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on); 190 gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on); 191 192 return 0; 193} 194 195static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card) 196{ 197 struct acp3x_es83xx_private *priv = get_mach_priv(card); 198 199 /* We need to disable the jack in the machine driver suspend 200 * callback so that the CODEC suspend callback actually gets 201 * called. Without doing it, the CODEC suspend/resume 202 * callbacks do not get called if headphones are plugged in. 203 * This is because plugging in headphones keeps some supplies 204 * active, this in turn means that the lowest bias level 205 * that the CODEC can go to is SND_SOC_BIAS_STANDBY. 206 * If components do not set idle_bias_on to true then 207 * their suspend/resume callbacks do not get called. 208 */ 209 dev_dbg(priv->codec_dev, "card suspend\n"); 210 snd_soc_component_set_jack(priv->codec, NULL, NULL); 211 return 0; 212} 213 214static int acp3x_es83xx_resume_post(struct snd_soc_card *card) 215{ 216 struct acp3x_es83xx_private *priv = get_mach_priv(card); 217 218 /* We disabled jack detection in suspend callback, 219 * enable it back. 220 */ 221 dev_dbg(priv->codec_dev, "card resume\n"); 222 snd_soc_component_set_jack(priv->codec, &es83xx_jack, NULL); 223 return 0; 224} 225 226static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv) 227{ 228 229 priv->enable_spk_gpio.crs_entry_index = 0; 230 priv->enable_hp_gpio.crs_entry_index = 1; 231 232 priv->enable_spk_gpio.active_low = false; 233 priv->enable_hp_gpio.active_low = false; 234 235 priv->gpio_mapping[0].name = "speakers-enable-gpios"; 236 priv->gpio_mapping[0].data = &priv->enable_spk_gpio; 237 priv->gpio_mapping[0].size = 1; 238 priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO; 239 240 priv->gpio_mapping[1].name = "headphone-enable-gpios"; 241 priv->gpio_mapping[1].data = &priv->enable_hp_gpio; 242 priv->gpio_mapping[1].size = 1; 243 priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO; 244 245 dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n", 246 priv->enable_spk_gpio.crs_entry_index, 247 str_low_high(priv->enable_spk_gpio.active_low), 248 priv->enable_hp_gpio.crs_entry_index, 249 str_low_high(priv->enable_hp_gpio.active_low)); 250 return 0; 251} 252 253static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv) 254{ 255 int num_routes = 0; 256 int i; 257 258 if (!(priv->quirk & ES83XX_ENABLE_DMIC)) { 259 priv->mic_map[num_routes].sink = "MIC1"; 260 priv->mic_map[num_routes].source = "Internal Mic"; 261 num_routes++; 262 } 263 264 priv->mic_map[num_routes].sink = "MIC2"; 265 priv->mic_map[num_routes].source = "Headset Mic"; 266 num_routes++; 267 268 for (i = 0; i < num_routes; i++) 269 dev_info(priv->codec_dev, "%s is %s\n", 270 priv->mic_map[i].source, priv->mic_map[i].sink); 271 272 return num_routes; 273} 274 275static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime) 276{ 277 struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component; 278 struct snd_soc_card *card = runtime->card; 279 struct acp3x_es83xx_private *priv = get_mach_priv(card); 280 int ret = 0; 281 int num_routes; 282 283 ret = snd_soc_card_jack_new_pins(card, "Headset", 284 SND_JACK_HEADSET | SND_JACK_BTN_0, 285 &es83xx_jack, es83xx_jack_pins, 286 ARRAY_SIZE(es83xx_jack_pins)); 287 if (ret) { 288 dev_err(card->dev, "jack creation failed %d\n", ret); 289 return ret; 290 } 291 292 snd_jack_set_key(es83xx_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); 293 294 snd_soc_component_set_jack(codec, &es83xx_jack, NULL); 295 296 priv->codec = codec; 297 acp3x_es83xx_configure_gpios(priv); 298 299 ret = devm_acpi_dev_add_driver_gpios(priv->codec_dev, priv->gpio_mapping); 300 if (ret) 301 dev_warn(priv->codec_dev, "failed to add speaker gpio\n"); 302 303 priv->gpio_speakers = gpiod_get_optional(priv->codec_dev, "speakers-enable", 304 priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH); 305 if (IS_ERR(priv->gpio_speakers)) { 306 dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n"); 307 return PTR_ERR(priv->gpio_speakers); 308 } 309 310 priv->gpio_headphone = gpiod_get_optional(priv->codec_dev, "headphone-enable", 311 priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH); 312 if (IS_ERR(priv->gpio_headphone)) { 313 dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n"); 314 return PTR_ERR(priv->gpio_headphone); 315 } 316 317 num_routes = acp3x_es83xx_configure_mics(priv); 318 if (num_routes > 0) { 319 struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); 320 321 ret = snd_soc_dapm_add_routes(dapm, priv->mic_map, num_routes); 322 if (ret != 0) 323 device_remove_software_node(priv->codec_dev); 324 } 325 326 return ret; 327} 328 329static const struct snd_soc_ops acp3x_es83xx_ops = { 330 .startup = acp3x_es83xx_codec_startup, 331}; 332 333 334SND_SOC_DAILINK_DEF(codec, 335 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi"))); 336 337static const struct dmi_system_id acp3x_es83xx_dmi_table[] = { 338 { 339 .matches = { 340 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), 341 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"), 342 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), 343 }, 344 .driver_data = (void *)(ES83XX_ENABLE_DMIC), 345 }, 346 { 347 .matches = { 348 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), 349 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"), 350 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), 351 }, 352 .driver_data = (void *)(ES83XX_ENABLE_DMIC), 353 }, 354 { 355 .matches = { 356 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), 357 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"), 358 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), 359 }, 360 .driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK), 361 }, 362 { 363 .matches = { 364 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), 365 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), 366 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"), 367 }, 368 .driver_data = (void *)(ES83XX_ENABLE_DMIC), 369 }, 370 { 371 .matches = { 372 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), 373 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), 374 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"), 375 }, 376 .driver_data = (void *)(ES83XX_ENABLE_DMIC), 377 }, 378 { 379 .matches = { 380 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"), 381 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"), 382 DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"), 383 }, 384 .driver_data = (void *)(ES83XX_ENABLE_DMIC), 385 }, 386 {} 387}; 388 389static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) 390{ 391 link->codecs = codec; 392 link->num_codecs = ARRAY_SIZE(codec); 393 link->init = acp3x_es83xx_init; 394 link->ops = &acp3x_es83xx_ops; 395 link->dai_fmt = SND_SOC_DAIFMT_I2S 396 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; 397 398 return 0; 399} 400 401static int acp3x_es83xx_probe(struct snd_soc_card *card) 402{ 403 int ret = 0; 404 struct device *dev = card->dev; 405 const struct dmi_system_id *dmi_id; 406 407 dmi_id = dmi_first_match(acp3x_es83xx_dmi_table); 408 if (dmi_id && dmi_id->driver_data) { 409 struct acp3x_es83xx_private *priv; 410 struct acp_card_drvdata *acp_drvdata; 411 struct acpi_device *adev; 412 struct device *codec_dev; 413 414 acp_drvdata = (struct acp_card_drvdata *)card->drvdata; 415 416 dev_info(dev, "matched DMI table with this system, trying to register sound card\n"); 417 418 adev = acpi_dev_get_first_match_dev(acp_drvdata->acpi_mach->id, NULL, -1); 419 if (!adev) { 420 dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id); 421 return -ENXIO; 422 } 423 424 codec_dev = acpi_get_first_physical_node(adev); 425 acpi_dev_put(adev); 426 if (!codec_dev) { 427 dev_warn(dev, "Error cannot find codec device, will defer probe\n"); 428 return -EPROBE_DEFER; 429 } 430 431 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 432 if (!priv) { 433 put_device(codec_dev); 434 return -ENOMEM; 435 } 436 437 priv->codec_dev = codec_dev; 438 priv->quirk = (unsigned long)dmi_id->driver_data; 439 acp_drvdata->mach_priv = priv; 440 dev_info(dev, "successfully probed the sound card\n"); 441 } else { 442 ret = -ENODEV; 443 dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n"); 444 } 445 return ret; 446} 447 448 449void acp3x_es83xx_init_ops(struct acp_mach_ops *ops) 450{ 451 ops->probe = acp3x_es83xx_probe; 452 ops->configure_widgets = acp3x_es83xx_configure_widgets; 453 ops->configure_link = acp3x_es83xx_configure_link; 454 ops->suspend_pre = acp3x_es83xx_suspend_pre; 455 ops->resume_post = acp3x_es83xx_resume_post; 456}
~/
Read-only file preview.