]>
Commit | Line | Data |
---|---|---|
8e8e69d6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
5b00ce63 JH |
2 | /* |
3 | * cht-bsw-nau8824.c - ASoc Machine driver for Intel Cherryview-based | |
4 | * platforms Cherrytrail and Braswell, with nau8824 codec. | |
5 | * | |
6 | * Copyright (C) 2018 Intel Corp | |
7 | * Copyright (C) 2018 Nuvoton Technology Corp | |
8 | * | |
9 | * Author: Wang, Joseph C <joequant@gmail.com> | |
10 | * Co-author: John Hsu <KCHSU0@nuvoton.com> | |
11 | * This file is based on cht_bsw_rt5672.c and cht-bsw-max98090.c | |
5b00ce63 JH |
12 | */ |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/slab.h> | |
17 | #include <sound/pcm.h> | |
18 | #include <sound/pcm_params.h> | |
19 | #include <sound/soc.h> | |
4506db80 | 20 | #include <sound/soc-acpi.h> |
5b00ce63 JH |
21 | #include <sound/jack.h> |
22 | #include <linux/input.h> | |
23 | #include "../atom/sst-atom-controls.h" | |
24 | #include "../../codecs/nau8824.h" | |
25 | ||
26 | struct cht_mc_private { | |
27 | struct snd_soc_jack jack; | |
28 | }; | |
29 | ||
30 | static struct snd_soc_jack_pin cht_bsw_jack_pins[] = { | |
31 | { | |
32 | .pin = "Headphone", | |
33 | .mask = SND_JACK_HEADPHONE, | |
34 | }, | |
35 | { | |
36 | .pin = "Headset Mic", | |
37 | .mask = SND_JACK_MICROPHONE, | |
38 | }, | |
39 | }; | |
40 | ||
41 | static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { | |
42 | SND_SOC_DAPM_HP("Headphone", NULL), | |
43 | SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
44 | SND_SOC_DAPM_MIC("Int Mic", NULL), | |
45 | SND_SOC_DAPM_SPK("Ext Spk", NULL), | |
46 | }; | |
47 | ||
48 | static const struct snd_soc_dapm_route cht_audio_map[] = { | |
49 | {"Ext Spk", NULL, "SPKOUTL"}, | |
50 | {"Ext Spk", NULL, "SPKOUTR"}, | |
51 | {"Headphone", NULL, "HPOL"}, | |
52 | {"Headphone", NULL, "HPOR"}, | |
53 | {"MIC1", NULL, "Int Mic"}, | |
54 | {"MIC2", NULL, "Int Mic"}, | |
55 | {"HSMIC1", NULL, "Headset Mic"}, | |
56 | {"HSMIC2", NULL, "Headset Mic"}, | |
57 | {"Playback", NULL, "ssp2 Tx"}, | |
58 | {"ssp2 Tx", NULL, "codec_out0"}, | |
59 | {"ssp2 Tx", NULL, "codec_out1"}, | |
60 | {"codec_in0", NULL, "ssp2 Rx" }, | |
61 | {"codec_in1", NULL, "ssp2 Rx" }, | |
62 | {"ssp2 Rx", NULL, "Capture"}, | |
63 | }; | |
64 | ||
65 | static const struct snd_kcontrol_new cht_mc_controls[] = { | |
66 | SOC_DAPM_PIN_SWITCH("Headphone"), | |
67 | SOC_DAPM_PIN_SWITCH("Headset Mic"), | |
68 | SOC_DAPM_PIN_SWITCH("Int Mic"), | |
69 | SOC_DAPM_PIN_SWITCH("Ext Spk"), | |
70 | }; | |
71 | ||
72 | static int cht_aif1_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, NAU8824_CLK_FLL_FS, 0, | |
80 | SND_SOC_CLOCK_IN); | |
81 | if (ret < 0) { | |
82 | dev_err(codec_dai->dev, "can't set FS clock %d\n", ret); | |
83 | return ret; | |
84 | } | |
85 | ret = snd_soc_dai_set_pll(codec_dai, 0, 0, params_rate(params), | |
86 | params_rate(params) * 256); | |
87 | if (ret < 0) { | |
88 | dev_err(codec_dai->dev, "can't set FLL: %d\n", ret); | |
89 | return ret; | |
90 | } | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) | |
96 | { | |
97 | struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); | |
98 | struct snd_soc_jack *jack = &ctx->jack; | |
5b00ce63 | 99 | struct snd_soc_dai *codec_dai = runtime->codec_dai; |
3bf045d1 | 100 | struct snd_soc_component *component = codec_dai->component; |
5b00ce63 JH |
101 | int ret, jack_type; |
102 | ||
103 | /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ | |
104 | ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xf, 0x1, 4, 24); | |
105 | if (ret < 0) { | |
106 | dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); | |
107 | return ret; | |
108 | } | |
109 | ||
110 | /* NAU88L24 supports 4 butons headset detection | |
111 | * KEY_MEDIA | |
112 | * KEY_VOICECOMMAND | |
113 | * KEY_VOLUMEUP | |
114 | * KEY_VOLUMEDOWN | |
115 | */ | |
5aff078a | 116 | jack_type = SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | |
5b00ce63 JH |
117 | SND_JACK_BTN_2 | SND_JACK_BTN_3; |
118 | ret = snd_soc_card_jack_new(runtime->card, "Headset", jack_type, jack, | |
119 | cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins)); | |
120 | if (ret) { | |
121 | dev_err(runtime->dev, | |
122 | "Headset Jack creation failed %d\n", ret); | |
123 | return ret; | |
124 | } | |
125 | snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); | |
126 | snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); | |
127 | snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); | |
128 | snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); | |
129 | ||
3bf045d1 | 130 | nau8824_enable_jack_detect(component, jack); |
5b00ce63 JH |
131 | |
132 | return ret; | |
133 | } | |
134 | ||
135 | static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, | |
136 | struct snd_pcm_hw_params *params) | |
137 | { | |
138 | struct snd_interval *rate = hw_param_interval(params, | |
139 | SNDRV_PCM_HW_PARAM_RATE); | |
140 | struct snd_interval *channels = hw_param_interval(params, | |
141 | SNDRV_PCM_HW_PARAM_CHANNELS); | |
142 | struct snd_mask *fmt = | |
143 | hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | |
144 | ||
145 | /* The DSP will covert the FE rate to 48k, stereo, 24bits */ | |
146 | rate->min = rate->max = 48000; | |
147 | channels->min = channels->max = 2; | |
148 | ||
149 | /* set SSP2 to 24-bit */ | |
150 | snd_mask_none(fmt); | |
151 | params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); | |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | static int cht_aif1_startup(struct snd_pcm_substream *substream) | |
157 | { | |
158 | return snd_pcm_hw_constraint_single(substream->runtime, | |
159 | SNDRV_PCM_HW_PARAM_RATE, 48000); | |
160 | } | |
161 | ||
162 | static const struct snd_soc_ops cht_aif1_ops = { | |
163 | .startup = cht_aif1_startup, | |
164 | }; | |
165 | ||
166 | static const struct snd_soc_ops cht_be_ssp2_ops = { | |
167 | .hw_params = cht_aif1_hw_params, | |
168 | }; | |
169 | ||
170 | static struct snd_soc_dai_link cht_dailink[] = { | |
171 | /* Front End DAI links */ | |
172 | [MERR_DPCM_AUDIO] = { | |
173 | .name = "Audio Port", | |
174 | .stream_name = "Audio", | |
175 | .cpu_dai_name = "media-cpu-dai", | |
176 | .codec_dai_name = "snd-soc-dummy-dai", | |
177 | .codec_name = "snd-soc-dummy", | |
178 | .platform_name = "sst-mfld-platform", | |
179 | .nonatomic = true, | |
180 | .dynamic = 1, | |
181 | .dpcm_playback = 1, | |
182 | .dpcm_capture = 1, | |
183 | .ops = &cht_aif1_ops, | |
184 | }, | |
185 | [MERR_DPCM_DEEP_BUFFER] = { | |
186 | .name = "Deep-Buffer Audio Port", | |
187 | .stream_name = "Deep-Buffer Audio", | |
188 | .cpu_dai_name = "deepbuffer-cpu-dai", | |
189 | .codec_dai_name = "snd-soc-dummy-dai", | |
190 | .codec_name = "snd-soc-dummy", | |
191 | .platform_name = "sst-mfld-platform", | |
192 | .nonatomic = true, | |
193 | .dynamic = 1, | |
194 | .dpcm_playback = 1, | |
195 | .ops = &cht_aif1_ops, | |
196 | }, | |
197 | [MERR_DPCM_COMPR] = { | |
198 | .name = "Compressed Port", | |
199 | .stream_name = "Compress", | |
200 | .cpu_dai_name = "compress-cpu-dai", | |
201 | .codec_dai_name = "snd-soc-dummy-dai", | |
202 | .codec_name = "snd-soc-dummy", | |
203 | .platform_name = "sst-mfld-platform", | |
204 | }, | |
205 | /* Back End DAI links */ | |
206 | { | |
207 | /* SSP2 - Codec */ | |
208 | .name = "SSP2-Codec", | |
209 | .id = 1, | |
210 | .cpu_dai_name = "ssp2-port", | |
211 | .platform_name = "sst-mfld-platform", | |
212 | .no_pcm = 1, | |
213 | .codec_dai_name = NAU8824_CODEC_DAI, | |
214 | .codec_name = "i2c-10508824:00", | |
215 | .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | |
216 | | SND_SOC_DAIFMT_CBS_CFS, | |
217 | .init = cht_codec_init, | |
218 | .be_hw_params_fixup = cht_codec_fixup, | |
219 | .dpcm_playback = 1, | |
220 | .dpcm_capture = 1, | |
221 | .ops = &cht_be_ssp2_ops, | |
222 | }, | |
223 | }; | |
224 | ||
225 | /* SoC card */ | |
226 | static struct snd_soc_card snd_soc_card_cht = { | |
227 | .name = "chtnau8824", | |
228 | .owner = THIS_MODULE, | |
229 | .dai_link = cht_dailink, | |
230 | .num_links = ARRAY_SIZE(cht_dailink), | |
231 | .dapm_widgets = cht_dapm_widgets, | |
232 | .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets), | |
233 | .dapm_routes = cht_audio_map, | |
234 | .num_dapm_routes = ARRAY_SIZE(cht_audio_map), | |
235 | .controls = cht_mc_controls, | |
236 | .num_controls = ARRAY_SIZE(cht_mc_controls), | |
237 | }; | |
238 | ||
239 | static int snd_cht_mc_probe(struct platform_device *pdev) | |
240 | { | |
241 | struct cht_mc_private *drv; | |
4506db80 PLB |
242 | struct snd_soc_acpi_mach *mach; |
243 | const char *platform_name; | |
5b00ce63 JH |
244 | int ret_val; |
245 | ||
b113855a | 246 | drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); |
5b00ce63 JH |
247 | if (!drv) |
248 | return -ENOMEM; | |
249 | snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); | |
250 | ||
4506db80 PLB |
251 | /* override plaform name, if required */ |
252 | mach = (&pdev->dev)->platform_data; | |
253 | platform_name = mach->mach_params.platform; | |
254 | ||
255 | ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht, | |
256 | platform_name); | |
257 | if (ret_val) | |
258 | return ret_val; | |
259 | ||
5b00ce63 JH |
260 | /* register the soc card */ |
261 | snd_soc_card_cht.dev = &pdev->dev; | |
262 | ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); | |
263 | if (ret_val) { | |
264 | dev_err(&pdev->dev, | |
265 | "snd_soc_register_card failed %d\n", ret_val); | |
266 | return ret_val; | |
267 | } | |
268 | platform_set_drvdata(pdev, &snd_soc_card_cht); | |
269 | ||
270 | return ret_val; | |
271 | } | |
272 | ||
273 | static struct platform_driver snd_cht_mc_driver = { | |
274 | .driver = { | |
275 | .name = "cht-bsw-nau8824", | |
276 | }, | |
277 | .probe = snd_cht_mc_probe, | |
278 | }; | |
279 | ||
280 | module_platform_driver(snd_cht_mc_driver); | |
281 | ||
282 | MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver"); | |
283 | MODULE_AUTHOR("Wang, Joseph C <joequant@gmail.com>"); | |
284 | MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>"); | |
285 | MODULE_LICENSE("GPL v2"); | |
286 | MODULE_ALIAS("platform:cht-bsw-nau8824"); |