186059653Szhengxing /*
286059653Szhengxing  * Rockchip machine ASoC driver for boards using a RT5645/RT5650 CODEC.
386059653Szhengxing  *
486059653Szhengxing  * Copyright (c) 2015, ROCKCHIP CORPORATION.  All rights reserved.
586059653Szhengxing  *
686059653Szhengxing  * This program is free software; you can redistribute it and/or modify it
786059653Szhengxing  * under the terms and conditions of the GNU General Public License,
886059653Szhengxing  * version 2, as published by the Free Software Foundation.
986059653Szhengxing  *
1086059653Szhengxing  * This program is distributed in the hope it will be useful, but WITHOUT
1186059653Szhengxing  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1286059653Szhengxing  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1386059653Szhengxing  * more details.
1486059653Szhengxing  *
1586059653Szhengxing  * You should have received a copy of the GNU General Public License
1686059653Szhengxing  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
1786059653Szhengxing  *
1886059653Szhengxing  */
1986059653Szhengxing 
2086059653Szhengxing #include <linux/module.h>
2186059653Szhengxing #include <linux/platform_device.h>
2286059653Szhengxing #include <linux/slab.h>
2386059653Szhengxing #include <linux/gpio.h>
2486059653Szhengxing #include <linux/of_gpio.h>
2586059653Szhengxing #include <linux/delay.h>
2686059653Szhengxing #include <sound/core.h>
2786059653Szhengxing #include <sound/jack.h>
2886059653Szhengxing #include <sound/pcm.h>
2986059653Szhengxing #include <sound/pcm_params.h>
3086059653Szhengxing #include <sound/soc.h>
3186059653Szhengxing #include "rockchip_i2s.h"
3279223bf1SKuninori Morimoto #include "../codecs/rt5645.h"
3386059653Szhengxing 
3486059653Szhengxing #define DRV_NAME "rockchip-snd-rt5645"
3586059653Szhengxing 
3686059653Szhengxing static struct snd_soc_jack headset_jack;
3786059653Szhengxing 
3886059653Szhengxing static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
3986059653Szhengxing 	SND_SOC_DAPM_HP("Headphones", NULL),
4086059653Szhengxing 	SND_SOC_DAPM_SPK("Speakers", NULL),
4186059653Szhengxing 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
4286059653Szhengxing 	SND_SOC_DAPM_MIC("Int Mic", NULL),
4386059653Szhengxing };
4486059653Szhengxing 
4586059653Szhengxing static const struct snd_soc_dapm_route rk_audio_map[] = {
4686059653Szhengxing 	/* Input Lines */
4786059653Szhengxing 	{"DMIC L2", NULL, "Int Mic"},
4886059653Szhengxing 	{"DMIC R2", NULL, "Int Mic"},
4986059653Szhengxing 	{"RECMIXL", NULL, "Headset Mic"},
5086059653Szhengxing 	{"RECMIXR", NULL, "Headset Mic"},
5186059653Szhengxing 
5286059653Szhengxing 	/* Output Lines */
5386059653Szhengxing 	{"Headphones", NULL, "HPOR"},
5486059653Szhengxing 	{"Headphones", NULL, "HPOL"},
5586059653Szhengxing 	{"Speakers", NULL, "SPOL"},
5686059653Szhengxing 	{"Speakers", NULL, "SPOR"},
5786059653Szhengxing };
5886059653Szhengxing 
5986059653Szhengxing static const struct snd_kcontrol_new rk_mc_controls[] = {
6086059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Headphones"),
6186059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Speakers"),
6286059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
6386059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Int Mic"),
6486059653Szhengxing };
6586059653Szhengxing 
6686059653Szhengxing static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
6786059653Szhengxing 			     struct snd_pcm_hw_params *params)
6886059653Szhengxing {
6986059653Szhengxing 	int ret = 0;
7086059653Szhengxing 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
7186059653Szhengxing 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
7286059653Szhengxing 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
7386059653Szhengxing 	int mclk;
7486059653Szhengxing 
7586059653Szhengxing 	switch (params_rate(params)) {
7686059653Szhengxing 	case 8000:
7786059653Szhengxing 	case 16000:
783a3b070dSCaesar Wang 	case 24000:
793a3b070dSCaesar Wang 	case 32000:
8086059653Szhengxing 	case 48000:
813a3b070dSCaesar Wang 	case 64000:
8286059653Szhengxing 	case 96000:
8386059653Szhengxing 		mclk = 12288000;
8486059653Szhengxing 		break;
853a3b070dSCaesar Wang 	case 11025:
863a3b070dSCaesar Wang 	case 22050:
8786059653Szhengxing 	case 44100:
883a3b070dSCaesar Wang 	case 88200:
8986059653Szhengxing 		mclk = 11289600;
9086059653Szhengxing 		break;
9186059653Szhengxing 	default:
9286059653Szhengxing 		return -EINVAL;
9386059653Szhengxing 	}
9486059653Szhengxing 
9586059653Szhengxing 	ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
9686059653Szhengxing 				     SND_SOC_CLOCK_OUT);
9786059653Szhengxing 	if (ret < 0) {
9886059653Szhengxing 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
9986059653Szhengxing 		return ret;
10086059653Szhengxing 	}
10186059653Szhengxing 
10286059653Szhengxing 	ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
10386059653Szhengxing 				     SND_SOC_CLOCK_IN);
10486059653Szhengxing 	if (ret < 0) {
10586059653Szhengxing 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
10686059653Szhengxing 		return ret;
10786059653Szhengxing 	}
10886059653Szhengxing 
10986059653Szhengxing 	return ret;
11086059653Szhengxing }
11186059653Szhengxing 
11286059653Szhengxing static int rk_init(struct snd_soc_pcm_runtime *runtime)
11386059653Szhengxing {
11486059653Szhengxing 	struct snd_soc_card *card = runtime->card;
11586059653Szhengxing 	int ret;
11686059653Szhengxing 
11786059653Szhengxing 	/* Enable Headset and 4 Buttons Jack detection */
11886059653Szhengxing 	ret = snd_soc_card_jack_new(card, "Headset Jack",
11986059653Szhengxing 				    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
12086059653Szhengxing 				    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
12186059653Szhengxing 				    SND_JACK_BTN_2 | SND_JACK_BTN_3,
12286059653Szhengxing 				    &headset_jack, NULL, 0);
123f8ce2000SXing Zheng 	if (ret) {
12486059653Szhengxing 		dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
12586059653Szhengxing 		return ret;
12686059653Szhengxing 	}
12786059653Szhengxing 
12879223bf1SKuninori Morimoto 	return rt5645_set_jack_detect(runtime->codec_dai->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()),
1400209bf2bSKuninori Morimoto 	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1")),
1410209bf2bSKuninori 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 
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 
1930209bf2bSKuninori Morimoto 	rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
19486059653Szhengxing 
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 
221a56df73bSAlexey Khoroshilov static int 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;
227a56df73bSAlexey Khoroshilov 
228a56df73bSAlexey Khoroshilov 	return 0;
22986059653Szhengxing }
23086059653Szhengxing 
23186059653Szhengxing static const struct of_device_id rockchip_rt5645_of_match[] = {
23286059653Szhengxing 	{ .compatible = "rockchip,rockchip-audio-rt5645", },
23386059653Szhengxing 	{},
23486059653Szhengxing };
23586059653Szhengxing 
23686059653Szhengxing MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
23786059653Szhengxing 
23886059653Szhengxing static struct platform_driver snd_rk_mc_driver = {
23986059653Szhengxing 	.probe = snd_rk_mc_probe,
240a56df73bSAlexey Khoroshilov 	.remove = snd_rk_mc_remove,
24186059653Szhengxing 	.driver = {
24286059653Szhengxing 		.name = DRV_NAME,
24386059653Szhengxing 		.pm = &snd_soc_pm_ops,
24486059653Szhengxing 		.of_match_table = rockchip_rt5645_of_match,
24586059653Szhengxing 	},
24686059653Szhengxing };
24786059653Szhengxing 
24886059653Szhengxing module_platform_driver(snd_rk_mc_driver);
24986059653Szhengxing 
25086059653Szhengxing MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
25186059653Szhengxing MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver");
25286059653Szhengxing MODULE_LICENSE("GPL v2");
25386059653Szhengxing MODULE_ALIAS("platform:" DRV_NAME);
254