19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
286059653Szhengxing /*
386059653Szhengxing  * Rockchip machine ASoC driver for boards using a RT5645/RT5650 CODEC.
486059653Szhengxing  *
586059653Szhengxing  * Copyright (c) 2015, ROCKCHIP CORPORATION.  All rights reserved.
686059653Szhengxing  */
786059653Szhengxing 
886059653Szhengxing #include <linux/module.h>
986059653Szhengxing #include <linux/platform_device.h>
1086059653Szhengxing #include <linux/slab.h>
1186059653Szhengxing #include <linux/gpio.h>
1286059653Szhengxing #include <linux/of_gpio.h>
1386059653Szhengxing #include <linux/delay.h>
1486059653Szhengxing #include <sound/core.h>
1586059653Szhengxing #include <sound/jack.h>
1686059653Szhengxing #include <sound/pcm.h>
1786059653Szhengxing #include <sound/pcm_params.h>
1886059653Szhengxing #include <sound/soc.h>
1986059653Szhengxing #include "rockchip_i2s.h"
2079223bf1SKuninori Morimoto #include "../codecs/rt5645.h"
2186059653Szhengxing 
2286059653Szhengxing #define DRV_NAME "rockchip-snd-rt5645"
2386059653Szhengxing 
2486059653Szhengxing static struct snd_soc_jack headset_jack;
25*24127e5aSAlper Nebi Yasak static struct snd_soc_jack_pin headset_jack_pins[] = {
26*24127e5aSAlper Nebi Yasak 	{
27*24127e5aSAlper Nebi Yasak 		.pin = "Headphones",
28*24127e5aSAlper Nebi Yasak 		.mask = SND_JACK_HEADPHONE,
29*24127e5aSAlper Nebi Yasak 	},
30*24127e5aSAlper Nebi Yasak 	{
31*24127e5aSAlper Nebi Yasak 		.pin = "Headset Mic",
32*24127e5aSAlper Nebi Yasak 		.mask = SND_JACK_MICROPHONE,
33*24127e5aSAlper Nebi Yasak 	},
34*24127e5aSAlper Nebi Yasak };
3586059653Szhengxing 
3686059653Szhengxing static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
3786059653Szhengxing 	SND_SOC_DAPM_HP("Headphones", NULL),
3886059653Szhengxing 	SND_SOC_DAPM_SPK("Speakers", NULL),
3986059653Szhengxing 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
4086059653Szhengxing 	SND_SOC_DAPM_MIC("Int Mic", NULL),
4186059653Szhengxing };
4286059653Szhengxing 
4386059653Szhengxing static const struct snd_soc_dapm_route rk_audio_map[] = {
4486059653Szhengxing 	/* Input Lines */
4586059653Szhengxing 	{"DMIC L2", NULL, "Int Mic"},
4686059653Szhengxing 	{"DMIC R2", NULL, "Int Mic"},
4786059653Szhengxing 	{"RECMIXL", NULL, "Headset Mic"},
4886059653Szhengxing 	{"RECMIXR", NULL, "Headset Mic"},
4986059653Szhengxing 
5086059653Szhengxing 	/* Output Lines */
5186059653Szhengxing 	{"Headphones", NULL, "HPOR"},
5286059653Szhengxing 	{"Headphones", NULL, "HPOL"},
5386059653Szhengxing 	{"Speakers", NULL, "SPOL"},
5486059653Szhengxing 	{"Speakers", NULL, "SPOR"},
5586059653Szhengxing };
5686059653Szhengxing 
5786059653Szhengxing static const struct snd_kcontrol_new rk_mc_controls[] = {
5886059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Headphones"),
5986059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Speakers"),
6086059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
6186059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Int Mic"),
6286059653Szhengxing };
6386059653Szhengxing 
rk_aif1_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)6486059653Szhengxing static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
6586059653Szhengxing 			     struct snd_pcm_hw_params *params)
6686059653Szhengxing {
6786059653Szhengxing 	int ret = 0;
685c5eb29eSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
69a7ff5268SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
70a7ff5268SKuninori Morimoto 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
7186059653Szhengxing 	int mclk;
7286059653Szhengxing 
7386059653Szhengxing 	switch (params_rate(params)) {
7486059653Szhengxing 	case 8000:
7586059653Szhengxing 	case 16000:
763a3b070dSCaesar Wang 	case 24000:
773a3b070dSCaesar Wang 	case 32000:
7886059653Szhengxing 	case 48000:
793a3b070dSCaesar Wang 	case 64000:
8086059653Szhengxing 	case 96000:
8186059653Szhengxing 		mclk = 12288000;
8286059653Szhengxing 		break;
833a3b070dSCaesar Wang 	case 11025:
843a3b070dSCaesar Wang 	case 22050:
8586059653Szhengxing 	case 44100:
863a3b070dSCaesar Wang 	case 88200:
8786059653Szhengxing 		mclk = 11289600;
8886059653Szhengxing 		break;
8986059653Szhengxing 	default:
9086059653Szhengxing 		return -EINVAL;
9186059653Szhengxing 	}
9286059653Szhengxing 
9386059653Szhengxing 	ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
9486059653Szhengxing 				     SND_SOC_CLOCK_OUT);
9586059653Szhengxing 	if (ret < 0) {
9686059653Szhengxing 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
9786059653Szhengxing 		return ret;
9886059653Szhengxing 	}
9986059653Szhengxing 
10086059653Szhengxing 	ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
10186059653Szhengxing 				     SND_SOC_CLOCK_IN);
10286059653Szhengxing 	if (ret < 0) {
10386059653Szhengxing 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
10486059653Szhengxing 		return ret;
10586059653Szhengxing 	}
10686059653Szhengxing 
10786059653Szhengxing 	return ret;
10886059653Szhengxing }
10986059653Szhengxing 
rk_init(struct snd_soc_pcm_runtime * runtime)11086059653Szhengxing static int rk_init(struct snd_soc_pcm_runtime *runtime)
11186059653Szhengxing {
11286059653Szhengxing 	struct snd_soc_card *card = runtime->card;
11386059653Szhengxing 	int ret;
11486059653Szhengxing 
11586059653Szhengxing 	/* Enable Headset and 4 Buttons Jack detection */
116*24127e5aSAlper Nebi Yasak 	ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
11786059653Szhengxing 					 SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
11886059653Szhengxing 					 SND_JACK_BTN_0 | SND_JACK_BTN_1 |
11986059653Szhengxing 					 SND_JACK_BTN_2 | SND_JACK_BTN_3,
120*24127e5aSAlper Nebi Yasak 					 &headset_jack,
121*24127e5aSAlper Nebi Yasak 					 headset_jack_pins,
122*24127e5aSAlper Nebi Yasak 					 ARRAY_SIZE(headset_jack_pins));
123f8ce2000SXing Zheng 	if (ret) {
12486059653Szhengxing 		dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
12586059653Szhengxing 		return ret;
12686059653Szhengxing 	}
12786059653Szhengxing 
128a7ff5268SKuninori Morimoto 	return rt5645_set_jack_detect(asoc_rtd_to_codec(runtime, 0)->component,
12986059653Szhengxing 				     &headset_jack,
13086059653Szhengxing 				     &headset_jack,
13186059653Szhengxing 				     &headset_jack);
13286059653Szhengxing }
13386059653Szhengxing 
134705e9994SJulia Lawall static const struct snd_soc_ops rk_aif1_ops = {
13586059653Szhengxing 	.hw_params = rk_aif1_hw_params,
13686059653Szhengxing };
13786059653Szhengxing 
1380209bf2bSKuninori Morimoto SND_SOC_DAILINK_DEFS(pcm,
1390209bf2bSKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_EMPTY()),
140f94d7b6eSKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1")),
141f94d7b6eSKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_EMPTY()));
1420209bf2bSKuninori Morimoto 
14386059653Szhengxing static struct snd_soc_dai_link rk_dailink = {
14486059653Szhengxing 	.name = "rt5645",
14586059653Szhengxing 	.stream_name = "rt5645 PCM",
14686059653Szhengxing 	.init = rk_init,
14786059653Szhengxing 	.ops = &rk_aif1_ops,
14886059653Szhengxing 	/* set rt5645 as slave */
14986059653Szhengxing 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
15086059653Szhengxing 		SND_SOC_DAIFMT_CBS_CFS,
1510209bf2bSKuninori Morimoto 	SND_SOC_DAILINK_REG(pcm),
15286059653Szhengxing };
15386059653Szhengxing 
15486059653Szhengxing static struct snd_soc_card snd_soc_card_rk = {
15586059653Szhengxing 	.name = "I2S-RT5650",
15654d8697fSAxel Lin 	.owner = THIS_MODULE,
15786059653Szhengxing 	.dai_link = &rk_dailink,
15886059653Szhengxing 	.num_links = 1,
15986059653Szhengxing 	.dapm_widgets = rk_dapm_widgets,
16086059653Szhengxing 	.num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
16186059653Szhengxing 	.dapm_routes = rk_audio_map,
16286059653Szhengxing 	.num_dapm_routes = ARRAY_SIZE(rk_audio_map),
16386059653Szhengxing 	.controls = rk_mc_controls,
16486059653Szhengxing 	.num_controls = ARRAY_SIZE(rk_mc_controls),
16586059653Szhengxing };
16686059653Szhengxing 
snd_rk_mc_probe(struct platform_device * pdev)16786059653Szhengxing static int snd_rk_mc_probe(struct platform_device *pdev)
16886059653Szhengxing {
16986059653Szhengxing 	int ret = 0;
17086059653Szhengxing 	struct snd_soc_card *card = &snd_soc_card_rk;
17186059653Szhengxing 	struct device_node *np = pdev->dev.of_node;
17286059653Szhengxing 
17386059653Szhengxing 	/* register the soc card */
17486059653Szhengxing 	card->dev = &pdev->dev;
17586059653Szhengxing 
1760209bf2bSKuninori Morimoto 	rk_dailink.codecs->of_node = of_parse_phandle(np,
17786059653Szhengxing 			"rockchip,audio-codec", 0);
1780209bf2bSKuninori Morimoto 	if (!rk_dailink.codecs->of_node) {
17986059653Szhengxing 		dev_err(&pdev->dev,
18086059653Szhengxing 			"Property 'rockchip,audio-codec' missing or invalid\n");
18186059653Szhengxing 		return -EINVAL;
18286059653Szhengxing 	}
18386059653Szhengxing 
1840209bf2bSKuninori Morimoto 	rk_dailink.cpus->of_node = of_parse_phandle(np,
18586059653Szhengxing 			"rockchip,i2s-controller", 0);
1860209bf2bSKuninori Morimoto 	if (!rk_dailink.cpus->of_node) {
18786059653Szhengxing 		dev_err(&pdev->dev,
18886059653Szhengxing 			"Property 'rockchip,i2s-controller' missing or invalid\n");
189a56df73bSAlexey Khoroshilov 		ret = -EINVAL;
190a56df73bSAlexey Khoroshilov 		goto put_codec_of_node;
19186059653Szhengxing 	}
19286059653Szhengxing 
193f94d7b6eSKuninori Morimoto 	rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
194f94d7b6eSKuninori Morimoto 
19586059653Szhengxing 	ret = snd_soc_of_parse_card_name(card, "rockchip,model");
19686059653Szhengxing 	if (ret) {
19786059653Szhengxing 		dev_err(&pdev->dev,
19886059653Szhengxing 			"Soc parse card name failed %d\n", ret);
199a56df73bSAlexey Khoroshilov 		goto put_cpu_of_node;
20086059653Szhengxing 	}
20186059653Szhengxing 
20286059653Szhengxing 	ret = devm_snd_soc_register_card(&pdev->dev, card);
20386059653Szhengxing 	if (ret) {
20486059653Szhengxing 		dev_err(&pdev->dev,
20586059653Szhengxing 			"Soc register card failed %d\n", ret);
206a56df73bSAlexey Khoroshilov 		goto put_cpu_of_node;
20786059653Szhengxing 	}
20886059653Szhengxing 
20986059653Szhengxing 	return ret;
210a56df73bSAlexey Khoroshilov 
211a56df73bSAlexey Khoroshilov put_cpu_of_node:
2120209bf2bSKuninori Morimoto 	of_node_put(rk_dailink.cpus->of_node);
2130209bf2bSKuninori Morimoto 	rk_dailink.cpus->of_node = NULL;
214a56df73bSAlexey Khoroshilov put_codec_of_node:
2150209bf2bSKuninori Morimoto 	of_node_put(rk_dailink.codecs->of_node);
2160209bf2bSKuninori Morimoto 	rk_dailink.codecs->of_node = NULL;
217a56df73bSAlexey Khoroshilov 
218a56df73bSAlexey Khoroshilov 	return ret;
219a56df73bSAlexey Khoroshilov }
220a56df73bSAlexey Khoroshilov 
snd_rk_mc_remove(struct platform_device * pdev)221ed1e0f5aSUwe Kleine-König static void snd_rk_mc_remove(struct platform_device *pdev)
222a56df73bSAlexey Khoroshilov {
2230209bf2bSKuninori Morimoto 	of_node_put(rk_dailink.cpus->of_node);
2240209bf2bSKuninori Morimoto 	rk_dailink.cpus->of_node = NULL;
2250209bf2bSKuninori Morimoto 	of_node_put(rk_dailink.codecs->of_node);
2260209bf2bSKuninori Morimoto 	rk_dailink.codecs->of_node = NULL;
22786059653Szhengxing }
22886059653Szhengxing 
22986059653Szhengxing static const struct of_device_id rockchip_rt5645_of_match[] = {
23086059653Szhengxing 	{ .compatible = "rockchip,rockchip-audio-rt5645", },
23186059653Szhengxing 	{},
23286059653Szhengxing };
23386059653Szhengxing 
23486059653Szhengxing MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
23586059653Szhengxing 
23686059653Szhengxing static struct platform_driver snd_rk_mc_driver = {
23786059653Szhengxing 	.probe = snd_rk_mc_probe,
238ed1e0f5aSUwe Kleine-König 	.remove_new = snd_rk_mc_remove,
23986059653Szhengxing 	.driver = {
24086059653Szhengxing 		.name = DRV_NAME,
24186059653Szhengxing 		.pm = &snd_soc_pm_ops,
24286059653Szhengxing 		.of_match_table = rockchip_rt5645_of_match,
24386059653Szhengxing 	},
24486059653Szhengxing };
24586059653Szhengxing 
24686059653Szhengxing module_platform_driver(snd_rk_mc_driver);
24786059653Szhengxing 
24886059653Szhengxing MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
24986059653Szhengxing MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver");
25086059653Szhengxing MODULE_LICENSE("GPL v2");
25186059653Szhengxing MODULE_ALIAS("platform:" DRV_NAME);
252