GNU Linux-libre 5.19-rc6-gnu
[releases.git] / sound / soc / intel / boards / haswell.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel Haswell Lynxpoint SST Audio
4  *
5  * Copyright (C) 2013, Intel Corporation. All rights reserved.
6  */
7
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <sound/core.h>
11 #include <sound/pcm.h>
12 #include <sound/soc.h>
13 #include <sound/soc-acpi.h>
14 #include <sound/pcm_params.h>
15
16 #include "../../codecs/rt5640.h"
17
18 /* Haswell ULT platforms have a Headphone and Mic jack */
19 static const struct snd_soc_dapm_widget haswell_widgets[] = {
20         SND_SOC_DAPM_HP("Headphones", NULL),
21         SND_SOC_DAPM_MIC("Mic", NULL),
22 };
23
24 static const struct snd_soc_dapm_route haswell_rt5640_map[] = {
25
26         {"Headphones", NULL, "HPOR"},
27         {"Headphones", NULL, "HPOL"},
28         {"IN2P", NULL, "Mic"},
29
30         /* CODEC BE connections */
31         {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
32         {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
33 };
34
35 static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
36                         struct snd_pcm_hw_params *params)
37 {
38         struct snd_interval *rate = hw_param_interval(params,
39                         SNDRV_PCM_HW_PARAM_RATE);
40         struct snd_interval *channels = hw_param_interval(params,
41                                                 SNDRV_PCM_HW_PARAM_CHANNELS);
42
43         /* The ADSP will covert the FE rate to 48k, stereo */
44         rate->min = rate->max = 48000;
45         channels->min = channels->max = 2;
46
47         /* set SSP0 to 16 bit */
48         params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
49         return 0;
50 }
51
52 static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream,
53         struct snd_pcm_hw_params *params)
54 {
55         struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
56         struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
57         int ret;
58
59         ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000,
60                 SND_SOC_CLOCK_IN);
61
62         if (ret < 0) {
63                 dev_err(rtd->dev, "can't set codec sysclk configuration\n");
64                 return ret;
65         }
66
67         /* set correct codec filter for DAI format and clock config */
68         snd_soc_component_update_bits(codec_dai->component, 0x83, 0xffff, 0x8000);
69
70         return ret;
71 }
72
73 static const struct snd_soc_ops haswell_rt5640_ops = {
74         .hw_params = haswell_rt5640_hw_params,
75 };
76
77 SND_SOC_DAILINK_DEF(dummy,
78         DAILINK_COMP_ARRAY(COMP_DUMMY()));
79
80 SND_SOC_DAILINK_DEF(system,
81         DAILINK_COMP_ARRAY(COMP_CPU("System Pin")));
82
83 SND_SOC_DAILINK_DEF(offload0,
84         DAILINK_COMP_ARRAY(COMP_CPU("Offload0 Pin")));
85
86 SND_SOC_DAILINK_DEF(offload1,
87         DAILINK_COMP_ARRAY(COMP_CPU("Offload1 Pin")));
88
89 SND_SOC_DAILINK_DEF(loopback,
90         DAILINK_COMP_ARRAY(COMP_CPU("Loopback Pin")));
91
92 SND_SOC_DAILINK_DEF(codec,
93         DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT33CA:00", "rt5640-aif1")));
94
95 SND_SOC_DAILINK_DEF(platform,
96         DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio")));
97
98 SND_SOC_DAILINK_DEF(ssp0_port,
99             DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port")));
100
101 static struct snd_soc_dai_link haswell_rt5640_dais[] = {
102         /* Front End DAI links */
103         {
104                 .name = "System",
105                 .stream_name = "System Playback/Capture",
106                 .nonatomic = 1,
107                 .dynamic = 1,
108                 .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
109                 .dpcm_playback = 1,
110                 .dpcm_capture = 1,
111                 SND_SOC_DAILINK_REG(system, dummy, platform),
112         },
113         {
114                 .name = "Offload0",
115                 .stream_name = "Offload0 Playback",
116                 .nonatomic = 1,
117                 .dynamic = 1,
118                 .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
119                 .dpcm_playback = 1,
120                 SND_SOC_DAILINK_REG(offload0, dummy, platform),
121         },
122         {
123                 .name = "Offload1",
124                 .stream_name = "Offload1 Playback",
125                 .nonatomic = 1,
126                 .dynamic = 1,
127                 .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
128                 .dpcm_playback = 1,
129                 SND_SOC_DAILINK_REG(offload1, dummy, platform),
130         },
131         {
132                 .name = "Loopback",
133                 .stream_name = "Loopback",
134                 .nonatomic = 1,
135                 .dynamic = 1,
136                 .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
137                 .dpcm_capture = 1,
138                 SND_SOC_DAILINK_REG(loopback, dummy, platform),
139         },
140
141         /* Back End DAI links */
142         {
143                 /* SSP0 - Codec */
144                 .name = "Codec",
145                 .id = 0,
146                 .no_pcm = 1,
147                 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
148                         SND_SOC_DAIFMT_CBC_CFC,
149                 .ignore_pmdown_time = 1,
150                 .be_hw_params_fixup = haswell_ssp0_fixup,
151                 .ops = &haswell_rt5640_ops,
152                 .dpcm_playback = 1,
153                 .dpcm_capture = 1,
154                 SND_SOC_DAILINK_REG(ssp0_port, codec, platform),
155         },
156 };
157
158 /* audio machine driver for Haswell Lynxpoint DSP + RT5640 */
159 static struct snd_soc_card haswell_rt5640 = {
160         .name = "haswell-rt5640",
161         .owner = THIS_MODULE,
162         .dai_link = haswell_rt5640_dais,
163         .num_links = ARRAY_SIZE(haswell_rt5640_dais),
164         .dapm_widgets = haswell_widgets,
165         .num_dapm_widgets = ARRAY_SIZE(haswell_widgets),
166         .dapm_routes = haswell_rt5640_map,
167         .num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map),
168         .fully_routed = true,
169 };
170
171 static int haswell_audio_probe(struct platform_device *pdev)
172 {
173         struct snd_soc_acpi_mach *mach;
174         int ret;
175
176         haswell_rt5640.dev = &pdev->dev;
177
178         /* override platform name, if required */
179         mach = pdev->dev.platform_data;
180         ret = snd_soc_fixup_dai_links_platform_name(&haswell_rt5640,
181                                                     mach->mach_params.platform);
182         if (ret)
183                 return ret;
184
185         return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
186 }
187
188 static struct platform_driver haswell_audio = {
189         .probe = haswell_audio_probe,
190         .driver = {
191                 .name = "haswell-audio",
192                 .pm = &snd_soc_pm_ops,
193         },
194 };
195
196 module_platform_driver(haswell_audio)
197
198 /* Module information */
199 MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
200 MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint");
201 MODULE_LICENSE("GPL v2");
202 MODULE_ALIAS("platform:haswell-audio");