brintos

brintos / linux-shallow public Read only

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