Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / sound / soc / intel / boards / byt-rt5640.c
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
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
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
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
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");