162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * sam9g20_wm8731  --  SoC audio for AT91SAM9G20-based
462306a36Sopenharmony_ci * 			ATMEL AT91SAM9G20ek board.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (C) 2005 SAN People
762306a36Sopenharmony_ci *  Copyright (C) 2008 Atmel
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Based on ati_b1_wm8731.c by:
1262306a36Sopenharmony_ci * Frank Mandarino <fmandarino@endrelia.com>
1362306a36Sopenharmony_ci * Copyright 2006 Endrelia Technologies Inc.
1462306a36Sopenharmony_ci * Based on corgi.c by:
1562306a36Sopenharmony_ci * Copyright 2005 Wolfson Microelectronics PLC.
1662306a36Sopenharmony_ci * Copyright 2005 Openedhand Ltd.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/moduleparam.h>
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/clk.h>
2362306a36Sopenharmony_ci#include <linux/timer.h>
2462306a36Sopenharmony_ci#include <linux/interrupt.h>
2562306a36Sopenharmony_ci#include <linux/platform_device.h>
2662306a36Sopenharmony_ci#include <linux/of.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/atmel-ssc.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <sound/core.h>
3162306a36Sopenharmony_ci#include <sound/pcm.h>
3262306a36Sopenharmony_ci#include <sound/pcm_params.h>
3362306a36Sopenharmony_ci#include <sound/soc.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "../codecs/wm8731.h"
3662306a36Sopenharmony_ci#include "atmel-pcm.h"
3762306a36Sopenharmony_ci#include "atmel_ssc_dai.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define MCLK_RATE 12000000
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * As shipped the board does not have inputs.  However, it is relatively
4362306a36Sopenharmony_ci * straightforward to modify the board to hook them up so support is left
4462306a36Sopenharmony_ci * in the driver.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci#undef ENABLE_MIC_INPUT
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
4962306a36Sopenharmony_ci	SND_SOC_DAPM_MIC("Int Mic", NULL),
5062306a36Sopenharmony_ci	SND_SOC_DAPM_SPK("Ext Spk", NULL),
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic const struct snd_soc_dapm_route intercon[] = {
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* speaker connected to LHPOUT/RHPOUT */
5662306a36Sopenharmony_ci	{"Ext Spk", NULL, "LHPOUT"},
5762306a36Sopenharmony_ci	{"Ext Spk", NULL, "RHPOUT"},
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* mic is connected to Mic Jack, with WM8731 Mic Bias */
6062306a36Sopenharmony_ci	{"MICIN", NULL, "Mic Bias"},
6162306a36Sopenharmony_ci	{"Mic Bias", NULL, "Int Mic"},
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * Logic for a wm8731 as connected on a at91sam9g20ek board.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
7062306a36Sopenharmony_ci	struct device *dev = rtd->dev;
7162306a36Sopenharmony_ci	int ret;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	dev_dbg(dev, "%s called\n", __func__);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,
7662306a36Sopenharmony_ci				     MCLK_RATE, SND_SOC_CLOCK_IN);
7762306a36Sopenharmony_ci	if (ret < 0) {
7862306a36Sopenharmony_ci		dev_err(dev, "Failed to set WM8731 SYSCLK: %d\n", ret);
7962306a36Sopenharmony_ci		return ret;
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#ifndef ENABLE_MIC_INPUT
8362306a36Sopenharmony_ci	snd_soc_dapm_nc_pin(&rtd->card->dapm, "Int Mic");
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciSND_SOC_DAILINK_DEFS(pcm,
9062306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CPU("at91rm9200_ssc.0")),
9162306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_CODEC("wm8731.0-001b", "wm8731-hifi")),
9262306a36Sopenharmony_ci	DAILINK_COMP_ARRAY(COMP_PLATFORM("at91rm9200_ssc.0")));
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic struct snd_soc_dai_link at91sam9g20ek_dai = {
9562306a36Sopenharmony_ci	.name = "WM8731",
9662306a36Sopenharmony_ci	.stream_name = "WM8731 PCM",
9762306a36Sopenharmony_ci	.init = at91sam9g20ek_wm8731_init,
9862306a36Sopenharmony_ci	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
9962306a36Sopenharmony_ci		   SND_SOC_DAIFMT_CBP_CFP,
10062306a36Sopenharmony_ci#ifndef ENABLE_MIC_INPUT
10162306a36Sopenharmony_ci	.playback_only = true,
10262306a36Sopenharmony_ci#endif
10362306a36Sopenharmony_ci	SND_SOC_DAILINK_REG(pcm),
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic struct snd_soc_card snd_soc_at91sam9g20ek = {
10762306a36Sopenharmony_ci	.name = "AT91SAMG20-EK",
10862306a36Sopenharmony_ci	.owner = THIS_MODULE,
10962306a36Sopenharmony_ci	.dai_link = &at91sam9g20ek_dai,
11062306a36Sopenharmony_ci	.num_links = 1,
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	.dapm_widgets = at91sam9g20ek_dapm_widgets,
11362306a36Sopenharmony_ci	.num_dapm_widgets = ARRAY_SIZE(at91sam9g20ek_dapm_widgets),
11462306a36Sopenharmony_ci	.dapm_routes = intercon,
11562306a36Sopenharmony_ci	.num_dapm_routes = ARRAY_SIZE(intercon),
11662306a36Sopenharmony_ci	.fully_routed = true,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int at91sam9g20ek_audio_probe(struct platform_device *pdev)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
12262306a36Sopenharmony_ci	struct device_node *codec_np, *cpu_np;
12362306a36Sopenharmony_ci	struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (!np) {
12762306a36Sopenharmony_ci		return -ENODEV;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	ret = atmel_ssc_set_audio(0);
13162306a36Sopenharmony_ci	if (ret) {
13262306a36Sopenharmony_ci		dev_err(&pdev->dev, "ssc channel is not valid: %d\n", ret);
13362306a36Sopenharmony_ci		return ret;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	card->dev = &pdev->dev;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Parse device node info */
13962306a36Sopenharmony_ci	ret = snd_soc_of_parse_card_name(card, "atmel,model");
14062306a36Sopenharmony_ci	if (ret)
14162306a36Sopenharmony_ci		goto err;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	ret = snd_soc_of_parse_audio_routing(card,
14462306a36Sopenharmony_ci		"atmel,audio-routing");
14562306a36Sopenharmony_ci	if (ret)
14662306a36Sopenharmony_ci		goto err;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* Parse codec info */
14962306a36Sopenharmony_ci	at91sam9g20ek_dai.codecs->name = NULL;
15062306a36Sopenharmony_ci	codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
15162306a36Sopenharmony_ci	if (!codec_np) {
15262306a36Sopenharmony_ci		dev_err(&pdev->dev, "codec info missing\n");
15362306a36Sopenharmony_ci		ret = -EINVAL;
15462306a36Sopenharmony_ci		goto err;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	at91sam9g20ek_dai.codecs->of_node = codec_np;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* Parse dai and platform info */
15962306a36Sopenharmony_ci	at91sam9g20ek_dai.cpus->dai_name = NULL;
16062306a36Sopenharmony_ci	at91sam9g20ek_dai.platforms->name = NULL;
16162306a36Sopenharmony_ci	cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
16262306a36Sopenharmony_ci	if (!cpu_np) {
16362306a36Sopenharmony_ci		dev_err(&pdev->dev, "dai and pcm info missing\n");
16462306a36Sopenharmony_ci		of_node_put(codec_np);
16562306a36Sopenharmony_ci		ret = -EINVAL;
16662306a36Sopenharmony_ci		goto err;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci	at91sam9g20ek_dai.cpus->of_node = cpu_np;
16962306a36Sopenharmony_ci	at91sam9g20ek_dai.platforms->of_node = cpu_np;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	of_node_put(codec_np);
17262306a36Sopenharmony_ci	of_node_put(cpu_np);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	ret = snd_soc_register_card(card);
17562306a36Sopenharmony_ci	if (ret) {
17662306a36Sopenharmony_ci		dev_err_probe(&pdev->dev, ret,
17762306a36Sopenharmony_ci			      "snd_soc_register_card() failed\n");
17862306a36Sopenharmony_ci		goto err;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cierr:
18462306a36Sopenharmony_ci	atmel_ssc_put_audio(0);
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void at91sam9g20ek_audio_remove(struct platform_device *pdev)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct snd_soc_card *card = platform_get_drvdata(pdev);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	snd_soc_unregister_card(card);
19362306a36Sopenharmony_ci	atmel_ssc_put_audio(0);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci#ifdef CONFIG_OF
19762306a36Sopenharmony_cistatic const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
19862306a36Sopenharmony_ci	{ .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
19962306a36Sopenharmony_ci	{ }
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
20262306a36Sopenharmony_ci#endif
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic struct platform_driver at91sam9g20ek_audio_driver = {
20562306a36Sopenharmony_ci	.driver = {
20662306a36Sopenharmony_ci		.name	= "at91sam9g20ek-audio",
20762306a36Sopenharmony_ci		.of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
20862306a36Sopenharmony_ci	},
20962306a36Sopenharmony_ci	.probe	= at91sam9g20ek_audio_probe,
21062306a36Sopenharmony_ci	.remove_new = at91sam9g20ek_audio_remove,
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cimodule_platform_driver(at91sam9g20ek_audio_driver);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/* Module information */
21662306a36Sopenharmony_ciMODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
21762306a36Sopenharmony_ciMODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
21862306a36Sopenharmony_ciMODULE_ALIAS("platform:at91sam9g20ek-audio");
21962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
220