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 "../common/sst-dsp.h"
25 #include "../haswell/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 	params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
60 	return 0;
61 }
62 
haswell_rt5640_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)63 static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream,
64 	struct snd_pcm_hw_params *params)
65 {
66 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
67 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
68 	int ret;
69 
70 	ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000,
71 		SND_SOC_CLOCK_IN);
72 
73 	if (ret < 0) {
74 		dev_err(rtd->dev, "can't set codec sysclk configuration\n");
75 		return ret;
76 	}
77 
78 	/* set correct codec filter for DAI format and clock config */
79 	snd_soc_component_update_bits(codec_dai->component, 0x83, 0xffff, 0x8000);
80 
81 	return ret;
82 }
83 
84 static const struct snd_soc_ops haswell_rt5640_ops = {
85 	.hw_params = haswell_rt5640_hw_params,
86 };
87 
haswell_rtd_init(struct snd_soc_pcm_runtime * rtd)88 static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
89 {
90 	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
91 	struct sst_pdata *pdata = dev_get_platdata(component->dev);
92 	struct sst_hsw *haswell = pdata->dsp;
93 	int ret;
94 
95 	/* Set ADSP SSP port settings */
96 	ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0,
97 		SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
98 		SST_HSW_DEVICE_CLOCK_MASTER, 9);
99 	if (ret < 0) {
100 		dev_err(rtd->dev, "failed to set device config\n");
101 		return ret;
102 	}
103 
104 	return 0;
105 }
106 
107 static struct snd_soc_dai_link haswell_rt5640_dais[] = {
108 	/* Front End DAI links */
109 	{
110 		.name = "System",
111 		.stream_name = "System Playback/Capture",
112 		.cpu_dai_name = "System Pin",
113 		.platform_name = "haswell-pcm-audio",
114 		.dynamic = 1,
115 		.codec_name = "snd-soc-dummy",
116 		.codec_dai_name = "snd-soc-dummy-dai",
117 		.init = haswell_rtd_init,
118 		.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
119 		.dpcm_playback = 1,
120 		.dpcm_capture = 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 	/* Back End DAI links */
157 	{
158 		/* SSP0 - Codec */
159 		.name = "Codec",
160 		.id = 0,
161 		.cpu_dai_name = "snd-soc-dummy-dai",
162 		.platform_name = "snd-soc-dummy",
163 		.no_pcm = 1,
164 		.codec_name = "i2c-INT33CA:00",
165 		.codec_dai_name = "rt5640-aif1",
166 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
167 			SND_SOC_DAIFMT_CBS_CFS,
168 		.ignore_suspend = 1,
169 		.ignore_pmdown_time = 1,
170 		.be_hw_params_fixup = haswell_ssp0_fixup,
171 		.ops = &haswell_rt5640_ops,
172 		.dpcm_playback = 1,
173 		.dpcm_capture = 1,
174 	},
175 };
176 
177 /* audio machine driver for Haswell Lynxpoint DSP + RT5640 */
178 static struct snd_soc_card haswell_rt5640 = {
179 	.name = "haswell-rt5640",
180 	.owner = THIS_MODULE,
181 	.dai_link = haswell_rt5640_dais,
182 	.num_links = ARRAY_SIZE(haswell_rt5640_dais),
183 	.dapm_widgets = haswell_widgets,
184 	.num_dapm_widgets = ARRAY_SIZE(haswell_widgets),
185 	.dapm_routes = haswell_rt5640_map,
186 	.num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map),
187 	.fully_routed = true,
188 };
189 
haswell_audio_probe(struct platform_device * pdev)190 static int haswell_audio_probe(struct platform_device *pdev)
191 {
192 	haswell_rt5640.dev = &pdev->dev;
193 
194 	return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
195 }
196 
197 static struct platform_driver haswell_audio = {
198 	.probe = haswell_audio_probe,
199 	.driver = {
200 		.name = "haswell-audio",
201 	},
202 };
203 
204 module_platform_driver(haswell_audio)
205 
206 /* Module information */
207 MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
208 MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint");
209 MODULE_LICENSE("GPL v2");
210 MODULE_ALIAS("platform:haswell-audio");
211