]>
Commit | Line | Data |
---|---|---|
473eb87a JK |
1 | /* |
2 | * skl-nhlt.c - Intel SKL Platform NHLT parsing | |
3 | * | |
4 | * Copyright (C) 2015 Intel Corp | |
5 | * Author: Sanjiv Kumar <sanjiv.kumar@intel.com> | |
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; version 2 of the License. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
18 | * | |
19 | */ | |
f65cf7d6 | 20 | #include <linux/pci.h> |
473eb87a | 21 | #include "skl.h" |
bc2bd45b | 22 | #include "skl-i2s.h" |
473eb87a | 23 | |
3b47c9dc PB |
24 | #define NHLT_ACPI_HEADER_SIG "NHLT" |
25 | ||
473eb87a | 26 | /* Unique identification for getting NHLT blobs */ |
94116f81 AS |
27 | static guid_t osc_guid = |
28 | GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, | |
29 | 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); | |
473eb87a | 30 | |
c286b3f9 | 31 | struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) |
473eb87a JK |
32 | { |
33 | acpi_handle handle; | |
34 | union acpi_object *obj; | |
35 | struct nhlt_resource_desc *nhlt_ptr = NULL; | |
c286b3f9 | 36 | struct nhlt_acpi_table *nhlt_table = NULL; |
473eb87a | 37 | |
6ad0005f VK |
38 | handle = ACPI_HANDLE(dev); |
39 | if (!handle) { | |
40 | dev_err(dev, "Didn't find ACPI_HANDLE\n"); | |
473eb87a JK |
41 | return NULL; |
42 | } | |
43 | ||
94116f81 | 44 | obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL); |
473eb87a JK |
45 | if (obj && obj->type == ACPI_TYPE_BUFFER) { |
46 | nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; | |
20a1ea22 TI |
47 | if (nhlt_ptr->length) |
48 | nhlt_table = (struct nhlt_acpi_table *) | |
c286b3f9 | 49 | memremap(nhlt_ptr->min_addr, nhlt_ptr->length, |
ba40a854 | 50 | MEMREMAP_WB); |
c286b3f9 | 51 | ACPI_FREE(obj); |
3b47c9dc PB |
52 | if (nhlt_table && (strncmp(nhlt_table->header.signature, |
53 | NHLT_ACPI_HEADER_SIG, | |
54 | strlen(NHLT_ACPI_HEADER_SIG)) != 0)) { | |
55 | memunmap(nhlt_table); | |
56 | dev_err(dev, "NHLT ACPI header signature incorrect\n"); | |
57 | return NULL; | |
58 | } | |
c286b3f9 | 59 | return nhlt_table; |
473eb87a JK |
60 | } |
61 | ||
62 | dev_err(dev, "device specific method to extract NHLT blob failed\n"); | |
63 | return NULL; | |
64 | } | |
65 | ||
c286b3f9 | 66 | void skl_nhlt_free(struct nhlt_acpi_table *nhlt) |
473eb87a | 67 | { |
c286b3f9 | 68 | memunmap((void *) nhlt); |
473eb87a JK |
69 | } |
70 | ||
71 | static struct nhlt_specific_cfg *skl_get_specific_cfg( | |
72 | struct device *dev, struct nhlt_fmt *fmt, | |
16882d24 | 73 | u8 no_ch, u32 rate, u16 bps, u8 linktype) |
473eb87a JK |
74 | { |
75 | struct nhlt_specific_cfg *sp_config; | |
76 | struct wav_fmt *wfmt; | |
77 | struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; | |
78 | int i; | |
79 | ||
80 | dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); | |
81 | ||
82 | for (i = 0; i < fmt->fmt_count; i++) { | |
83 | wfmt = &fmt_config->fmt_ext.fmt; | |
84 | dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, | |
85 | wfmt->bits_per_sample, wfmt->samples_per_sec); | |
16882d24 JK |
86 | if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { |
87 | /* | |
88 | * if link type is dmic ignore rate check as the blob is | |
89 | * generic for all rates | |
90 | */ | |
473eb87a | 91 | sp_config = &fmt_config->config; |
16882d24 JK |
92 | if (linktype == NHLT_LINK_DMIC) |
93 | return sp_config; | |
473eb87a | 94 | |
16882d24 JK |
95 | if (wfmt->samples_per_sec == rate) |
96 | return sp_config; | |
473eb87a JK |
97 | } |
98 | ||
99 | fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + | |
100 | fmt_config->config.size); | |
101 | } | |
102 | ||
103 | return NULL; | |
104 | } | |
105 | ||
106 | static void dump_config(struct device *dev, u32 instance_id, u8 linktype, | |
107 | u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) | |
108 | { | |
109 | dev_dbg(dev, "Input configuration\n"); | |
110 | dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); | |
111 | dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); | |
112 | dev_dbg(dev, "bits_per_sample=%d\n", bps); | |
113 | } | |
114 | ||
115 | static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, | |
db2f586b | 116 | u32 instance_id, u8 link_type, u8 dirn, u8 dev_type) |
473eb87a | 117 | { |
db2f586b SV |
118 | dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n", |
119 | epnt->virtual_bus_id, epnt->linktype, | |
120 | epnt->direction, epnt->device_type); | |
473eb87a JK |
121 | |
122 | if ((epnt->virtual_bus_id == instance_id) && | |
123 | (epnt->linktype == link_type) && | |
e02b0330 GS |
124 | (epnt->direction == dirn)) { |
125 | /* do not check dev_type for DMIC link type */ | |
126 | if (epnt->linktype == NHLT_LINK_DMIC) | |
127 | return true; | |
128 | ||
129 | if (epnt->device_type == dev_type) | |
130 | return true; | |
131 | } | |
132 | ||
133 | return false; | |
473eb87a JK |
134 | } |
135 | ||
136 | struct nhlt_specific_cfg | |
137 | *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, | |
db2f586b SV |
138 | u8 s_fmt, u8 num_ch, u32 s_rate, |
139 | u8 dirn, u8 dev_type) | |
473eb87a JK |
140 | { |
141 | struct nhlt_fmt *fmt; | |
142 | struct nhlt_endpoint *epnt; | |
143 | struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); | |
144 | struct device *dev = bus->dev; | |
145 | struct nhlt_specific_cfg *sp_config; | |
c286b3f9 | 146 | struct nhlt_acpi_table *nhlt = skl->nhlt; |
83b50246 | 147 | u16 bps = (s_fmt == 16) ? 16 : 32; |
473eb87a JK |
148 | u8 j; |
149 | ||
150 | dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); | |
151 | ||
152 | epnt = (struct nhlt_endpoint *)nhlt->desc; | |
153 | ||
154 | dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); | |
155 | ||
156 | for (j = 0; j < nhlt->endpoint_count; j++) { | |
db2f586b SV |
157 | if (skl_check_ep_match(dev, epnt, instance, link_type, |
158 | dirn, dev_type)) { | |
473eb87a JK |
159 | fmt = (struct nhlt_fmt *)(epnt->config.caps + |
160 | epnt->config.size); | |
16882d24 JK |
161 | sp_config = skl_get_specific_cfg(dev, fmt, num_ch, |
162 | s_rate, bps, link_type); | |
473eb87a JK |
163 | if (sp_config) |
164 | return sp_config; | |
165 | } | |
166 | ||
167 | epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); | |
168 | } | |
169 | ||
170 | return NULL; | |
171 | } | |
4b235c43 | 172 | |
f65cf7d6 YZ |
173 | int skl_get_dmic_geo(struct skl *skl) |
174 | { | |
175 | struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; | |
176 | struct nhlt_endpoint *epnt; | |
177 | struct nhlt_dmic_array_config *cfg; | |
178 | struct device *dev = &skl->pci->dev; | |
179 | unsigned int dmic_geo = 0; | |
180 | u8 j; | |
181 | ||
182 | epnt = (struct nhlt_endpoint *)nhlt->desc; | |
183 | ||
184 | for (j = 0; j < nhlt->endpoint_count; j++) { | |
185 | if (epnt->linktype == NHLT_LINK_DMIC) { | |
186 | cfg = (struct nhlt_dmic_array_config *) | |
187 | (epnt->config.caps); | |
188 | switch (cfg->array_type) { | |
189 | case NHLT_MIC_ARRAY_2CH_SMALL: | |
190 | case NHLT_MIC_ARRAY_2CH_BIG: | |
191 | dmic_geo |= MIC_ARRAY_2CH; | |
192 | break; | |
193 | ||
194 | case NHLT_MIC_ARRAY_4CH_1ST_GEOM: | |
195 | case NHLT_MIC_ARRAY_4CH_L_SHAPED: | |
196 | case NHLT_MIC_ARRAY_4CH_2ND_GEOM: | |
197 | dmic_geo |= MIC_ARRAY_4CH; | |
198 | break; | |
199 | ||
200 | default: | |
201 | dev_warn(dev, "undefined DMIC array_type 0x%0x\n", | |
202 | cfg->array_type); | |
203 | ||
204 | } | |
205 | } | |
206 | epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); | |
207 | } | |
208 | ||
209 | return dmic_geo; | |
210 | } | |
211 | ||
0cf5a171 | 212 | static void skl_nhlt_trim_space(char *trim) |
4b235c43 | 213 | { |
0cf5a171 | 214 | char *s = trim; |
4b235c43 VK |
215 | int cnt; |
216 | int i; | |
217 | ||
218 | cnt = 0; | |
219 | for (i = 0; s[i]; i++) { | |
220 | if (!isspace(s[i])) | |
221 | s[cnt++] = s[i]; | |
222 | } | |
223 | ||
224 | s[cnt] = '\0'; | |
225 | } | |
226 | ||
227 | int skl_nhlt_update_topology_bin(struct skl *skl) | |
228 | { | |
229 | struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; | |
230 | struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); | |
231 | struct device *dev = bus->dev; | |
232 | ||
233 | dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", | |
234 | nhlt->header.oem_id, nhlt->header.oem_table_id, | |
235 | nhlt->header.oem_revision); | |
236 | ||
237 | snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", | |
238 | skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, | |
239 | nhlt->header.oem_revision, "-tplg.bin"); | |
240 | ||
0cf5a171 | 241 | skl_nhlt_trim_space(skl->tplg_name); |
4b235c43 VK |
242 | |
243 | return 0; | |
244 | } | |
0cf5a171 SP |
245 | |
246 | static ssize_t skl_nhlt_platform_id_show(struct device *dev, | |
247 | struct device_attribute *attr, char *buf) | |
248 | { | |
249 | struct pci_dev *pci = to_pci_dev(dev); | |
250 | struct hdac_ext_bus *ebus = pci_get_drvdata(pci); | |
251 | struct skl *skl = ebus_to_skl(ebus); | |
252 | struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; | |
253 | char platform_id[32]; | |
254 | ||
255 | sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id, | |
256 | nhlt->header.oem_id, nhlt->header.oem_table_id, | |
257 | nhlt->header.oem_revision); | |
258 | ||
259 | skl_nhlt_trim_space(platform_id); | |
260 | return sprintf(buf, "%s\n", platform_id); | |
261 | } | |
262 | ||
263 | static DEVICE_ATTR(platform_id, 0444, skl_nhlt_platform_id_show, NULL); | |
264 | ||
265 | int skl_nhlt_create_sysfs(struct skl *skl) | |
266 | { | |
267 | struct device *dev = &skl->pci->dev; | |
268 | ||
269 | if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr)) | |
270 | dev_warn(dev, "Error creating sysfs entry\n"); | |
271 | ||
272 | return 0; | |
273 | } | |
274 | ||
275 | void skl_nhlt_remove_sysfs(struct skl *skl) | |
276 | { | |
277 | struct device *dev = &skl->pci->dev; | |
278 | ||
279 | sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); | |
280 | } | |
bc2bd45b SP |
281 | |
282 | /* | |
283 | * Queries NHLT for all the fmt configuration for a particular endpoint and | |
284 | * stores all possible rates supported in a rate table for the corresponding | |
285 | * sclk/sclkfs. | |
286 | */ | |
8e79ec98 | 287 | static void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks, |
bc2bd45b SP |
288 | struct nhlt_fmt *fmt, u8 id) |
289 | { | |
290 | struct skl_i2s_config_blob_legacy *i2s_config; | |
291 | struct skl_clk_parent_src *parent; | |
292 | struct skl_ssp_clk *sclk, *sclkfs; | |
293 | struct nhlt_fmt_cfg *fmt_cfg; | |
294 | struct wav_fmt_ext *wav_fmt; | |
295 | unsigned long rate = 0; | |
296 | bool present = false; | |
297 | int rate_index = 0; | |
298 | u16 channels, bps; | |
299 | u8 clk_src; | |
300 | int i, j; | |
301 | u32 fs; | |
302 | ||
303 | sclk = &ssp_clks[SKL_SCLK_OFS]; | |
304 | sclkfs = &ssp_clks[SKL_SCLKFS_OFS]; | |
305 | ||
306 | if (fmt->fmt_count == 0) | |
307 | return; | |
308 | ||
309 | for (i = 0; i < fmt->fmt_count; i++) { | |
310 | fmt_cfg = &fmt->fmt_config[i]; | |
311 | wav_fmt = &fmt_cfg->fmt_ext; | |
312 | ||
313 | channels = wav_fmt->fmt.channels; | |
314 | bps = wav_fmt->fmt.bits_per_sample; | |
315 | fs = wav_fmt->fmt.samples_per_sec; | |
316 | ||
317 | /* | |
318 | * In case of TDM configuration on a ssp, there can | |
319 | * be more than one blob in which channel masks are | |
320 | * different for each usecase for a specific rate and bps. | |
321 | * But the sclk rate will be generated for the total | |
322 | * number of channels used for that endpoint. | |
323 | * | |
324 | * So for the given fs and bps, choose blob which has | |
325 | * the superset of all channels for that endpoint and | |
326 | * derive the rate. | |
327 | */ | |
328 | for (j = i; j < fmt->fmt_count; j++) { | |
329 | fmt_cfg = &fmt->fmt_config[j]; | |
330 | wav_fmt = &fmt_cfg->fmt_ext; | |
331 | if ((fs == wav_fmt->fmt.samples_per_sec) && | |
332 | (bps == wav_fmt->fmt.bits_per_sample)) | |
333 | channels = max_t(u16, channels, | |
334 | wav_fmt->fmt.channels); | |
335 | } | |
336 | ||
337 | rate = channels * bps * fs; | |
338 | ||
339 | /* check if the rate is added already to the given SSP's sclk */ | |
87684d33 DC |
340 | for (j = 0; (j < SKL_MAX_CLK_RATES) && |
341 | (sclk[id].rate_cfg[j].rate != 0); j++) { | |
bc2bd45b SP |
342 | if (sclk[id].rate_cfg[j].rate == rate) { |
343 | present = true; | |
344 | break; | |
345 | } | |
346 | } | |
347 | ||
348 | /* Fill rate and parent for sclk/sclkfs */ | |
349 | if (!present) { | |
350 | /* MCLK Divider Source Select */ | |
351 | i2s_config = (struct skl_i2s_config_blob_legacy *) | |
352 | fmt->fmt_config[0].config.caps; | |
353 | clk_src = ((i2s_config->mclk.mdivctrl) | |
354 | & SKL_MNDSS_DIV_CLK_SRC_MASK) >> | |
355 | SKL_SHIFT(SKL_MNDSS_DIV_CLK_SRC_MASK); | |
356 | ||
357 | parent = skl_get_parent_clk(clk_src); | |
358 | ||
359 | /* | |
360 | * Do not copy the config data if there is no parent | |
361 | * clock available for this clock source select | |
362 | */ | |
363 | if (!parent) | |
364 | continue; | |
365 | ||
366 | sclk[id].rate_cfg[rate_index].rate = rate; | |
367 | sclk[id].rate_cfg[rate_index].config = fmt_cfg; | |
368 | sclkfs[id].rate_cfg[rate_index].rate = rate; | |
369 | sclkfs[id].rate_cfg[rate_index].config = fmt_cfg; | |
370 | sclk[id].parent_name = parent->name; | |
371 | sclkfs[id].parent_name = parent->name; | |
372 | ||
373 | rate_index++; | |
374 | } | |
375 | } | |
376 | } | |
377 | ||
8e79ec98 | 378 | static void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk, |
bc2bd45b SP |
379 | struct nhlt_fmt *fmt, u8 id) |
380 | { | |
381 | struct skl_i2s_config_blob_legacy *i2s_config; | |
382 | struct nhlt_specific_cfg *fmt_cfg; | |
383 | struct skl_clk_parent_src *parent; | |
384 | u32 clkdiv, div_ratio; | |
385 | u8 clk_src; | |
386 | ||
387 | fmt_cfg = &fmt->fmt_config[0].config; | |
388 | i2s_config = (struct skl_i2s_config_blob_legacy *)fmt_cfg->caps; | |
389 | ||
390 | /* MCLK Divider Source Select */ | |
391 | clk_src = ((i2s_config->mclk.mdivctrl) & SKL_MCLK_DIV_CLK_SRC_MASK) >> | |
392 | SKL_SHIFT(SKL_MCLK_DIV_CLK_SRC_MASK); | |
393 | ||
394 | clkdiv = i2s_config->mclk.mdivr & SKL_MCLK_DIV_RATIO_MASK; | |
395 | ||
396 | /* bypass divider */ | |
397 | div_ratio = 1; | |
398 | ||
399 | if (clkdiv != SKL_MCLK_DIV_RATIO_MASK) | |
400 | /* Divider is 2 + clkdiv */ | |
401 | div_ratio = clkdiv + 2; | |
402 | ||
403 | /* Calculate MCLK rate from source using div value */ | |
404 | parent = skl_get_parent_clk(clk_src); | |
405 | if (!parent) | |
406 | return; | |
407 | ||
408 | mclk[id].rate_cfg[0].rate = parent->rate/div_ratio; | |
409 | mclk[id].rate_cfg[0].config = &fmt->fmt_config[0]; | |
410 | mclk[id].parent_name = parent->name; | |
411 | } | |
412 | ||
413 | void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks) | |
414 | { | |
415 | struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; | |
416 | struct nhlt_endpoint *epnt; | |
417 | struct nhlt_fmt *fmt; | |
418 | int i; | |
419 | u8 id; | |
420 | ||
421 | epnt = (struct nhlt_endpoint *)nhlt->desc; | |
422 | for (i = 0; i < nhlt->endpoint_count; i++) { | |
423 | if (epnt->linktype == NHLT_LINK_SSP) { | |
424 | id = epnt->virtual_bus_id; | |
425 | ||
426 | fmt = (struct nhlt_fmt *)(epnt->config.caps | |
427 | + epnt->config.size); | |
428 | ||
429 | skl_get_ssp_clks(skl, ssp_clks, fmt, id); | |
430 | skl_get_mclk(skl, ssp_clks, fmt, id); | |
431 | } | |
432 | epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); | |
433 | } | |
434 | } |