18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * max98357a.c -- MAX98357A ALSA SoC Codec driver
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/acpi.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/gpio.h>
128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <sound/pcm.h>
198c2ecf20Sopenharmony_ci#include <sound/soc.h>
208c2ecf20Sopenharmony_ci#include <sound/soc-dai.h>
218c2ecf20Sopenharmony_ci#include <sound/soc-dapm.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct max98357a_priv {
248c2ecf20Sopenharmony_ci	struct gpio_desc *sdmode;
258c2ecf20Sopenharmony_ci	unsigned int sdmode_delay;
268c2ecf20Sopenharmony_ci	int sdmode_switch;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
308c2ecf20Sopenharmony_ci		int cmd, struct snd_soc_dai *dai)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct snd_soc_component *component = dai->component;
338c2ecf20Sopenharmony_ci	struct max98357a_priv *max98357a =
348c2ecf20Sopenharmony_ci		snd_soc_component_get_drvdata(component);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (!max98357a->sdmode)
378c2ecf20Sopenharmony_ci		return 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	switch (cmd) {
408c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
418c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
428c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
438c2ecf20Sopenharmony_ci		mdelay(max98357a->sdmode_delay);
448c2ecf20Sopenharmony_ci		if (max98357a->sdmode_switch) {
458c2ecf20Sopenharmony_ci			gpiod_set_value(max98357a->sdmode, 1);
468c2ecf20Sopenharmony_ci			dev_dbg(component->dev, "set sdmode to 1");
478c2ecf20Sopenharmony_ci		}
488c2ecf20Sopenharmony_ci		break;
498c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
508c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
518c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
528c2ecf20Sopenharmony_ci		gpiod_set_value(max98357a->sdmode, 0);
538c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "set sdmode to 0");
548c2ecf20Sopenharmony_ci		break;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int max98357a_sdmode_event(struct snd_soc_dapm_widget *w,
618c2ecf20Sopenharmony_ci		struct snd_kcontrol *kcontrol, int event)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct snd_soc_component *component =
648c2ecf20Sopenharmony_ci		snd_soc_dapm_to_component(w->dapm);
658c2ecf20Sopenharmony_ci	struct max98357a_priv *max98357a =
668c2ecf20Sopenharmony_ci		snd_soc_component_get_drvdata(component);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (event & SND_SOC_DAPM_POST_PMU)
698c2ecf20Sopenharmony_ci		max98357a->sdmode_switch = 1;
708c2ecf20Sopenharmony_ci	else if (event & SND_SOC_DAPM_POST_PMD)
718c2ecf20Sopenharmony_ci		max98357a->sdmode_switch = 0;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = {
778c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUTPUT("Speaker"),
788c2ecf20Sopenharmony_ci	SND_SOC_DAPM_OUT_DRV_E("SD_MODE", SND_SOC_NOPM, 0, 0, NULL, 0,
798c2ecf20Sopenharmony_ci			max98357a_sdmode_event,
808c2ecf20Sopenharmony_ci			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route max98357a_dapm_routes[] = {
848c2ecf20Sopenharmony_ci	{"SD_MODE", NULL, "HiFi Playback"},
858c2ecf20Sopenharmony_ci	{"Speaker", NULL, "SD_MODE"},
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver max98357a_component_driver = {
898c2ecf20Sopenharmony_ci	.dapm_widgets		= max98357a_dapm_widgets,
908c2ecf20Sopenharmony_ci	.num_dapm_widgets	= ARRAY_SIZE(max98357a_dapm_widgets),
918c2ecf20Sopenharmony_ci	.dapm_routes		= max98357a_dapm_routes,
928c2ecf20Sopenharmony_ci	.num_dapm_routes	= ARRAY_SIZE(max98357a_dapm_routes),
938c2ecf20Sopenharmony_ci	.idle_bias_on		= 1,
948c2ecf20Sopenharmony_ci	.use_pmdown_time	= 1,
958c2ecf20Sopenharmony_ci	.endianness		= 1,
968c2ecf20Sopenharmony_ci	.non_legacy_dai_naming	= 1,
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops max98357a_dai_ops = {
1008c2ecf20Sopenharmony_ci	.trigger        = max98357a_daiops_trigger,
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver max98357a_dai_driver = {
1048c2ecf20Sopenharmony_ci	.name = "HiFi",
1058c2ecf20Sopenharmony_ci	.playback = {
1068c2ecf20Sopenharmony_ci		.stream_name	= "HiFi Playback",
1078c2ecf20Sopenharmony_ci		.formats	= SNDRV_PCM_FMTBIT_S16 |
1088c2ecf20Sopenharmony_ci					SNDRV_PCM_FMTBIT_S24 |
1098c2ecf20Sopenharmony_ci					SNDRV_PCM_FMTBIT_S32,
1108c2ecf20Sopenharmony_ci		.rates		= SNDRV_PCM_RATE_8000 |
1118c2ecf20Sopenharmony_ci					SNDRV_PCM_RATE_16000 |
1128c2ecf20Sopenharmony_ci					SNDRV_PCM_RATE_32000 |
1138c2ecf20Sopenharmony_ci					SNDRV_PCM_RATE_44100 |
1148c2ecf20Sopenharmony_ci					SNDRV_PCM_RATE_48000 |
1158c2ecf20Sopenharmony_ci					SNDRV_PCM_RATE_88200 |
1168c2ecf20Sopenharmony_ci					SNDRV_PCM_RATE_96000,
1178c2ecf20Sopenharmony_ci		.rate_min	= 8000,
1188c2ecf20Sopenharmony_ci		.rate_max	= 96000,
1198c2ecf20Sopenharmony_ci		.channels_min	= 1,
1208c2ecf20Sopenharmony_ci		.channels_max	= 2,
1218c2ecf20Sopenharmony_ci	},
1228c2ecf20Sopenharmony_ci	.ops    = &max98357a_dai_ops,
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int max98357a_platform_probe(struct platform_device *pdev)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct max98357a_priv *max98357a;
1288c2ecf20Sopenharmony_ci	int ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	max98357a = devm_kzalloc(&pdev->dev, sizeof(*max98357a), GFP_KERNEL);
1318c2ecf20Sopenharmony_ci	if (!max98357a)
1328c2ecf20Sopenharmony_ci		return -ENOMEM;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	max98357a->sdmode = devm_gpiod_get_optional(&pdev->dev,
1358c2ecf20Sopenharmony_ci				"sdmode", GPIOD_OUT_LOW);
1368c2ecf20Sopenharmony_ci	if (IS_ERR(max98357a->sdmode))
1378c2ecf20Sopenharmony_ci		return PTR_ERR(max98357a->sdmode);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ret = device_property_read_u32(&pdev->dev, "sdmode-delay",
1408c2ecf20Sopenharmony_ci					&max98357a->sdmode_delay);
1418c2ecf20Sopenharmony_ci	if (ret) {
1428c2ecf20Sopenharmony_ci		max98357a->sdmode_delay = 0;
1438c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev,
1448c2ecf20Sopenharmony_ci			"no optional property 'sdmode-delay' found, "
1458c2ecf20Sopenharmony_ci			"default: no delay\n");
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	dev_set_drvdata(&pdev->dev, max98357a);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return devm_snd_soc_register_component(&pdev->dev,
1518c2ecf20Sopenharmony_ci			&max98357a_component_driver,
1528c2ecf20Sopenharmony_ci			&max98357a_dai_driver, 1);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
1568c2ecf20Sopenharmony_cistatic const struct of_device_id max98357a_device_id[] = {
1578c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max98357a" },
1588c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max98360a" },
1598c2ecf20Sopenharmony_ci	{}
1608c2ecf20Sopenharmony_ci};
1618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max98357a_device_id);
1628c2ecf20Sopenharmony_ci#endif
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
1658c2ecf20Sopenharmony_cistatic const struct acpi_device_id max98357a_acpi_match[] = {
1668c2ecf20Sopenharmony_ci	{ "MX98357A", 0 },
1678c2ecf20Sopenharmony_ci	{ "MX98360A", 0 },
1688c2ecf20Sopenharmony_ci	{},
1698c2ecf20Sopenharmony_ci};
1708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, max98357a_acpi_match);
1718c2ecf20Sopenharmony_ci#endif
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic struct platform_driver max98357a_platform_driver = {
1748c2ecf20Sopenharmony_ci	.driver = {
1758c2ecf20Sopenharmony_ci		.name = "max98357a",
1768c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(max98357a_device_id),
1778c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(max98357a_acpi_match),
1788c2ecf20Sopenharmony_ci	},
1798c2ecf20Sopenharmony_ci	.probe	= max98357a_platform_probe,
1808c2ecf20Sopenharmony_ci};
1818c2ecf20Sopenharmony_cimodule_platform_driver(max98357a_platform_driver);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX98357A Codec Driver");
1848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
185