1 // SPDX-License-Identifier: GPL-2.0-only
3 * skl-nhlt.c - Intel SKL Platform NHLT parsing
5 * Copyright (C) 2015 Intel Corp
6 * Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 #include <linux/pci.h>
12 #include <sound/intel-nhlt.h>
16 static struct nhlt_specific_cfg
*skl_get_specific_cfg(
17 struct device
*dev
, struct nhlt_fmt
*fmt
,
18 u8 no_ch
, u32 rate
, u16 bps
, u8 linktype
)
20 struct nhlt_specific_cfg
*sp_config
;
22 struct nhlt_fmt_cfg
*fmt_config
= fmt
->fmt_config
;
25 dev_dbg(dev
, "Format count =%d\n", fmt
->fmt_count
);
27 for (i
= 0; i
< fmt
->fmt_count
; i
++) {
28 wfmt
= &fmt_config
->fmt_ext
.fmt
;
29 dev_dbg(dev
, "ch=%d fmt=%d s_rate=%d\n", wfmt
->channels
,
30 wfmt
->bits_per_sample
, wfmt
->samples_per_sec
);
31 if (wfmt
->channels
== no_ch
&& wfmt
->bits_per_sample
== bps
) {
33 * if link type is dmic ignore rate check as the blob is
34 * generic for all rates
36 sp_config
= &fmt_config
->config
;
37 if (linktype
== NHLT_LINK_DMIC
)
40 if (wfmt
->samples_per_sec
== rate
)
44 fmt_config
= (struct nhlt_fmt_cfg
*)(fmt_config
->config
.caps
+
45 fmt_config
->config
.size
);
51 static void dump_config(struct device
*dev
, u32 instance_id
, u8 linktype
,
52 u8 s_fmt
, u8 num_channels
, u32 s_rate
, u8 dirn
, u16 bps
)
54 dev_dbg(dev
, "Input configuration\n");
55 dev_dbg(dev
, "ch=%d fmt=%d s_rate=%d\n", num_channels
, s_fmt
, s_rate
);
56 dev_dbg(dev
, "vbus_id=%d link_type=%d\n", instance_id
, linktype
);
57 dev_dbg(dev
, "bits_per_sample=%d\n", bps
);
60 static bool skl_check_ep_match(struct device
*dev
, struct nhlt_endpoint
*epnt
,
61 u32 instance_id
, u8 link_type
, u8 dirn
, u8 dev_type
)
63 dev_dbg(dev
, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
64 epnt
->virtual_bus_id
, epnt
->linktype
,
65 epnt
->direction
, epnt
->device_type
);
67 if ((epnt
->virtual_bus_id
== instance_id
) &&
68 (epnt
->linktype
== link_type
) &&
69 (epnt
->direction
== dirn
)) {
70 /* do not check dev_type for DMIC link type */
71 if (epnt
->linktype
== NHLT_LINK_DMIC
)
74 if (epnt
->device_type
== dev_type
)
81 struct nhlt_specific_cfg
82 *skl_get_ep_blob(struct skl_dev
*skl
, u32 instance
, u8 link_type
,
83 u8 s_fmt
, u8 num_ch
, u32 s_rate
,
87 struct nhlt_endpoint
*epnt
;
88 struct hdac_bus
*bus
= skl_to_bus(skl
);
89 struct device
*dev
= bus
->dev
;
90 struct nhlt_specific_cfg
*sp_config
;
91 struct nhlt_acpi_table
*nhlt
= skl
->nhlt
;
92 u16 bps
= (s_fmt
== 16) ? 16 : 32;
95 dump_config(dev
, instance
, link_type
, s_fmt
, num_ch
, s_rate
, dirn
, bps
);
97 epnt
= (struct nhlt_endpoint
*)nhlt
->desc
;
99 dev_dbg(dev
, "endpoint count =%d\n", nhlt
->endpoint_count
);
101 for (j
= 0; j
< nhlt
->endpoint_count
; j
++) {
102 if (skl_check_ep_match(dev
, epnt
, instance
, link_type
,
104 fmt
= (struct nhlt_fmt
*)(epnt
->config
.caps
+
106 sp_config
= skl_get_specific_cfg(dev
, fmt
, num_ch
,
107 s_rate
, bps
, link_type
);
112 epnt
= (struct nhlt_endpoint
*)((u8
*)epnt
+ epnt
->length
);
118 static void skl_nhlt_trim_space(char *trim
)
125 for (i
= 0; s
[i
]; i
++) {
133 int skl_nhlt_update_topology_bin(struct skl_dev
*skl
)
135 struct nhlt_acpi_table
*nhlt
= (struct nhlt_acpi_table
*)skl
->nhlt
;
136 struct hdac_bus
*bus
= skl_to_bus(skl
);
137 struct device
*dev
= bus
->dev
;
139 dev_dbg(dev
, "oem_id %.6s, oem_table_id %.8s oem_revision %d\n",
140 nhlt
->header
.oem_id
, nhlt
->header
.oem_table_id
,
141 nhlt
->header
.oem_revision
);
143 snprintf(skl
->tplg_name
, sizeof(skl
->tplg_name
), "%x-%.6s-%.8s-%d%s",
144 skl
->pci_id
, nhlt
->header
.oem_id
, nhlt
->header
.oem_table_id
,
145 nhlt
->header
.oem_revision
, "-tplg.bin");
147 skl_nhlt_trim_space(skl
->tplg_name
);
152 static ssize_t
skl_nhlt_platform_id_show(struct device
*dev
,
153 struct device_attribute
*attr
, char *buf
)
155 struct pci_dev
*pci
= to_pci_dev(dev
);
156 struct hdac_bus
*bus
= pci_get_drvdata(pci
);
157 struct skl_dev
*skl
= bus_to_skl(bus
);
158 struct nhlt_acpi_table
*nhlt
= (struct nhlt_acpi_table
*)skl
->nhlt
;
159 char platform_id
[32];
161 sprintf(platform_id
, "%x-%.6s-%.8s-%d", skl
->pci_id
,
162 nhlt
->header
.oem_id
, nhlt
->header
.oem_table_id
,
163 nhlt
->header
.oem_revision
);
165 skl_nhlt_trim_space(platform_id
);
166 return sprintf(buf
, "%s\n", platform_id
);
169 static DEVICE_ATTR(platform_id
, 0444, skl_nhlt_platform_id_show
, NULL
);
171 int skl_nhlt_create_sysfs(struct skl_dev
*skl
)
173 struct device
*dev
= &skl
->pci
->dev
;
175 if (sysfs_create_file(&dev
->kobj
, &dev_attr_platform_id
.attr
))
176 dev_warn(dev
, "Error creating sysfs entry\n");
181 void skl_nhlt_remove_sysfs(struct skl_dev
*skl
)
183 struct device
*dev
= &skl
->pci
->dev
;
185 sysfs_remove_file(&dev
->kobj
, &dev_attr_platform_id
.attr
);
189 * Queries NHLT for all the fmt configuration for a particular endpoint and
190 * stores all possible rates supported in a rate table for the corresponding
193 static void skl_get_ssp_clks(struct skl_dev
*skl
, struct skl_ssp_clk
*ssp_clks
,
194 struct nhlt_fmt
*fmt
, u8 id
)
196 struct skl_i2s_config_blob_ext
*i2s_config_ext
;
197 struct skl_i2s_config_blob_legacy
*i2s_config
;
198 struct skl_clk_parent_src
*parent
;
199 struct skl_ssp_clk
*sclk
, *sclkfs
;
200 struct nhlt_fmt_cfg
*fmt_cfg
;
201 struct wav_fmt_ext
*wav_fmt
;
202 unsigned long rate
= 0;
203 bool present
= false;
210 sclk
= &ssp_clks
[SKL_SCLK_OFS
];
211 sclkfs
= &ssp_clks
[SKL_SCLKFS_OFS
];
213 if (fmt
->fmt_count
== 0)
216 for (i
= 0; i
< fmt
->fmt_count
; i
++) {
217 fmt_cfg
= &fmt
->fmt_config
[i
];
218 wav_fmt
= &fmt_cfg
->fmt_ext
;
220 channels
= wav_fmt
->fmt
.channels
;
221 bps
= wav_fmt
->fmt
.bits_per_sample
;
222 fs
= wav_fmt
->fmt
.samples_per_sec
;
225 * In case of TDM configuration on a ssp, there can
226 * be more than one blob in which channel masks are
227 * different for each usecase for a specific rate and bps.
228 * But the sclk rate will be generated for the total
229 * number of channels used for that endpoint.
231 * So for the given fs and bps, choose blob which has
232 * the superset of all channels for that endpoint and
235 for (j
= i
; j
< fmt
->fmt_count
; j
++) {
236 fmt_cfg
= &fmt
->fmt_config
[j
];
237 wav_fmt
= &fmt_cfg
->fmt_ext
;
238 if ((fs
== wav_fmt
->fmt
.samples_per_sec
) &&
239 (bps
== wav_fmt
->fmt
.bits_per_sample
))
240 channels
= max_t(u16
, channels
,
241 wav_fmt
->fmt
.channels
);
244 rate
= channels
* bps
* fs
;
246 /* check if the rate is added already to the given SSP's sclk */
247 for (j
= 0; (j
< SKL_MAX_CLK_RATES
) &&
248 (sclk
[id
].rate_cfg
[j
].rate
!= 0); j
++) {
249 if (sclk
[id
].rate_cfg
[j
].rate
== rate
) {
255 /* Fill rate and parent for sclk/sclkfs */
257 i2s_config_ext
= (struct skl_i2s_config_blob_ext
*)
258 fmt
->fmt_config
[0].config
.caps
;
260 /* MCLK Divider Source Select */
261 if (is_legacy_blob(i2s_config_ext
->hdr
.sig
)) {
262 i2s_config
= ext_to_legacy_blob(i2s_config_ext
);
263 clk_src
= get_clk_src(i2s_config
->mclk
,
264 SKL_MNDSS_DIV_CLK_SRC_MASK
);
266 clk_src
= get_clk_src(i2s_config_ext
->mclk
,
267 SKL_MNDSS_DIV_CLK_SRC_MASK
);
270 parent
= skl_get_parent_clk(clk_src
);
273 * Do not copy the config data if there is no parent
274 * clock available for this clock source select
279 sclk
[id
].rate_cfg
[rate_index
].rate
= rate
;
280 sclk
[id
].rate_cfg
[rate_index
].config
= fmt_cfg
;
281 sclkfs
[id
].rate_cfg
[rate_index
].rate
= rate
;
282 sclkfs
[id
].rate_cfg
[rate_index
].config
= fmt_cfg
;
283 sclk
[id
].parent_name
= parent
->name
;
284 sclkfs
[id
].parent_name
= parent
->name
;
291 static void skl_get_mclk(struct skl_dev
*skl
, struct skl_ssp_clk
*mclk
,
292 struct nhlt_fmt
*fmt
, u8 id
)
294 struct skl_i2s_config_blob_ext
*i2s_config_ext
;
295 struct skl_i2s_config_blob_legacy
*i2s_config
;
296 struct nhlt_specific_cfg
*fmt_cfg
;
297 struct skl_clk_parent_src
*parent
;
298 u32 clkdiv
, div_ratio
;
301 fmt_cfg
= &fmt
->fmt_config
[0].config
;
302 i2s_config_ext
= (struct skl_i2s_config_blob_ext
*)fmt_cfg
->caps
;
304 /* MCLK Divider Source Select and divider */
305 if (is_legacy_blob(i2s_config_ext
->hdr
.sig
)) {
306 i2s_config
= ext_to_legacy_blob(i2s_config_ext
);
307 clk_src
= get_clk_src(i2s_config
->mclk
,
308 SKL_MCLK_DIV_CLK_SRC_MASK
);
309 clkdiv
= i2s_config
->mclk
.mdivr
&
310 SKL_MCLK_DIV_RATIO_MASK
;
312 clk_src
= get_clk_src(i2s_config_ext
->mclk
,
313 SKL_MCLK_DIV_CLK_SRC_MASK
);
314 clkdiv
= i2s_config_ext
->mclk
.mdivr
[0] &
315 SKL_MCLK_DIV_RATIO_MASK
;
321 if (clkdiv
!= SKL_MCLK_DIV_RATIO_MASK
)
322 /* Divider is 2 + clkdiv */
323 div_ratio
= clkdiv
+ 2;
325 /* Calculate MCLK rate from source using div value */
326 parent
= skl_get_parent_clk(clk_src
);
330 mclk
[id
].rate_cfg
[0].rate
= parent
->rate
/div_ratio
;
331 mclk
[id
].rate_cfg
[0].config
= &fmt
->fmt_config
[0];
332 mclk
[id
].parent_name
= parent
->name
;
335 void skl_get_clks(struct skl_dev
*skl
, struct skl_ssp_clk
*ssp_clks
)
337 struct nhlt_acpi_table
*nhlt
= (struct nhlt_acpi_table
*)skl
->nhlt
;
338 struct nhlt_endpoint
*epnt
;
339 struct nhlt_fmt
*fmt
;
343 epnt
= (struct nhlt_endpoint
*)nhlt
->desc
;
344 for (i
= 0; i
< nhlt
->endpoint_count
; i
++) {
345 if (epnt
->linktype
== NHLT_LINK_SSP
) {
346 id
= epnt
->virtual_bus_id
;
348 fmt
= (struct nhlt_fmt
*)(epnt
->config
.caps
349 + epnt
->config
.size
);
351 skl_get_ssp_clks(skl
, ssp_clks
, fmt
, id
);
352 skl_get_mclk(skl
, ssp_clks
, fmt
, id
);
354 epnt
= (struct nhlt_endpoint
*)((u8
*)epnt
+ epnt
->length
);