Path: blob/master/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
56148 views
// SPDX-License-Identifier: GPL-2.0+1//2// Machine driver for AMD ACP Audio engine using ES8336 codec.3//4// Copyright 2023 Marian Postevca <[email protected]>5#include <sound/core.h>6#include <sound/soc.h>7#include <sound/pcm.h>8#include <sound/pcm_params.h>9#include <sound/soc-dapm.h>10#include <sound/jack.h>11#include <sound/soc-acpi.h>12#include <linux/clk.h>13#include <linux/gpio.h>14#include <linux/gpio/consumer.h>15#include <linux/module.h>16#include <linux/i2c.h>17#include <linux/input.h>18#include <linux/io.h>19#include <linux/acpi.h>20#include <linux/dmi.h>21#include <linux/string_choices.h>22#include "../acp-mach.h"23#include "acp3x-es83xx.h"2425#define get_mach_priv(card) ((struct acp3x_es83xx_private *)((acp_get_drvdata(card))->mach_priv))2627#define DUAL_CHANNEL 22829#define ES83XX_ENABLE_DMIC BIT(4)30#define ES83XX_48_MHZ_MCLK BIT(5)3132struct acp3x_es83xx_private {33bool speaker_on;34bool headphone_on;35unsigned long quirk;36struct snd_soc_component *codec;37struct device *codec_dev;38struct gpio_desc *gpio_speakers, *gpio_headphone;39struct acpi_gpio_params enable_spk_gpio, enable_hp_gpio;40struct acpi_gpio_mapping gpio_mapping[3];41struct snd_soc_dapm_route mic_map[2];42};4344static const unsigned int channels[] = {45DUAL_CHANNEL,46};4748static const struct snd_pcm_hw_constraint_list constraints_channels = {49.count = ARRAY_SIZE(channels),50.list = channels,51.mask = 0,52};5354#define ES83xx_12288_KHZ_MCLK_FREQ (48000 * 256)55#define ES83xx_48_MHZ_MCLK_FREQ (48000 * 1000)5657static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,58struct snd_kcontrol *kcontrol, int event);59static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,60struct snd_kcontrol *kcontrol, int event);6162static int acp3x_es83xx_codec_startup(struct snd_pcm_substream *substream)63{64struct snd_pcm_runtime *runtime;65struct snd_soc_pcm_runtime *rtd;66struct snd_soc_dai *codec_dai;67struct acp3x_es83xx_private *priv;68unsigned int freq;69int ret;7071runtime = substream->runtime;72rtd = snd_soc_substream_to_rtd(substream);73codec_dai = snd_soc_rtd_to_codec(rtd, 0);74priv = get_mach_priv(rtd->card);7576if (priv->quirk & ES83XX_48_MHZ_MCLK) {77dev_dbg(priv->codec_dev, "using a 48Mhz MCLK\n");78freq = ES83xx_48_MHZ_MCLK_FREQ;79} else {80dev_dbg(priv->codec_dev, "using a 12.288Mhz MCLK\n");81freq = ES83xx_12288_KHZ_MCLK_FREQ;82}8384ret = snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_OUT);85if (ret < 0) {86dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);87return ret;88}8990runtime->hw.channels_max = DUAL_CHANNEL;91snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,92&constraints_channels);9394return 0;95}9697static struct snd_soc_jack es83xx_jack;9899static struct snd_soc_jack_pin es83xx_jack_pins[] = {100{101.pin = "Headphone",102.mask = SND_JACK_HEADPHONE,103},104{105.pin = "Headset Mic",106.mask = SND_JACK_MICROPHONE,107},108};109110static const struct snd_soc_dapm_widget acp3x_es83xx_widgets[] = {111SND_SOC_DAPM_SPK("Speaker", NULL),112SND_SOC_DAPM_HP("Headphone", NULL),113SND_SOC_DAPM_MIC("Headset Mic", NULL),114SND_SOC_DAPM_MIC("Internal Mic", NULL),115116SND_SOC_DAPM_SUPPLY("Headphone Power", SND_SOC_NOPM, 0, 0,117acp3x_es83xx_headphone_power_event,118SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),119SND_SOC_DAPM_SUPPLY("Speaker Power", SND_SOC_NOPM, 0, 0,120acp3x_es83xx_speaker_power_event,121SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),122};123124static const struct snd_soc_dapm_route acp3x_es83xx_audio_map[] = {125{"Headphone", NULL, "HPOL"},126{"Headphone", NULL, "HPOR"},127{"Headphone", NULL, "Headphone Power"},128129/*130* There is no separate speaker output instead the speakers are muxed to131* the HP outputs. The mux is controlled Speaker and/or headphone switch.132*/133{"Speaker", NULL, "HPOL"},134{"Speaker", NULL, "HPOR"},135{"Speaker", NULL, "Speaker Power"},136};137138139static const struct snd_kcontrol_new acp3x_es83xx_controls[] = {140SOC_DAPM_PIN_SWITCH("Speaker"),141SOC_DAPM_PIN_SWITCH("Headphone"),142SOC_DAPM_PIN_SWITCH("Headset Mic"),143SOC_DAPM_PIN_SWITCH("Internal Mic"),144};145146static int acp3x_es83xx_configure_widgets(struct snd_soc_card *card)147{148card->dapm_widgets = acp3x_es83xx_widgets;149card->num_dapm_widgets = ARRAY_SIZE(acp3x_es83xx_widgets);150card->controls = acp3x_es83xx_controls;151card->num_controls = ARRAY_SIZE(acp3x_es83xx_controls);152card->dapm_routes = acp3x_es83xx_audio_map;153card->num_dapm_routes = ARRAY_SIZE(acp3x_es83xx_audio_map);154155return 0;156}157158static int acp3x_es83xx_headphone_power_event(struct snd_soc_dapm_widget *w,159struct snd_kcontrol *kcontrol, int event)160{161struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);162struct acp3x_es83xx_private *priv = get_mach_priv(card);163164dev_dbg(priv->codec_dev, "headphone power event = %d\n", event);165if (SND_SOC_DAPM_EVENT_ON(event))166priv->headphone_on = true;167else168priv->headphone_on = false;169170gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);171gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);172173return 0;174}175176static int acp3x_es83xx_speaker_power_event(struct snd_soc_dapm_widget *w,177struct snd_kcontrol *kcontrol, int event)178{179struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);180struct acp3x_es83xx_private *priv = get_mach_priv(card);181182dev_dbg(priv->codec_dev, "speaker power event: %d\n", event);183if (SND_SOC_DAPM_EVENT_ON(event))184priv->speaker_on = true;185else186priv->speaker_on = false;187188gpiod_set_value_cansleep(priv->gpio_speakers, priv->speaker_on);189gpiod_set_value_cansleep(priv->gpio_headphone, priv->headphone_on);190191return 0;192}193194static int acp3x_es83xx_suspend_pre(struct snd_soc_card *card)195{196struct acp3x_es83xx_private *priv = get_mach_priv(card);197198/* We need to disable the jack in the machine driver suspend199* callback so that the CODEC suspend callback actually gets200* called. Without doing it, the CODEC suspend/resume201* callbacks do not get called if headphones are plugged in.202* This is because plugging in headphones keeps some supplies203* active, this in turn means that the lowest bias level204* that the CODEC can go to is SND_SOC_BIAS_STANDBY.205* If components do not set idle_bias_on to true then206* their suspend/resume callbacks do not get called.207*/208dev_dbg(priv->codec_dev, "card suspend\n");209snd_soc_component_set_jack(priv->codec, NULL, NULL);210return 0;211}212213static int acp3x_es83xx_resume_post(struct snd_soc_card *card)214{215struct acp3x_es83xx_private *priv = get_mach_priv(card);216217/* We disabled jack detection in suspend callback,218* enable it back.219*/220dev_dbg(priv->codec_dev, "card resume\n");221snd_soc_component_set_jack(priv->codec, &es83xx_jack, NULL);222return 0;223}224225static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)226{227228priv->enable_spk_gpio.crs_entry_index = 0;229priv->enable_hp_gpio.crs_entry_index = 1;230231priv->enable_spk_gpio.active_low = false;232priv->enable_hp_gpio.active_low = false;233234priv->gpio_mapping[0].name = "speakers-enable-gpios";235priv->gpio_mapping[0].data = &priv->enable_spk_gpio;236priv->gpio_mapping[0].size = 1;237priv->gpio_mapping[0].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;238239priv->gpio_mapping[1].name = "headphone-enable-gpios";240priv->gpio_mapping[1].data = &priv->enable_hp_gpio;241priv->gpio_mapping[1].size = 1;242priv->gpio_mapping[1].quirks = ACPI_GPIO_QUIRK_ONLY_GPIOIO;243244dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n",245priv->enable_spk_gpio.crs_entry_index,246str_low_high(priv->enable_spk_gpio.active_low),247priv->enable_hp_gpio.crs_entry_index,248str_low_high(priv->enable_hp_gpio.active_low));249return 0;250}251252static int acp3x_es83xx_configure_mics(struct acp3x_es83xx_private *priv)253{254int num_routes = 0;255int i;256257if (!(priv->quirk & ES83XX_ENABLE_DMIC)) {258priv->mic_map[num_routes].sink = "MIC1";259priv->mic_map[num_routes].source = "Internal Mic";260num_routes++;261}262263priv->mic_map[num_routes].sink = "MIC2";264priv->mic_map[num_routes].source = "Headset Mic";265num_routes++;266267for (i = 0; i < num_routes; i++)268dev_info(priv->codec_dev, "%s is %s\n",269priv->mic_map[i].source, priv->mic_map[i].sink);270271return num_routes;272}273274static int acp3x_es83xx_init(struct snd_soc_pcm_runtime *runtime)275{276struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component;277struct snd_soc_card *card = runtime->card;278struct acp3x_es83xx_private *priv = get_mach_priv(card);279int ret = 0;280int num_routes;281282ret = snd_soc_card_jack_new_pins(card, "Headset",283SND_JACK_HEADSET | SND_JACK_BTN_0,284&es83xx_jack, es83xx_jack_pins,285ARRAY_SIZE(es83xx_jack_pins));286if (ret) {287dev_err(card->dev, "jack creation failed %d\n", ret);288return ret;289}290291snd_jack_set_key(es83xx_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);292293snd_soc_component_set_jack(codec, &es83xx_jack, NULL);294295priv->codec = codec;296acp3x_es83xx_configure_gpios(priv);297298ret = devm_acpi_dev_add_driver_gpios(priv->codec_dev, priv->gpio_mapping);299if (ret)300dev_warn(priv->codec_dev, "failed to add speaker gpio\n");301302priv->gpio_speakers = gpiod_get_optional(priv->codec_dev, "speakers-enable",303priv->enable_spk_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);304if (IS_ERR(priv->gpio_speakers)) {305dev_err(priv->codec_dev, "could not get speakers-enable GPIO\n");306return PTR_ERR(priv->gpio_speakers);307}308309priv->gpio_headphone = gpiod_get_optional(priv->codec_dev, "headphone-enable",310priv->enable_hp_gpio.active_low ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH);311if (IS_ERR(priv->gpio_headphone)) {312dev_err(priv->codec_dev, "could not get headphone-enable GPIO\n");313return PTR_ERR(priv->gpio_headphone);314}315316num_routes = acp3x_es83xx_configure_mics(priv);317if (num_routes > 0) {318struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);319320ret = snd_soc_dapm_add_routes(dapm, priv->mic_map, num_routes);321if (ret != 0)322device_remove_software_node(priv->codec_dev);323}324325return ret;326}327328static const struct snd_soc_ops acp3x_es83xx_ops = {329.startup = acp3x_es83xx_codec_startup,330};331332333SND_SOC_DAILINK_DEF(codec,334DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));335336static const struct dmi_system_id acp3x_es83xx_dmi_table[] = {337{338.matches = {339DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),340DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXXW"),341DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),342},343.driver_data = (void *)(ES83XX_ENABLE_DMIC),344},345{346.matches = {347DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),348DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "KLVL-WXX9"),349DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),350},351.driver_data = (void *)(ES83XX_ENABLE_DMIC),352},353{354.matches = {355DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),356DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "BOM-WXX9"),357DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),358},359.driver_data = (void *)(ES83XX_ENABLE_DMIC|ES83XX_48_MHZ_MCLK),360},361{362.matches = {363DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),364DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),365DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1010"),366},367.driver_data = (void *)(ES83XX_ENABLE_DMIC),368},369{370.matches = {371DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),372DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),373DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1020"),374},375.driver_data = (void *)(ES83XX_ENABLE_DMIC),376},377{378.matches = {379DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "HUAWEI"),380DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HVY-WXX9"),381DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "M1040"),382},383.driver_data = (void *)(ES83XX_ENABLE_DMIC),384},385{}386};387388static int acp3x_es83xx_configure_link(struct snd_soc_card *card, struct snd_soc_dai_link *link)389{390link->codecs = codec;391link->num_codecs = ARRAY_SIZE(codec);392link->init = acp3x_es83xx_init;393link->ops = &acp3x_es83xx_ops;394link->dai_fmt = SND_SOC_DAIFMT_I2S395| SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;396397return 0;398}399400static int acp3x_es83xx_probe(struct snd_soc_card *card)401{402int ret = 0;403struct device *dev = card->dev;404const struct dmi_system_id *dmi_id;405406dmi_id = dmi_first_match(acp3x_es83xx_dmi_table);407if (dmi_id && dmi_id->driver_data) {408struct acp3x_es83xx_private *priv;409struct acp_card_drvdata *acp_drvdata;410struct acpi_device *adev;411struct device *codec_dev;412413acp_drvdata = (struct acp_card_drvdata *)card->drvdata;414415dev_info(dev, "matched DMI table with this system, trying to register sound card\n");416417adev = acpi_dev_get_first_match_dev(acp_drvdata->acpi_mach->id, NULL, -1);418if (!adev) {419dev_err(dev, "Error cannot find '%s' dev\n", acp_drvdata->acpi_mach->id);420return -ENXIO;421}422423codec_dev = acpi_get_first_physical_node(adev);424acpi_dev_put(adev);425if (!codec_dev) {426dev_warn(dev, "Error cannot find codec device, will defer probe\n");427return -EPROBE_DEFER;428}429430priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);431if (!priv) {432put_device(codec_dev);433return -ENOMEM;434}435436priv->codec_dev = codec_dev;437priv->quirk = (unsigned long)dmi_id->driver_data;438acp_drvdata->mach_priv = priv;439dev_info(dev, "successfully probed the sound card\n");440} else {441ret = -ENODEV;442dev_warn(dev, "this system has a ES83xx codec defined in ACPI, but the driver doesn't have this system registered in DMI table\n");443}444return ret;445}446447448void acp3x_es83xx_init_ops(struct acp_mach_ops *ops)449{450ops->probe = acp3x_es83xx_probe;451ops->configure_widgets = acp3x_es83xx_configure_widgets;452ops->configure_link = acp3x_es83xx_configure_link;453ops->suspend_pre = acp3x_es83xx_suspend_pre;454ops->resume_post = acp3x_es83xx_resume_post;455}456457458