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 24000:
83 	case 32000:
84 	case 48000:
85 	case 64000:
86 	case 96000:
87 		mclk = 12288000;
88 		break;
89 	case 11025:
90 	case 22050:
91 	case 44100:
92 	case 88200:
93 		mclk = 11289600;
94 		break;
95 	default:
96 		return -EINVAL;
97 	}
98 
99 	ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
100 				     SND_SOC_CLOCK_OUT);
101 	if (ret < 0) {
102 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
103 		return ret;
104 	}
105 
106 	ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
107 				     SND_SOC_CLOCK_IN);
108 	if (ret < 0) {
109 		dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
110 		return ret;
111 	}
112 
113 	return ret;
114 }
115 
116 static int rk_init(struct snd_soc_pcm_runtime *runtime)
117 {
118 	struct snd_soc_card *card = runtime->card;
119 	int ret;
120 
121 	/* Enable Headset and 4 Buttons Jack detection */
122 	ret = snd_soc_card_jack_new(card, "Headset Jack",
123 				    SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
124 				    SND_JACK_BTN_0 | SND_JACK_BTN_1 |
125 				    SND_JACK_BTN_2 | SND_JACK_BTN_3,
126 				    &headset_jack, NULL, 0);
127 	if (ret) {
128 		dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
129 		return ret;
130 	}
131 
132 	return rt5645_set_jack_detect(runtime->codec,
133 				     &headset_jack,
134 				     &headset_jack,
135 				     &headset_jack);
136 }
137 
138 static struct snd_soc_ops rk_aif1_ops = {
139 	.hw_params = rk_aif1_hw_params,
140 };
141 
142 static struct snd_soc_dai_link rk_dailink = {
143 	.name = "rt5645",
144 	.stream_name = "rt5645 PCM",
145 	.codec_dai_name = "rt5645-aif1",
146 	.init = rk_init,
147 	.ops = &rk_aif1_ops,
148 	/* set rt5645 as slave */
149 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
150 		SND_SOC_DAIFMT_CBS_CFS,
151 };
152 
153 static struct snd_soc_card snd_soc_card_rk = {
154 	.name = "I2S-RT5650",
155 	.owner = THIS_MODULE,
156 	.dai_link = &rk_dailink,
157 	.num_links = 1,
158 	.dapm_widgets = rk_dapm_widgets,
159 	.num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
160 	.dapm_routes = rk_audio_map,
161 	.num_dapm_routes = ARRAY_SIZE(rk_audio_map),
162 	.controls = rk_mc_controls,
163 	.num_controls = ARRAY_SIZE(rk_mc_controls),
164 };
165 
166 static int snd_rk_mc_probe(struct platform_device *pdev)
167 {
168 	int ret = 0;
169 	struct snd_soc_card *card = &snd_soc_card_rk;
170 	struct device_node *np = pdev->dev.of_node;
171 
172 	/* register the soc card */
173 	card->dev = &pdev->dev;
174 
175 	rk_dailink.codec_of_node = of_parse_phandle(np,
176 			"rockchip,audio-codec", 0);
177 	if (!rk_dailink.codec_of_node) {
178 		dev_err(&pdev->dev,
179 			"Property 'rockchip,audio-codec' missing or invalid\n");
180 		return -EINVAL;
181 	}
182 
183 	rk_dailink.cpu_of_node = of_parse_phandle(np,
184 			"rockchip,i2s-controller", 0);
185 	if (!rk_dailink.cpu_of_node) {
186 		dev_err(&pdev->dev,
187 			"Property 'rockchip,i2s-controller' missing or invalid\n");
188 		return -EINVAL;
189 	}
190 
191 	rk_dailink.platform_of_node = rk_dailink.cpu_of_node;
192 
193 	ret = snd_soc_of_parse_card_name(card, "rockchip,model");
194 	if (ret) {
195 		dev_err(&pdev->dev,
196 			"Soc parse card name failed %d\n", ret);
197 		return ret;
198 	}
199 
200 	ret = devm_snd_soc_register_card(&pdev->dev, card);
201 	if (ret) {
202 		dev_err(&pdev->dev,
203 			"Soc register card failed %d\n", ret);
204 		return ret;
205 	}
206 
207 	return ret;
208 }
209 
210 static const struct of_device_id rockchip_rt5645_of_match[] = {
211 	{ .compatible = "rockchip,rockchip-audio-rt5645", },
212 	{},
213 };
214 
215 MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
216 
217 static struct platform_driver snd_rk_mc_driver = {
218 	.probe = snd_rk_mc_probe,
219 	.driver = {
220 		.name = DRV_NAME,
221 		.pm = &snd_soc_pm_ops,
222 		.of_match_table = rockchip_rt5645_of_match,
223 	},
224 };
225 
226 module_platform_driver(snd_rk_mc_driver);
227 
228 MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
229 MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver");
230 MODULE_LICENSE("GPL v2");
231 MODULE_ALIAS("platform:" DRV_NAME);
232