1 /*
2  * Rockchip machine ASoC driver for boards using a RT5645/RT5650 CODEC.
3  *
4  * Copyright (c) 2015, ROCKCHIP CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/slab.h>
23 #include <linux/gpio.h>
24 #include <linux/of_gpio.h>
25 #include <linux/delay.h>
26 #include <sound/core.h>
27 #include <sound/jack.h>
28 #include <sound/pcm.h>
29 #include <sound/pcm_params.h>
30 #include <sound/soc.h>
31 #include "rockchip_i2s.h"
32 
33 #define DRV_NAME "rockchip-snd-rt5645"
34 
35 static struct snd_soc_jack headset_jack;
36 
37 /* Jack detect via rt5645 driver. */
38 extern int rt5645_set_jack_detect(struct snd_soc_codec *codec,
39 	struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack,
40 	struct snd_soc_jack *btn_jack);
41 
42 static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
43 	SND_SOC_DAPM_HP("Headphones", NULL),
44 	SND_SOC_DAPM_SPK("Speakers", NULL),
45 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
46 	SND_SOC_DAPM_MIC("Int Mic", NULL),
47 };
48 
49 static const struct snd_soc_dapm_route rk_audio_map[] = {
50 	/* Input Lines */
51 	{"DMIC L2", NULL, "Int Mic"},
52 	{"DMIC R2", NULL, "Int Mic"},
53 	{"RECMIXL", NULL, "Headset Mic"},
54 	{"RECMIXR", NULL, "Headset Mic"},
55 
56 	/* Output Lines */
57 	{"Headphones", NULL, "HPOR"},
58 	{"Headphones", NULL, "HPOL"},
59 	{"Speakers", NULL, "SPOL"},
60 	{"Speakers", NULL, "SPOR"},
61 };
62 
63 static const struct snd_kcontrol_new rk_mc_controls[] = {
64 	SOC_DAPM_PIN_SWITCH("Headphones"),
65 	SOC_DAPM_PIN_SWITCH("Speakers"),
66 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
67 	SOC_DAPM_PIN_SWITCH("Int Mic"),
68 };
69 
70 static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
71 			     struct snd_pcm_hw_params *params)
72 {
73 	int ret = 0;
74 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
75 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
76 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
77 	int mclk;
78 
79 	switch (params_rate(params)) {
80 	case 8000:
81 	case 16000:
82 	case 48000:
83 	case 96000:
84 		mclk = 12288000;
85 		break;
86 	case 44100:
87 		mclk = 11289600;
88 		break;
89 	default:
90 		return -EINVAL;
91 	}
92 
93 	ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
94 				     SND_SOC_CLOCK_OUT);
95 	if (ret < 0) {
96 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
97 		return ret;
98 	}
99 
100 	ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
101 				     SND_SOC_CLOCK_IN);
102 	if (ret < 0) {
103 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
104 		return ret;
105 	}
106 
107 	return ret;
108 }
109 
110 static int rk_init(struct snd_soc_pcm_runtime *runtime)
111 {
112 	struct snd_soc_card *card = runtime->card;
113 	int ret;
114 
115 	/* Enable Headset and 4 Buttons Jack detection */
116 	ret = snd_soc_card_jack_new(card, "Headset Jack",
117 				    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
118 				    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
119 				    SND_JACK_BTN_2 | SND_JACK_BTN_3,
120 				    &headset_jack, NULL, 0);
121 	if (ret) {
122 		dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
123 		return ret;
124 	}
125 
126 	return rt5645_set_jack_detect(runtime->codec,
127 				     &headset_jack,
128 				     &headset_jack,
129 				     &headset_jack);
130 }
131 
132 static struct snd_soc_ops rk_aif1_ops = {
133 	.hw_params = rk_aif1_hw_params,
134 };
135 
136 static struct snd_soc_dai_link rk_dailink = {
137 	.name = "rt5645",
138 	.stream_name = "rt5645 PCM",
139 	.codec_dai_name = "rt5645-aif1",
140 	.init = rk_init,
141 	.ops = &rk_aif1_ops,
142 	/* set rt5645 as slave */
143 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
144 		SND_SOC_DAIFMT_CBS_CFS,
145 };
146 
147 static struct snd_soc_card snd_soc_card_rk = {
148 	.name = "I2S-RT5650",
149 	.owner = THIS_MODULE,
150 	.dai_link = &rk_dailink,
151 	.num_links = 1,
152 	.dapm_widgets = rk_dapm_widgets,
153 	.num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
154 	.dapm_routes = rk_audio_map,
155 	.num_dapm_routes = ARRAY_SIZE(rk_audio_map),
156 	.controls = rk_mc_controls,
157 	.num_controls = ARRAY_SIZE(rk_mc_controls),
158 };
159 
160 static int snd_rk_mc_probe(struct platform_device *pdev)
161 {
162 	int ret = 0;
163 	struct snd_soc_card *card = &snd_soc_card_rk;
164 	struct device_node *np = pdev->dev.of_node;
165 
166 	/* register the soc card */
167 	card->dev = &pdev->dev;
168 
169 	rk_dailink.codec_of_node = of_parse_phandle(np,
170 			"rockchip,audio-codec", 0);
171 	if (!rk_dailink.codec_of_node) {
172 		dev_err(&pdev->dev,
173 			"Property 'rockchip,audio-codec' missing or invalid\n");
174 		return -EINVAL;
175 	}
176 
177 	rk_dailink.cpu_of_node = of_parse_phandle(np,
178 			"rockchip,i2s-controller", 0);
179 	if (!rk_dailink.cpu_of_node) {
180 		dev_err(&pdev->dev,
181 			"Property 'rockchip,i2s-controller' missing or invalid\n");
182 		return -EINVAL;
183 	}
184 
185 	rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
186 
187 	ret = snd_soc_of_parse_card_name(card, "rockchip,model");
188 	if (ret) {
189 		dev_err(&pdev->dev,
190 			"Soc parse card name failed %d\n", ret);
191 		return ret;
192 	}
193 
194 	ret = devm_snd_soc_register_card(&pdev->dev, card);
195 	if (ret) {
196 		dev_err(&pdev->dev,
197 			"Soc register card failed %d\n", ret);
198 		return ret;
199 	}
200 
201 	return ret;
202 }
203 
204 static const struct of_device_id rockchip_rt5645_of_match[] = {
205 	{ .compatible = "rockchip,rockchip-audio-rt5645", },
206 	{},
207 };
208 
209 MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
210 
211 static struct platform_driver snd_rk_mc_driver = {
212 	.probe = snd_rk_mc_probe,
213 	.driver = {
214 		.name = DRV_NAME,
215 		.pm = &snd_soc_pm_ops,
216 		.of_match_table = rockchip_rt5645_of_match,
217 	},
218 };
219 
220 module_platform_driver(snd_rk_mc_driver);
221 
222 MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
223 MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver");
224 MODULE_LICENSE("GPL v2");
225 MODULE_ALIAS("platform:" DRV_NAME);
226