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