• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Intel Haswell Lynxpoint SST Audio
3  *
4  * Copyright (C) 2013, Intel Corporation. All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License version
8  * 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16 
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <sound/core.h>
20 #include <sound/pcm.h>
21 #include <sound/soc.h>
22 #include <sound/pcm_params.h>
23 
24 #include "sst-dsp.h"
25 #include "sst-haswell-ipc.h"
26 
27 #include "../codecs/rt5640.h"
28 
29 /* Haswell ULT platforms have a Headphone and Mic jack */
30 static const struct snd_soc_dapm_widget haswell_widgets[] = {
31 	SND_SOC_DAPM_HP("Headphones", NULL),
32 	SND_SOC_DAPM_MIC("Mic", NULL),
33 };
34 
35 static const struct snd_soc_dapm_route haswell_rt5640_map[] = {
36 
37 	{"Headphones", NULL, "HPOR"},
38 	{"Headphones", NULL, "HPOL"},
39 	{"IN2P", NULL, "Mic"},
40 
41 	/* CODEC BE connections */
42 	{"SSP0 CODEC IN", NULL, "AIF1 Capture"},
43 	{"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
44 };
45 
haswell_ssp0_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)46 static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
47 			struct snd_pcm_hw_params *params)
48 {
49 	struct snd_interval *rate = hw_param_interval(params,
50 			SNDRV_PCM_HW_PARAM_RATE);
51 	struct snd_interval *channels = hw_param_interval(params,
52 						SNDRV_PCM_HW_PARAM_CHANNELS);
53 
54 	/* The ADSP will covert the FE rate to 48k, stereo */
55 	rate->min = rate->max = 48000;
56 	channels->min = channels->max = 2;
57 
58 	/* set SSP0 to 16 bit */
59 	snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
60 				    SNDRV_PCM_HW_PARAM_FIRST_MASK],
61 				    SNDRV_PCM_FORMAT_S16_LE);
62 	return 0;
63 }
64 
haswell_rt5640_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)65 static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream,
66 	struct snd_pcm_hw_params *params)
67 {
68 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
69 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
70 	int ret;
71 
72 	ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000,
73 		SND_SOC_CLOCK_IN);
74 
75 	if (ret < 0) {
76 		dev_err(rtd->dev, "can't set codec sysclk configuration\n");
77 		return ret;
78 	}
79 
80 	/* set correct codec filter for DAI format and clock config */
81 	snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000);
82 
83 	return ret;
84 }
85 
86 static struct snd_soc_ops haswell_rt5640_ops = {
87 	.hw_params = haswell_rt5640_hw_params,
88 };
89 
haswell_rtd_init(struct snd_soc_pcm_runtime * rtd)90 static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
91 {
92 	struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
93 	struct sst_hsw *haswell = pdata->dsp;
94 	int ret;
95 
96 	/* Set ADSP SSP port settings */
97 	ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0,
98 		SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
99 		SST_HSW_DEVICE_CLOCK_MASTER, 9);
100 	if (ret < 0) {
101 		dev_err(rtd->dev, "failed to set device config\n");
102 		return ret;
103 	}
104 
105 	return 0;
106 }
107 
108 static struct snd_soc_dai_link haswell_rt5640_dais[] = {
109 	/* Front End DAI links */
110 	{
111 		.name = "System",
112 		.stream_name = "System Playback",
113 		.cpu_dai_name = "System Pin",
114 		.platform_name = "haswell-pcm-audio",
115 		.dynamic = 1,
116 		.codec_name = "snd-soc-dummy",
117 		.codec_dai_name = "snd-soc-dummy-dai",
118 		.init = haswell_rtd_init,
119 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
120 		.dpcm_playback = 1,
121 	},
122 	{
123 		.name = "Offload0",
124 		.stream_name = "Offload0 Playback",
125 		.cpu_dai_name = "Offload0 Pin",
126 		.platform_name = "haswell-pcm-audio",
127 		.dynamic = 1,
128 		.codec_name = "snd-soc-dummy",
129 		.codec_dai_name = "snd-soc-dummy-dai",
130 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
131 		.dpcm_playback = 1,
132 	},
133 	{
134 		.name = "Offload1",
135 		.stream_name = "Offload1 Playback",
136 		.cpu_dai_name = "Offload1 Pin",
137 		.platform_name = "haswell-pcm-audio",
138 		.dynamic = 1,
139 		.codec_name = "snd-soc-dummy",
140 		.codec_dai_name = "snd-soc-dummy-dai",
141 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
142 		.dpcm_playback = 1,
143 	},
144 	{
145 		.name = "Loopback",
146 		.stream_name = "Loopback",
147 		.cpu_dai_name = "Loopback Pin",
148 		.platform_name = "haswell-pcm-audio",
149 		.dynamic = 0,
150 		.codec_name = "snd-soc-dummy",
151 		.codec_dai_name = "snd-soc-dummy-dai",
152 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
153 		.dpcm_capture = 1,
154 	},
155 	{
156 		.name = "Capture",
157 		.stream_name = "Capture",
158 		.cpu_dai_name = "Capture Pin",
159 		.platform_name = "haswell-pcm-audio",
160 		.dynamic = 1,
161 		.codec_name = "snd-soc-dummy",
162 		.codec_dai_name = "snd-soc-dummy-dai",
163 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
164 		.dpcm_capture = 1,
165 	},
166 
167 	/* Back End DAI links */
168 	{
169 		/* SSP0 - Codec */
170 		.name = "Codec",
171 		.be_id = 0,
172 		.cpu_dai_name = "snd-soc-dummy-dai",
173 		.platform_name = "snd-soc-dummy",
174 		.no_pcm = 1,
175 		.codec_name = "i2c-INT33CA:00",
176 		.codec_dai_name = "rt5640-aif1",
177 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
178 			SND_SOC_DAIFMT_CBS_CFS,
179 		.ignore_suspend = 1,
180 		.ignore_pmdown_time = 1,
181 		.be_hw_params_fixup = haswell_ssp0_fixup,
182 		.ops = &haswell_rt5640_ops,
183 		.dpcm_playback = 1,
184 		.dpcm_capture = 1,
185 	},
186 };
187 
188 /* audio machine driver for Haswell Lynxpoint DSP + RT5640 */
189 static struct snd_soc_card haswell_rt5640 = {
190 	.name = "haswell-rt5640",
191 	.owner = THIS_MODULE,
192 	.dai_link = haswell_rt5640_dais,
193 	.num_links = ARRAY_SIZE(haswell_rt5640_dais),
194 	.dapm_widgets = haswell_widgets,
195 	.num_dapm_widgets = ARRAY_SIZE(haswell_widgets),
196 	.dapm_routes = haswell_rt5640_map,
197 	.num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map),
198 	.fully_routed = true,
199 };
200 
haswell_audio_probe(struct platform_device * pdev)201 static int haswell_audio_probe(struct platform_device *pdev)
202 {
203 	haswell_rt5640.dev = &pdev->dev;
204 
205 	return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
206 }
207 
208 static struct platform_driver haswell_audio = {
209 	.probe = haswell_audio_probe,
210 	.driver = {
211 		.name = "haswell-audio",
212 		.owner = THIS_MODULE,
213 	},
214 };
215 
216 module_platform_driver(haswell_audio)
217 
218 /* Module information */
219 MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
220 MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint");
221 MODULE_LICENSE("GPL v2");
222 MODULE_ALIAS("platform:haswell-audio");
223