• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Baytrail SST RT5640 machine driver
4  * Copyright (c) 2014, Intel Corporation.
5  */
6 
7 #include <linux/init.h>
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <linux/acpi.h>
11 #include <linux/device.h>
12 #include <linux/dmi.h>
13 #include <linux/slab.h>
14 #include <sound/pcm.h>
15 #include <sound/pcm_params.h>
16 #include <sound/soc.h>
17 #include <sound/jack.h>
18 #include "../../codecs/rt5640.h"
19 
20 #include "../common/sst-dsp.h"
21 
22 static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
23 	SND_SOC_DAPM_HP("Headphone", NULL),
24 	SND_SOC_DAPM_MIC("Headset Mic", NULL),
25 	SND_SOC_DAPM_MIC("Internal Mic", NULL),
26 	SND_SOC_DAPM_SPK("Speaker", NULL),
27 };
28 
29 static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
30 	{"Headset Mic", NULL, "MICBIAS1"},
31 	{"IN2P", NULL, "Headset Mic"},
32 	{"Headphone", NULL, "HPOL"},
33 	{"Headphone", NULL, "HPOR"},
34 	{"Speaker", NULL, "SPOLP"},
35 	{"Speaker", NULL, "SPOLN"},
36 	{"Speaker", NULL, "SPORP"},
37 	{"Speaker", NULL, "SPORN"},
38 };
39 
40 static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
41 	{"DMIC1", NULL, "Internal Mic"},
42 };
43 
44 static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
45 	{"DMIC2", NULL, "Internal Mic"},
46 };
47 
48 static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
49 	{"Internal Mic", NULL, "MICBIAS1"},
50 	{"IN1P", NULL, "Internal Mic"},
51 };
52 
53 enum {
54 	BYT_RT5640_DMIC1_MAP,
55 	BYT_RT5640_DMIC2_MAP,
56 	BYT_RT5640_IN1_MAP,
57 };
58 
59 #define BYT_RT5640_MAP(quirk)	((quirk) & 0xff)
60 #define BYT_RT5640_DMIC_EN	BIT(16)
61 
62 static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
63 					BYT_RT5640_DMIC_EN;
64 
65 static const struct snd_kcontrol_new byt_rt5640_controls[] = {
66 	SOC_DAPM_PIN_SWITCH("Headphone"),
67 	SOC_DAPM_PIN_SWITCH("Headset Mic"),
68 	SOC_DAPM_PIN_SWITCH("Internal Mic"),
69 	SOC_DAPM_PIN_SWITCH("Speaker"),
70 };
71 
byt_rt5640_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)72 static int byt_rt5640_hw_params(struct snd_pcm_substream *substream,
73 				struct snd_pcm_hw_params *params)
74 {
75 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
76 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
77 	int ret;
78 
79 	ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
80 				     params_rate(params) * 256,
81 				     SND_SOC_CLOCK_IN);
82 	if (ret < 0) {
83 		dev_err(codec_dai->dev, "can't set codec clock %d\n", ret);
84 		return ret;
85 	}
86 	ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
87 				  params_rate(params) * 64,
88 				  params_rate(params) * 256);
89 	if (ret < 0) {
90 		dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
91 		return ret;
92 	}
93 	return 0;
94 }
95 
byt_rt5640_quirk_cb(const struct dmi_system_id * id)96 static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
97 {
98 	byt_rt5640_quirk = (unsigned long)id->driver_data;
99 	return 1;
100 }
101 
102 static const struct dmi_system_id byt_rt5640_quirk_table[] = {
103 	{
104 		.callback = byt_rt5640_quirk_cb,
105 		.matches = {
106 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
107 			DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
108 		},
109 		.driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
110 	},
111 	{
112 		.callback = byt_rt5640_quirk_cb,
113 		.matches = {
114 			DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
115 			DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
116 		},
117 		.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
118 						 BYT_RT5640_DMIC_EN),
119 	},
120 	{}
121 };
122 
byt_rt5640_init(struct snd_soc_pcm_runtime * runtime)123 static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
124 {
125 	int ret;
126 	struct snd_soc_component *component = runtime->codec_dai->component;
127 	struct snd_soc_card *card = runtime->card;
128 	const struct snd_soc_dapm_route *custom_map;
129 	int num_routes;
130 
131 	card->dapm.idle_bias_off = true;
132 
133 	ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
134 					ARRAY_SIZE(byt_rt5640_controls));
135 	if (ret) {
136 		dev_err(card->dev, "unable to add card controls\n");
137 		return ret;
138 	}
139 
140 	dmi_check_system(byt_rt5640_quirk_table);
141 	switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
142 	case BYT_RT5640_IN1_MAP:
143 		custom_map = byt_rt5640_intmic_in1_map;
144 		num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
145 		break;
146 	case BYT_RT5640_DMIC2_MAP:
147 		custom_map = byt_rt5640_intmic_dmic2_map;
148 		num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
149 		break;
150 	default:
151 		custom_map = byt_rt5640_intmic_dmic1_map;
152 		num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
153 	}
154 
155 	ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
156 	if (ret)
157 		return ret;
158 
159 	if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
160 		ret = rt5640_dmic_enable(component, 0, 0);
161 		if (ret)
162 			return ret;
163 	}
164 
165 	snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
166 	snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
167 
168 	return ret;
169 }
170 
171 static struct snd_soc_ops byt_rt5640_ops = {
172 	.hw_params = byt_rt5640_hw_params,
173 };
174 
175 SND_SOC_DAILINK_DEFS(audio,
176 	DAILINK_COMP_ARRAY(COMP_CPU("baytrail-pcm-audio")),
177 	DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5640:00", "rt5640-aif1")),
178 	DAILINK_COMP_ARRAY(COMP_PLATFORM("baytrail-pcm-audio")));
179 
180 static struct snd_soc_dai_link byt_rt5640_dais[] = {
181 	{
182 		.name = "Baytrail Audio",
183 		.stream_name = "Audio",
184 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
185 			   SND_SOC_DAIFMT_CBS_CFS,
186 		.init = byt_rt5640_init,
187 		.ops = &byt_rt5640_ops,
188 		SND_SOC_DAILINK_REG(audio),
189 	},
190 };
191 
192 static struct snd_soc_card byt_rt5640_card = {
193 	.name = "byt-rt5640",
194 	.owner = THIS_MODULE,
195 	.dai_link = byt_rt5640_dais,
196 	.num_links = ARRAY_SIZE(byt_rt5640_dais),
197 	.dapm_widgets = byt_rt5640_widgets,
198 	.num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
199 	.dapm_routes = byt_rt5640_audio_map,
200 	.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
201 	.fully_routed = true,
202 };
203 
byt_rt5640_probe(struct platform_device * pdev)204 static int byt_rt5640_probe(struct platform_device *pdev)
205 {
206 	struct snd_soc_card *card = &byt_rt5640_card;
207 
208 	card->dev = &pdev->dev;
209 	return devm_snd_soc_register_card(&pdev->dev, card);
210 }
211 
212 static struct platform_driver byt_rt5640_audio = {
213 	.probe = byt_rt5640_probe,
214 	.driver = {
215 		.name = "byt-rt5640",
216 		.pm = &snd_soc_pm_ops,
217 	},
218 };
219 module_platform_driver(byt_rt5640_audio)
220 
221 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
222 MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
223 MODULE_LICENSE("GPL v2");
224 MODULE_ALIAS("platform:byt-rt5640");
225