1 // SPDX-License-Identifier: GPL-2.0-only
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 *
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 */
11 #include <linux/pci.h>
12 #include <sound/intel-nhlt.h>
13 #include "skl.h"
14 #include "skl-i2s.h"
15
skl_nhlt_trim_space(char * trim)16 static void skl_nhlt_trim_space(char *trim)
17 {
18 char *s = trim;
19 int cnt;
20 int i;
21
22 cnt = 0;
23 for (i = 0; s[i]; i++) {
24 if (!isspace(s[i]))
25 s[cnt++] = s[i];
26 }
27
28 s[cnt] = '\0';
29 }
30
skl_nhlt_update_topology_bin(struct skl_dev * skl)31 int skl_nhlt_update_topology_bin(struct skl_dev *skl)
32 {
33 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
34 struct hdac_bus *bus = skl_to_bus(skl);
35 struct device *dev = bus->dev;
36
37 dev_dbg(dev, "oem_id %.6s, oem_table_id %.8s oem_revision %d\n",
38 nhlt->header.oem_id, nhlt->header.oem_table_id,
39 nhlt->header.oem_revision);
40
41 snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s",
42 skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,
43 nhlt->header.oem_revision, "-tplg.bin");
44
45 skl_nhlt_trim_space(skl->tplg_name);
46
47 return 0;
48 }
49
platform_id_show(struct device * dev,struct device_attribute * attr,char * buf)50 static ssize_t platform_id_show(struct device *dev,
51 struct device_attribute *attr, char *buf)
52 {
53 struct pci_dev *pci = to_pci_dev(dev);
54 struct hdac_bus *bus = pci_get_drvdata(pci);
55 struct skl_dev *skl = bus_to_skl(bus);
56 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
57 char platform_id[32];
58
59 sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id,
60 nhlt->header.oem_id, nhlt->header.oem_table_id,
61 nhlt->header.oem_revision);
62
63 skl_nhlt_trim_space(platform_id);
64 return sysfs_emit(buf, "%s\n", platform_id);
65 }
66
67 static DEVICE_ATTR_RO(platform_id);
68
skl_nhlt_create_sysfs(struct skl_dev * skl)69 int skl_nhlt_create_sysfs(struct skl_dev *skl)
70 {
71 struct device *dev = &skl->pci->dev;
72
73 if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr))
74 dev_warn(dev, "Error creating sysfs entry\n");
75
76 return 0;
77 }
78
skl_nhlt_remove_sysfs(struct skl_dev * skl)79 void skl_nhlt_remove_sysfs(struct skl_dev *skl)
80 {
81 struct device *dev = &skl->pci->dev;
82
83 if (skl->nhlt)
84 sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr);
85 }
86
87 /*
88 * Queries NHLT for all the fmt configuration for a particular endpoint and
89 * stores all possible rates supported in a rate table for the corresponding
90 * sclk/sclkfs.
91 */
skl_get_ssp_clks(struct skl_dev * skl,struct skl_ssp_clk * ssp_clks,struct nhlt_fmt * fmt,u8 id)92 static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
93 struct nhlt_fmt *fmt, u8 id)
94 {
95 struct skl_i2s_config_blob_ext *i2s_config_ext;
96 struct skl_i2s_config_blob_legacy *i2s_config;
97 struct skl_clk_parent_src *parent;
98 struct skl_ssp_clk *sclk, *sclkfs;
99 struct nhlt_fmt_cfg *fmt_cfg;
100 struct wav_fmt_ext *wav_fmt;
101 unsigned long rate;
102 int rate_index = 0;
103 u16 channels, bps;
104 u8 clk_src;
105 int i, j;
106 u32 fs;
107
108 sclk = &ssp_clks[SKL_SCLK_OFS];
109 sclkfs = &ssp_clks[SKL_SCLKFS_OFS];
110
111 if (fmt->fmt_count == 0)
112 return;
113
114 fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
115 for (i = 0; i < fmt->fmt_count; i++) {
116 struct nhlt_fmt_cfg *saved_fmt_cfg = fmt_cfg;
117 bool present = false;
118
119 wav_fmt = &saved_fmt_cfg->fmt_ext;
120
121 channels = wav_fmt->fmt.channels;
122 bps = wav_fmt->fmt.bits_per_sample;
123 fs = wav_fmt->fmt.samples_per_sec;
124
125 /*
126 * In case of TDM configuration on a ssp, there can
127 * be more than one blob in which channel masks are
128 * different for each usecase for a specific rate and bps.
129 * But the sclk rate will be generated for the total
130 * number of channels used for that endpoint.
131 *
132 * So for the given fs and bps, choose blob which has
133 * the superset of all channels for that endpoint and
134 * derive the rate.
135 */
136 for (j = i; j < fmt->fmt_count; j++) {
137 struct nhlt_fmt_cfg *tmp_fmt_cfg = fmt_cfg;
138
139 wav_fmt = &tmp_fmt_cfg->fmt_ext;
140 if ((fs == wav_fmt->fmt.samples_per_sec) &&
141 (bps == wav_fmt->fmt.bits_per_sample)) {
142 channels = max_t(u16, channels,
143 wav_fmt->fmt.channels);
144 saved_fmt_cfg = tmp_fmt_cfg;
145 }
146 /* Move to the next nhlt_fmt_cfg */
147 tmp_fmt_cfg = (struct nhlt_fmt_cfg *)(tmp_fmt_cfg->config.caps +
148 tmp_fmt_cfg->config.size);
149 }
150
151 rate = channels * bps * fs;
152
153 /* check if the rate is added already to the given SSP's sclk */
154 for (j = 0; (j < SKL_MAX_CLK_RATES) &&
155 (sclk[id].rate_cfg[j].rate != 0); j++) {
156 if (sclk[id].rate_cfg[j].rate == rate) {
157 present = true;
158 break;
159 }
160 }
161
162 /* Fill rate and parent for sclk/sclkfs */
163 if (!present) {
164 struct nhlt_fmt_cfg *first_fmt_cfg;
165
166 first_fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
167 i2s_config_ext = (struct skl_i2s_config_blob_ext *)
168 first_fmt_cfg->config.caps;
169
170 /* MCLK Divider Source Select */
171 if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
172 i2s_config = ext_to_legacy_blob(i2s_config_ext);
173 clk_src = get_clk_src(i2s_config->mclk,
174 SKL_MNDSS_DIV_CLK_SRC_MASK);
175 } else {
176 clk_src = get_clk_src(i2s_config_ext->mclk,
177 SKL_MNDSS_DIV_CLK_SRC_MASK);
178 }
179
180 parent = skl_get_parent_clk(clk_src);
181
182 /* Move to the next nhlt_fmt_cfg */
183 fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
184 fmt_cfg->config.size);
185 /*
186 * Do not copy the config data if there is no parent
187 * clock available for this clock source select
188 */
189 if (!parent)
190 continue;
191
192 sclk[id].rate_cfg[rate_index].rate = rate;
193 sclk[id].rate_cfg[rate_index].config = saved_fmt_cfg;
194 sclkfs[id].rate_cfg[rate_index].rate = rate;
195 sclkfs[id].rate_cfg[rate_index].config = saved_fmt_cfg;
196 sclk[id].parent_name = parent->name;
197 sclkfs[id].parent_name = parent->name;
198
199 rate_index++;
200 }
201 }
202 }
203
skl_get_mclk(struct skl_dev * skl,struct skl_ssp_clk * mclk,struct nhlt_fmt * fmt,u8 id)204 static void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk,
205 struct nhlt_fmt *fmt, u8 id)
206 {
207 struct skl_i2s_config_blob_ext *i2s_config_ext;
208 struct skl_i2s_config_blob_legacy *i2s_config;
209 struct nhlt_fmt_cfg *fmt_cfg;
210 struct skl_clk_parent_src *parent;
211 u32 clkdiv, div_ratio;
212 u8 clk_src;
213
214 fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
215 i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->config.caps;
216
217 /* MCLK Divider Source Select and divider */
218 if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
219 i2s_config = ext_to_legacy_blob(i2s_config_ext);
220 clk_src = get_clk_src(i2s_config->mclk,
221 SKL_MCLK_DIV_CLK_SRC_MASK);
222 clkdiv = i2s_config->mclk.mdivr &
223 SKL_MCLK_DIV_RATIO_MASK;
224 } else {
225 clk_src = get_clk_src(i2s_config_ext->mclk,
226 SKL_MCLK_DIV_CLK_SRC_MASK);
227 clkdiv = i2s_config_ext->mclk.mdivr[0] &
228 SKL_MCLK_DIV_RATIO_MASK;
229 }
230
231 /* bypass divider */
232 div_ratio = 1;
233
234 if (clkdiv != SKL_MCLK_DIV_RATIO_MASK)
235 /* Divider is 2 + clkdiv */
236 div_ratio = clkdiv + 2;
237
238 /* Calculate MCLK rate from source using div value */
239 parent = skl_get_parent_clk(clk_src);
240 if (!parent)
241 return;
242
243 mclk[id].rate_cfg[0].rate = parent->rate/div_ratio;
244 mclk[id].rate_cfg[0].config = fmt_cfg;
245 mclk[id].parent_name = parent->name;
246 }
247
skl_get_clks(struct skl_dev * skl,struct skl_ssp_clk * ssp_clks)248 void skl_get_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks)
249 {
250 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
251 struct nhlt_endpoint *epnt;
252 struct nhlt_fmt *fmt;
253 int i;
254 u8 id;
255
256 epnt = (struct nhlt_endpoint *)nhlt->desc;
257 for (i = 0; i < nhlt->endpoint_count; i++) {
258 if (epnt->linktype == NHLT_LINK_SSP) {
259 id = epnt->virtual_bus_id;
260
261 fmt = (struct nhlt_fmt *)(epnt->config.caps
262 + epnt->config.size);
263
264 skl_get_ssp_clks(skl, ssp_clks, fmt, id);
265 skl_get_mclk(skl, ssp_clks, fmt, id);
266 }
267 epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
268 }
269 }
270