18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/sound/soc/pxa/z2.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * SoC Audio driver for Aeronix Zipit Z2
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Ken McGuire <kenm@desertweyr.com>
88c2ecf20Sopenharmony_ci * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
138c2ecf20Sopenharmony_ci#include <linux/timer.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/gpio.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <sound/core.h>
198c2ecf20Sopenharmony_ci#include <sound/pcm.h>
208c2ecf20Sopenharmony_ci#include <sound/soc.h>
218c2ecf20Sopenharmony_ci#include <sound/jack.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
248c2ecf20Sopenharmony_ci#include <mach/hardware.h>
258c2ecf20Sopenharmony_ci#include <mach/audio.h>
268c2ecf20Sopenharmony_ci#include <mach/z2.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "../codecs/wm8750.h"
298c2ecf20Sopenharmony_ci#include "pxa2xx-i2s.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic struct snd_soc_card snd_soc_z2;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int z2_hw_params(struct snd_pcm_substream *substream,
348c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
378c2ecf20Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
388c2ecf20Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
398c2ecf20Sopenharmony_ci	unsigned int clk = 0;
408c2ecf20Sopenharmony_ci	int ret = 0;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	switch (params_rate(params)) {
438c2ecf20Sopenharmony_ci	case 8000:
448c2ecf20Sopenharmony_ci	case 16000:
458c2ecf20Sopenharmony_ci	case 48000:
468c2ecf20Sopenharmony_ci	case 96000:
478c2ecf20Sopenharmony_ci		clk = 12288000;
488c2ecf20Sopenharmony_ci		break;
498c2ecf20Sopenharmony_ci	case 11025:
508c2ecf20Sopenharmony_ci	case 22050:
518c2ecf20Sopenharmony_ci	case 44100:
528c2ecf20Sopenharmony_ci		clk = 11289600;
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* set the codec system clock for DAC and ADC */
578c2ecf20Sopenharmony_ci	ret = snd_soc_dai_set_sysclk(codec_dai, WM8750_SYSCLK, clk,
588c2ecf20Sopenharmony_ci		SND_SOC_CLOCK_IN);
598c2ecf20Sopenharmony_ci	if (ret < 0)
608c2ecf20Sopenharmony_ci		return ret;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* set the I2S system clock as input (unused) */
638c2ecf20Sopenharmony_ci	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
648c2ecf20Sopenharmony_ci		SND_SOC_CLOCK_IN);
658c2ecf20Sopenharmony_ci	if (ret < 0)
668c2ecf20Sopenharmony_ci		return ret;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic struct snd_soc_jack hs_jack;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* Headset jack detection DAPM pins */
748c2ecf20Sopenharmony_cistatic struct snd_soc_jack_pin hs_jack_pins[] = {
758c2ecf20Sopenharmony_ci	{
768c2ecf20Sopenharmony_ci		.pin	= "Mic Jack",
778c2ecf20Sopenharmony_ci		.mask	= SND_JACK_MICROPHONE,
788c2ecf20Sopenharmony_ci	},
798c2ecf20Sopenharmony_ci	{
808c2ecf20Sopenharmony_ci		.pin	= "Headphone Jack",
818c2ecf20Sopenharmony_ci		.mask	= SND_JACK_HEADPHONE,
828c2ecf20Sopenharmony_ci	},
838c2ecf20Sopenharmony_ci	{
848c2ecf20Sopenharmony_ci		.pin    = "Ext Spk",
858c2ecf20Sopenharmony_ci		.mask   = SND_JACK_HEADPHONE,
868c2ecf20Sopenharmony_ci		.invert = 1
878c2ecf20Sopenharmony_ci	},
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* Headset jack detection gpios */
918c2ecf20Sopenharmony_cistatic struct snd_soc_jack_gpio hs_jack_gpios[] = {
928c2ecf20Sopenharmony_ci	{
938c2ecf20Sopenharmony_ci		.gpio		= GPIO37_ZIPITZ2_HEADSET_DETECT,
948c2ecf20Sopenharmony_ci		.name		= "hsdet-gpio",
958c2ecf20Sopenharmony_ci		.report		= SND_JACK_HEADSET,
968c2ecf20Sopenharmony_ci		.debounce_time	= 200,
978c2ecf20Sopenharmony_ci		.invert		= 1,
988c2ecf20Sopenharmony_ci	},
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* z2 machine dapm widgets */
1028c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8750_dapm_widgets[] = {
1038c2ecf20Sopenharmony_ci	SND_SOC_DAPM_HP("Headphone Jack", NULL),
1048c2ecf20Sopenharmony_ci	SND_SOC_DAPM_MIC("Mic Jack", NULL),
1058c2ecf20Sopenharmony_ci	SND_SOC_DAPM_SPK("Ext Spk", NULL),
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* headset is a mic and mono headphone */
1088c2ecf20Sopenharmony_ci	SND_SOC_DAPM_HP("Headset Jack", NULL),
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* Z2 machine audio_map */
1128c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route z2_audio_map[] = {
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* headphone connected to LOUT1, ROUT1 */
1158c2ecf20Sopenharmony_ci	{"Headphone Jack", NULL, "LOUT1"},
1168c2ecf20Sopenharmony_ci	{"Headphone Jack", NULL, "ROUT1"},
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* ext speaker connected to LOUT2, ROUT2  */
1198c2ecf20Sopenharmony_ci	{"Ext Spk", NULL, "ROUT2"},
1208c2ecf20Sopenharmony_ci	{"Ext Spk", NULL, "LOUT2"},
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* mic is connected to R input 2 - with bias */
1238c2ecf20Sopenharmony_ci	{"RINPUT2", NULL, "Mic Bias"},
1248c2ecf20Sopenharmony_ci	{"Mic Bias", NULL, "Mic Jack"},
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/*
1288c2ecf20Sopenharmony_ci * Logic for a wm8750 as connected on a Z2 Device
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_cistatic int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Jack detection API stuff */
1358c2ecf20Sopenharmony_ci	ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
1368c2ecf20Sopenharmony_ci				    &hs_jack, hs_jack_pins,
1378c2ecf20Sopenharmony_ci				    ARRAY_SIZE(hs_jack_pins));
1388c2ecf20Sopenharmony_ci	if (ret)
1398c2ecf20Sopenharmony_ci		goto err;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
1428c2ecf20Sopenharmony_ci				hs_jack_gpios);
1438c2ecf20Sopenharmony_ci	if (ret)
1448c2ecf20Sopenharmony_ci		goto err;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return 0;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cierr:
1498c2ecf20Sopenharmony_ci	return ret;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic const struct snd_soc_ops z2_ops = {
1538c2ecf20Sopenharmony_ci	.hw_params = z2_hw_params,
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* z2 digital audio interface glue - connects codec <--> CPU */
1578c2ecf20Sopenharmony_ciSND_SOC_DAILINK_DEFS(wm8750,
1588c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CPU("pxa2xx-i2s")),
1598c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC("wm8750.0-001b", "wm8750-hifi")),
1608c2ecf20Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_PLATFORM("pxa-pcm-audio")));
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic struct snd_soc_dai_link z2_dai = {
1638c2ecf20Sopenharmony_ci	.name		= "wm8750",
1648c2ecf20Sopenharmony_ci	.stream_name	= "WM8750",
1658c2ecf20Sopenharmony_ci	.init		= z2_wm8750_init,
1668c2ecf20Sopenharmony_ci	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
1678c2ecf20Sopenharmony_ci			  SND_SOC_DAIFMT_CBS_CFS,
1688c2ecf20Sopenharmony_ci	.ops		= &z2_ops,
1698c2ecf20Sopenharmony_ci	SND_SOC_DAILINK_REG(wm8750),
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/* z2 audio machine driver */
1738c2ecf20Sopenharmony_cistatic struct snd_soc_card snd_soc_z2 = {
1748c2ecf20Sopenharmony_ci	.name		= "Z2",
1758c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1768c2ecf20Sopenharmony_ci	.dai_link	= &z2_dai,
1778c2ecf20Sopenharmony_ci	.num_links	= 1,
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	.dapm_widgets = wm8750_dapm_widgets,
1808c2ecf20Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
1818c2ecf20Sopenharmony_ci	.dapm_routes = z2_audio_map,
1828c2ecf20Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
1838c2ecf20Sopenharmony_ci	.fully_routed = true,
1848c2ecf20Sopenharmony_ci};
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic struct platform_device *z2_snd_device;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int __init z2_init(void)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	int ret;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!machine_is_zipit2())
1938c2ecf20Sopenharmony_ci		return -ENODEV;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	z2_snd_device = platform_device_alloc("soc-audio", -1);
1968c2ecf20Sopenharmony_ci	if (!z2_snd_device)
1978c2ecf20Sopenharmony_ci		return -ENOMEM;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	platform_set_drvdata(z2_snd_device, &snd_soc_z2);
2008c2ecf20Sopenharmony_ci	ret = platform_device_add(z2_snd_device);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (ret)
2038c2ecf20Sopenharmony_ci		platform_device_put(z2_snd_device);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return ret;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void __exit z2_exit(void)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	platform_device_unregister(z2_snd_device);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cimodule_init(z2_init);
2148c2ecf20Sopenharmony_cimodule_exit(z2_exit);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ken McGuire <kenm@desertweyr.com>, "
2178c2ecf20Sopenharmony_ci		"Marek Vasut <marek.vasut@gmail.com>");
2188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC ZipitZ2");
2198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
220