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"
3286059653Szhengxing 
3386059653Szhengxing #define DRV_NAME "rockchip-snd-rt5645"
3486059653Szhengxing 
3586059653Szhengxing static struct snd_soc_jack headset_jack;
3686059653Szhengxing 
3786059653Szhengxing /* Jack detect via rt5645 driver. */
3886059653Szhengxing extern int rt5645_set_jack_detect(struct snd_soc_codec *codec,
3986059653Szhengxing 	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
4086059653Szhengxing 	struct snd_soc_jack *btn_jack);
4186059653Szhengxing 
4286059653Szhengxing static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
4386059653Szhengxing 	SND_SOC_DAPM_HP("Headphones", NULL),
4486059653Szhengxing 	SND_SOC_DAPM_SPK("Speakers", NULL),
4586059653Szhengxing 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
4686059653Szhengxing 	SND_SOC_DAPM_MIC("Int Mic", NULL),
4786059653Szhengxing };
4886059653Szhengxing 
4986059653Szhengxing static const struct snd_soc_dapm_route rk_audio_map[] = {
5086059653Szhengxing 	/* Input Lines */
5186059653Szhengxing 	{"DMIC L2", NULL, "Int Mic"},
5286059653Szhengxing 	{"DMIC R2", NULL, "Int Mic"},
5386059653Szhengxing 	{"RECMIXL", NULL, "Headset Mic"},
5486059653Szhengxing 	{"RECMIXR", NULL, "Headset Mic"},
5586059653Szhengxing 
5686059653Szhengxing 	/* Output Lines */
5786059653Szhengxing 	{"Headphones", NULL, "HPOR"},
5886059653Szhengxing 	{"Headphones", NULL, "HPOL"},
5986059653Szhengxing 	{"Speakers", NULL, "SPOL"},
6086059653Szhengxing 	{"Speakers", NULL, "SPOR"},
6186059653Szhengxing };
6286059653Szhengxing 
6386059653Szhengxing static const struct snd_kcontrol_new rk_mc_controls[] = {
6486059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Headphones"),
6586059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Speakers"),
6686059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
6786059653Szhengxing 	SOC_DAPM_PIN_SWITCH("Int Mic"),
6886059653Szhengxing };
6986059653Szhengxing 
7086059653Szhengxing static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
7186059653Szhengxing 			     struct snd_pcm_hw_params *params)
7286059653Szhengxing {
7386059653Szhengxing 	int ret = 0;
7486059653Szhengxing 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
7586059653Szhengxing 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
7686059653Szhengxing 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
7786059653Szhengxing 	int mclk;
7886059653Szhengxing 
7986059653Szhengxing 	switch (params_rate(params)) {
8086059653Szhengxing 	case 8000:
8186059653Szhengxing 	case 16000:
8286059653Szhengxing 	case 48000:
8386059653Szhengxing 	case 96000:
8486059653Szhengxing 		mclk = 12288000;
8586059653Szhengxing 		break;
8686059653Szhengxing 	case 44100:
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 
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 */
11686059653Szhengxing 	ret = snd_soc_card_jack_new(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,
12086059653Szhengxing 				    &headset_jack, NULL, 0);
121f8ce2000SXing Zheng 	if (ret) {
12286059653Szhengxing 		dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
12386059653Szhengxing 		return ret;
12486059653Szhengxing 	}
12586059653Szhengxing 
12686059653Szhengxing 	return rt5645_set_jack_detect(runtime->codec,
12786059653Szhengxing 				     &headset_jack,
12886059653Szhengxing 				     &headset_jack,
12986059653Szhengxing 				     &headset_jack);
13086059653Szhengxing }
13186059653Szhengxing 
13286059653Szhengxing static struct snd_soc_ops rk_aif1_ops = {
13386059653Szhengxing 	.hw_params = rk_aif1_hw_params,
13486059653Szhengxing };
13586059653Szhengxing 
13686059653Szhengxing static struct snd_soc_dai_link rk_dailink = {
13786059653Szhengxing 	.name = "rt5645",
13886059653Szhengxing 	.stream_name = "rt5645 PCM",
13986059653Szhengxing 	.codec_dai_name = "rt5645-aif1",
14086059653Szhengxing 	.init = rk_init,
14186059653Szhengxing 	.ops = &rk_aif1_ops,
14286059653Szhengxing 	/* set rt5645 as slave */
14386059653Szhengxing 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
14486059653Szhengxing 		SND_SOC_DAIFMT_CBS_CFS,
14586059653Szhengxing };
14686059653Szhengxing 
14786059653Szhengxing static struct snd_soc_card snd_soc_card_rk = {
14886059653Szhengxing 	.name = "I2S-RT5650",
14986059653Szhengxing 	.dai_link = &rk_dailink,
15086059653Szhengxing 	.num_links = 1,
15186059653Szhengxing 	.dapm_widgets = rk_dapm_widgets,
15286059653Szhengxing 	.num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
15386059653Szhengxing 	.dapm_routes = rk_audio_map,
15486059653Szhengxing 	.num_dapm_routes = ARRAY_SIZE(rk_audio_map),
15586059653Szhengxing 	.controls = rk_mc_controls,
15686059653Szhengxing 	.num_controls = ARRAY_SIZE(rk_mc_controls),
15786059653Szhengxing };
15886059653Szhengxing 
15986059653Szhengxing static int snd_rk_mc_probe(struct platform_device *pdev)
16086059653Szhengxing {
16186059653Szhengxing 	int ret = 0;
16286059653Szhengxing 	struct snd_soc_card *card = &snd_soc_card_rk;
16386059653Szhengxing 	struct device_node *np = pdev->dev.of_node;
16486059653Szhengxing 
16586059653Szhengxing 	/* register the soc card */
16686059653Szhengxing 	card->dev = &pdev->dev;
16786059653Szhengxing 
16886059653Szhengxing 	rk_dailink.codec_of_node = of_parse_phandle(np,
16986059653Szhengxing 			"rockchip,audio-codec", 0);
17086059653Szhengxing 	if (!rk_dailink.codec_of_node) {
17186059653Szhengxing 		dev_err(&pdev->dev,
17286059653Szhengxing 			"Property 'rockchip,audio-codec' missing or invalid\n");
17386059653Szhengxing 		return -EINVAL;
17486059653Szhengxing 	}
17586059653Szhengxing 
17686059653Szhengxing 	rk_dailink.cpu_of_node = of_parse_phandle(np,
17786059653Szhengxing 			"rockchip,i2s-controller", 0);
17886059653Szhengxing 	if (!rk_dailink.cpu_of_node) {
17986059653Szhengxing 		dev_err(&pdev->dev,
18086059653Szhengxing 			"Property 'rockchip,i2s-controller' missing or invalid\n");
18186059653Szhengxing 		return -EINVAL;
18286059653Szhengxing 	}
18386059653Szhengxing 
18486059653Szhengxing 	rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
18586059653Szhengxing 
18686059653Szhengxing 	ret = snd_soc_of_parse_card_name(card, "rockchip,model");
18786059653Szhengxing 	if (ret) {
18886059653Szhengxing 		dev_err(&pdev->dev,
18986059653Szhengxing 			"Soc parse card name failed %d\n", ret);
19086059653Szhengxing 		return ret;
19186059653Szhengxing 	}
19286059653Szhengxing 
19386059653Szhengxing 	ret = devm_snd_soc_register_card(&pdev->dev, card);
19486059653Szhengxing 	if (ret) {
19586059653Szhengxing 		dev_err(&pdev->dev,
19686059653Szhengxing 			"Soc register card failed %d\n", ret);
19786059653Szhengxing 		return ret;
19886059653Szhengxing 	}
19986059653Szhengxing 
20086059653Szhengxing 	return ret;
20186059653Szhengxing }
20286059653Szhengxing 
20386059653Szhengxing static const struct of_device_id rockchip_rt5645_of_match[] = {
20486059653Szhengxing 	{ .compatible = "rockchip,rockchip-audio-rt5645", },
20586059653Szhengxing 	{},
20686059653Szhengxing };
20786059653Szhengxing 
20886059653Szhengxing MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
20986059653Szhengxing 
21086059653Szhengxing static struct platform_driver snd_rk_mc_driver = {
21186059653Szhengxing 	.probe = snd_rk_mc_probe,
21286059653Szhengxing 	.driver = {
21386059653Szhengxing 		.name = DRV_NAME,
21486059653Szhengxing 		.owner = THIS_MODULE,
21586059653Szhengxing 		.pm = &snd_soc_pm_ops,
21686059653Szhengxing 		.of_match_table = rockchip_rt5645_of_match,
21786059653Szhengxing 	},
21886059653Szhengxing };
21986059653Szhengxing 
22086059653Szhengxing module_platform_driver(snd_rk_mc_driver);
22186059653Szhengxing 
22286059653Szhengxing MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
22386059653Szhengxing MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver");
22486059653Szhengxing MODULE_LICENSE("GPL v2");
22586059653Szhengxing MODULE_ALIAS("platform:" DRV_NAME);
226