18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2473eb87aSJeeja KP /*
3473eb87aSJeeja KP * skl-nhlt.c - Intel SKL Platform NHLT parsing
4473eb87aSJeeja KP *
5473eb87aSJeeja KP * Copyright (C) 2015 Intel Corp
6473eb87aSJeeja KP * Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
7473eb87aSJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8473eb87aSJeeja KP *
9473eb87aSJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10473eb87aSJeeja KP */
11f65cf7d6SYong Zhi #include <linux/pci.h>
1263643b59SPierre-Louis Bossart #include <sound/intel-nhlt.h>
13473eb87aSJeeja KP #include "skl.h"
14bc2bd45bSSriram Periyasamy #include "skl-i2s.h"
15473eb87aSJeeja KP
skl_nhlt_trim_space(char * trim)160cf5a171SSubhransu S. Prusty static void skl_nhlt_trim_space(char *trim)
174b235c43SVinod Koul {
180cf5a171SSubhransu S. Prusty char *s = trim;
194b235c43SVinod Koul int cnt;
204b235c43SVinod Koul int i;
214b235c43SVinod Koul
224b235c43SVinod Koul cnt = 0;
234b235c43SVinod Koul for (i = 0; s[i]; i++) {
244b235c43SVinod Koul if (!isspace(s[i]))
254b235c43SVinod Koul s[cnt++] = s[i];
264b235c43SVinod Koul }
274b235c43SVinod Koul
284b235c43SVinod Koul s[cnt] = '\0';
294b235c43SVinod Koul }
304b235c43SVinod Koul
skl_nhlt_update_topology_bin(struct skl_dev * skl)31bcc2a2dcSCezary Rojewski int skl_nhlt_update_topology_bin(struct skl_dev *skl)
324b235c43SVinod Koul {
334b235c43SVinod Koul struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
3476f56faeSRakesh Ughreja struct hdac_bus *bus = skl_to_bus(skl);
354b235c43SVinod Koul struct device *dev = bus->dev;
364b235c43SVinod Koul
37855a06daSAmadeusz Sławiński dev_dbg(dev, "oem_id %.6s, oem_table_id %.8s oem_revision %d\n",
384b235c43SVinod Koul nhlt->header.oem_id, nhlt->header.oem_table_id,
394b235c43SVinod Koul nhlt->header.oem_revision);
404b235c43SVinod Koul
414b235c43SVinod Koul snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s",
424b235c43SVinod Koul skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,
434b235c43SVinod Koul nhlt->header.oem_revision, "-tplg.bin");
444b235c43SVinod Koul
450cf5a171SSubhransu S. Prusty skl_nhlt_trim_space(skl->tplg_name);
464b235c43SVinod Koul
474b235c43SVinod Koul return 0;
484b235c43SVinod Koul }
490cf5a171SSubhransu S. Prusty
platform_id_show(struct device * dev,struct device_attribute * attr,char * buf)50ae624a38SYueHaibing static ssize_t platform_id_show(struct device *dev,
510cf5a171SSubhransu S. Prusty struct device_attribute *attr, char *buf)
520cf5a171SSubhransu S. Prusty {
530cf5a171SSubhransu S. Prusty struct pci_dev *pci = to_pci_dev(dev);
5476f56faeSRakesh Ughreja struct hdac_bus *bus = pci_get_drvdata(pci);
55bcc2a2dcSCezary Rojewski struct skl_dev *skl = bus_to_skl(bus);
560cf5a171SSubhransu S. Prusty struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
570cf5a171SSubhransu S. Prusty char platform_id[32];
580cf5a171SSubhransu S. Prusty
590cf5a171SSubhransu S. Prusty sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id,
600cf5a171SSubhransu S. Prusty nhlt->header.oem_id, nhlt->header.oem_table_id,
610cf5a171SSubhransu S. Prusty nhlt->header.oem_revision);
620cf5a171SSubhransu S. Prusty
630cf5a171SSubhransu S. Prusty skl_nhlt_trim_space(platform_id);
64*11af2b1eSTakashi Iwai return sysfs_emit(buf, "%s\n", platform_id);
650cf5a171SSubhransu S. Prusty }
660cf5a171SSubhransu S. Prusty
67ae624a38SYueHaibing static DEVICE_ATTR_RO(platform_id);
680cf5a171SSubhransu S. Prusty
skl_nhlt_create_sysfs(struct skl_dev * skl)69bcc2a2dcSCezary Rojewski int skl_nhlt_create_sysfs(struct skl_dev *skl)
700cf5a171SSubhransu S. Prusty {
710cf5a171SSubhransu S. Prusty struct device *dev = &skl->pci->dev;
720cf5a171SSubhransu S. Prusty
730cf5a171SSubhransu S. Prusty if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr))
740cf5a171SSubhransu S. Prusty dev_warn(dev, "Error creating sysfs entry\n");
750cf5a171SSubhransu S. Prusty
760cf5a171SSubhransu S. Prusty return 0;
770cf5a171SSubhransu S. Prusty }
780cf5a171SSubhransu S. Prusty
skl_nhlt_remove_sysfs(struct skl_dev * skl)79bcc2a2dcSCezary Rojewski void skl_nhlt_remove_sysfs(struct skl_dev *skl)
800cf5a171SSubhransu S. Prusty {
810cf5a171SSubhransu S. Prusty struct device *dev = &skl->pci->dev;
820cf5a171SSubhransu S. Prusty
839e6c382fSCezary Rojewski if (skl->nhlt)
840cf5a171SSubhransu S. Prusty sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr);
850cf5a171SSubhransu S. Prusty }
86bc2bd45bSSriram Periyasamy
87bc2bd45bSSriram Periyasamy /*
88bc2bd45bSSriram Periyasamy * Queries NHLT for all the fmt configuration for a particular endpoint and
89bc2bd45bSSriram Periyasamy * stores all possible rates supported in a rate table for the corresponding
90bc2bd45bSSriram Periyasamy * sclk/sclkfs.
91bc2bd45bSSriram Periyasamy */
skl_get_ssp_clks(struct skl_dev * skl,struct skl_ssp_clk * ssp_clks,struct nhlt_fmt * fmt,u8 id)92bcc2a2dcSCezary Rojewski static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
93bc2bd45bSSriram Periyasamy struct nhlt_fmt *fmt, u8 id)
94bc2bd45bSSriram Periyasamy {
959afbc5ecSSriram Periyasamy struct skl_i2s_config_blob_ext *i2s_config_ext;
96bc2bd45bSSriram Periyasamy struct skl_i2s_config_blob_legacy *i2s_config;
97bc2bd45bSSriram Periyasamy struct skl_clk_parent_src *parent;
98bc2bd45bSSriram Periyasamy struct skl_ssp_clk *sclk, *sclkfs;
99bc2bd45bSSriram Periyasamy struct nhlt_fmt_cfg *fmt_cfg;
100bc2bd45bSSriram Periyasamy struct wav_fmt_ext *wav_fmt;
1018be54edbSPierre-Louis Bossart unsigned long rate;
102bc2bd45bSSriram Periyasamy int rate_index = 0;
103bc2bd45bSSriram Periyasamy u16 channels, bps;
104bc2bd45bSSriram Periyasamy u8 clk_src;
105bc2bd45bSSriram Periyasamy int i, j;
106bc2bd45bSSriram Periyasamy u32 fs;
107bc2bd45bSSriram Periyasamy
108bc2bd45bSSriram Periyasamy sclk = &ssp_clks[SKL_SCLK_OFS];
109bc2bd45bSSriram Periyasamy sclkfs = &ssp_clks[SKL_SCLKFS_OFS];
110bc2bd45bSSriram Periyasamy
111bc2bd45bSSriram Periyasamy if (fmt->fmt_count == 0)
112bc2bd45bSSriram Periyasamy return;
113bc2bd45bSSriram Periyasamy
114fc976f56SPeter Ujfalusi fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
115bc2bd45bSSriram Periyasamy for (i = 0; i < fmt->fmt_count; i++) {
116fc976f56SPeter Ujfalusi struct nhlt_fmt_cfg *saved_fmt_cfg = fmt_cfg;
117219af251SPeter Ujfalusi bool present = false;
118219af251SPeter Ujfalusi
119fc976f56SPeter Ujfalusi wav_fmt = &saved_fmt_cfg->fmt_ext;
120bc2bd45bSSriram Periyasamy
121bc2bd45bSSriram Periyasamy channels = wav_fmt->fmt.channels;
122bc2bd45bSSriram Periyasamy bps = wav_fmt->fmt.bits_per_sample;
123bc2bd45bSSriram Periyasamy fs = wav_fmt->fmt.samples_per_sec;
124bc2bd45bSSriram Periyasamy
125bc2bd45bSSriram Periyasamy /*
126bc2bd45bSSriram Periyasamy * In case of TDM configuration on a ssp, there can
127bc2bd45bSSriram Periyasamy * be more than one blob in which channel masks are
128bc2bd45bSSriram Periyasamy * different for each usecase for a specific rate and bps.
129bc2bd45bSSriram Periyasamy * But the sclk rate will be generated for the total
130bc2bd45bSSriram Periyasamy * number of channels used for that endpoint.
131bc2bd45bSSriram Periyasamy *
132bc2bd45bSSriram Periyasamy * So for the given fs and bps, choose blob which has
133bc2bd45bSSriram Periyasamy * the superset of all channels for that endpoint and
134bc2bd45bSSriram Periyasamy * derive the rate.
135bc2bd45bSSriram Periyasamy */
136bc2bd45bSSriram Periyasamy for (j = i; j < fmt->fmt_count; j++) {
137fc976f56SPeter Ujfalusi struct nhlt_fmt_cfg *tmp_fmt_cfg = fmt_cfg;
138fc976f56SPeter Ujfalusi
139fc976f56SPeter Ujfalusi wav_fmt = &tmp_fmt_cfg->fmt_ext;
140bc2bd45bSSriram Periyasamy if ((fs == wav_fmt->fmt.samples_per_sec) &&
141fc976f56SPeter Ujfalusi (bps == wav_fmt->fmt.bits_per_sample)) {
142bc2bd45bSSriram Periyasamy channels = max_t(u16, channels,
143bc2bd45bSSriram Periyasamy wav_fmt->fmt.channels);
144fc976f56SPeter Ujfalusi saved_fmt_cfg = tmp_fmt_cfg;
145fc976f56SPeter Ujfalusi }
146fc976f56SPeter Ujfalusi /* Move to the next nhlt_fmt_cfg */
147fc976f56SPeter Ujfalusi tmp_fmt_cfg = (struct nhlt_fmt_cfg *)(tmp_fmt_cfg->config.caps +
148fc976f56SPeter Ujfalusi tmp_fmt_cfg->config.size);
149bc2bd45bSSriram Periyasamy }
150bc2bd45bSSriram Periyasamy
151bc2bd45bSSriram Periyasamy rate = channels * bps * fs;
152bc2bd45bSSriram Periyasamy
153bc2bd45bSSriram Periyasamy /* check if the rate is added already to the given SSP's sclk */
15487684d33SDan Carpenter for (j = 0; (j < SKL_MAX_CLK_RATES) &&
15587684d33SDan Carpenter (sclk[id].rate_cfg[j].rate != 0); j++) {
156bc2bd45bSSriram Periyasamy if (sclk[id].rate_cfg[j].rate == rate) {
157bc2bd45bSSriram Periyasamy present = true;
158bc2bd45bSSriram Periyasamy break;
159bc2bd45bSSriram Periyasamy }
160bc2bd45bSSriram Periyasamy }
161bc2bd45bSSriram Periyasamy
162bc2bd45bSSriram Periyasamy /* Fill rate and parent for sclk/sclkfs */
163bc2bd45bSSriram Periyasamy if (!present) {
164fc976f56SPeter Ujfalusi struct nhlt_fmt_cfg *first_fmt_cfg;
165fc976f56SPeter Ujfalusi
166fc976f56SPeter Ujfalusi first_fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
1679afbc5ecSSriram Periyasamy i2s_config_ext = (struct skl_i2s_config_blob_ext *)
168fc976f56SPeter Ujfalusi first_fmt_cfg->config.caps;
1699afbc5ecSSriram Periyasamy
1709afbc5ecSSriram Periyasamy /* MCLK Divider Source Select */
1719afbc5ecSSriram Periyasamy if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
1729afbc5ecSSriram Periyasamy i2s_config = ext_to_legacy_blob(i2s_config_ext);
1739afbc5ecSSriram Periyasamy clk_src = get_clk_src(i2s_config->mclk,
1749afbc5ecSSriram Periyasamy SKL_MNDSS_DIV_CLK_SRC_MASK);
1759afbc5ecSSriram Periyasamy } else {
1769afbc5ecSSriram Periyasamy clk_src = get_clk_src(i2s_config_ext->mclk,
1779afbc5ecSSriram Periyasamy SKL_MNDSS_DIV_CLK_SRC_MASK);
1789afbc5ecSSriram Periyasamy }
179bc2bd45bSSriram Periyasamy
180bc2bd45bSSriram Periyasamy parent = skl_get_parent_clk(clk_src);
181bc2bd45bSSriram Periyasamy
182fc976f56SPeter Ujfalusi /* Move to the next nhlt_fmt_cfg */
183fc976f56SPeter Ujfalusi fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
184fc976f56SPeter Ujfalusi fmt_cfg->config.size);
185bc2bd45bSSriram Periyasamy /*
186bc2bd45bSSriram Periyasamy * Do not copy the config data if there is no parent
187bc2bd45bSSriram Periyasamy * clock available for this clock source select
188bc2bd45bSSriram Periyasamy */
189bc2bd45bSSriram Periyasamy if (!parent)
190bc2bd45bSSriram Periyasamy continue;
191bc2bd45bSSriram Periyasamy
192bc2bd45bSSriram Periyasamy sclk[id].rate_cfg[rate_index].rate = rate;
193fc976f56SPeter Ujfalusi sclk[id].rate_cfg[rate_index].config = saved_fmt_cfg;
194bc2bd45bSSriram Periyasamy sclkfs[id].rate_cfg[rate_index].rate = rate;
195fc976f56SPeter Ujfalusi sclkfs[id].rate_cfg[rate_index].config = saved_fmt_cfg;
196bc2bd45bSSriram Periyasamy sclk[id].parent_name = parent->name;
197bc2bd45bSSriram Periyasamy sclkfs[id].parent_name = parent->name;
198bc2bd45bSSriram Periyasamy
199bc2bd45bSSriram Periyasamy rate_index++;
200bc2bd45bSSriram Periyasamy }
201bc2bd45bSSriram Periyasamy }
202bc2bd45bSSriram Periyasamy }
203bc2bd45bSSriram Periyasamy
skl_get_mclk(struct skl_dev * skl,struct skl_ssp_clk * mclk,struct nhlt_fmt * fmt,u8 id)204bcc2a2dcSCezary Rojewski static void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk,
205bc2bd45bSSriram Periyasamy struct nhlt_fmt *fmt, u8 id)
206bc2bd45bSSriram Periyasamy {
2079afbc5ecSSriram Periyasamy struct skl_i2s_config_blob_ext *i2s_config_ext;
208bc2bd45bSSriram Periyasamy struct skl_i2s_config_blob_legacy *i2s_config;
209fc976f56SPeter Ujfalusi struct nhlt_fmt_cfg *fmt_cfg;
210bc2bd45bSSriram Periyasamy struct skl_clk_parent_src *parent;
211bc2bd45bSSriram Periyasamy u32 clkdiv, div_ratio;
212bc2bd45bSSriram Periyasamy u8 clk_src;
213bc2bd45bSSriram Periyasamy
214fc976f56SPeter Ujfalusi fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
215fc976f56SPeter Ujfalusi i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->config.caps;
216bc2bd45bSSriram Periyasamy
2179afbc5ecSSriram Periyasamy /* MCLK Divider Source Select and divider */
2189afbc5ecSSriram Periyasamy if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
2199afbc5ecSSriram Periyasamy i2s_config = ext_to_legacy_blob(i2s_config_ext);
2209afbc5ecSSriram Periyasamy clk_src = get_clk_src(i2s_config->mclk,
2219afbc5ecSSriram Periyasamy SKL_MCLK_DIV_CLK_SRC_MASK);
2229afbc5ecSSriram Periyasamy clkdiv = i2s_config->mclk.mdivr &
2239afbc5ecSSriram Periyasamy SKL_MCLK_DIV_RATIO_MASK;
2249afbc5ecSSriram Periyasamy } else {
2259afbc5ecSSriram Periyasamy clk_src = get_clk_src(i2s_config_ext->mclk,
2269afbc5ecSSriram Periyasamy SKL_MCLK_DIV_CLK_SRC_MASK);
2279afbc5ecSSriram Periyasamy clkdiv = i2s_config_ext->mclk.mdivr[0] &
2289afbc5ecSSriram Periyasamy SKL_MCLK_DIV_RATIO_MASK;
2299afbc5ecSSriram Periyasamy }
230bc2bd45bSSriram Periyasamy
231bc2bd45bSSriram Periyasamy /* bypass divider */
232bc2bd45bSSriram Periyasamy div_ratio = 1;
233bc2bd45bSSriram Periyasamy
234bc2bd45bSSriram Periyasamy if (clkdiv != SKL_MCLK_DIV_RATIO_MASK)
235bc2bd45bSSriram Periyasamy /* Divider is 2 + clkdiv */
236bc2bd45bSSriram Periyasamy div_ratio = clkdiv + 2;
237bc2bd45bSSriram Periyasamy
238bc2bd45bSSriram Periyasamy /* Calculate MCLK rate from source using div value */
239bc2bd45bSSriram Periyasamy parent = skl_get_parent_clk(clk_src);
240bc2bd45bSSriram Periyasamy if (!parent)
241bc2bd45bSSriram Periyasamy return;
242bc2bd45bSSriram Periyasamy
243bc2bd45bSSriram Periyasamy mclk[id].rate_cfg[0].rate = parent->rate/div_ratio;
244fc976f56SPeter Ujfalusi mclk[id].rate_cfg[0].config = fmt_cfg;
245bc2bd45bSSriram Periyasamy mclk[id].parent_name = parent->name;
246bc2bd45bSSriram Periyasamy }
247bc2bd45bSSriram Periyasamy
skl_get_clks(struct skl_dev * skl,struct skl_ssp_clk * ssp_clks)248bcc2a2dcSCezary Rojewski void skl_get_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks)
249bc2bd45bSSriram Periyasamy {
250bc2bd45bSSriram Periyasamy struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
251bc2bd45bSSriram Periyasamy struct nhlt_endpoint *epnt;
252bc2bd45bSSriram Periyasamy struct nhlt_fmt *fmt;
253bc2bd45bSSriram Periyasamy int i;
254bc2bd45bSSriram Periyasamy u8 id;
255bc2bd45bSSriram Periyasamy
256bc2bd45bSSriram Periyasamy epnt = (struct nhlt_endpoint *)nhlt->desc;
257bc2bd45bSSriram Periyasamy for (i = 0; i < nhlt->endpoint_count; i++) {
258bc2bd45bSSriram Periyasamy if (epnt->linktype == NHLT_LINK_SSP) {
259bc2bd45bSSriram Periyasamy id = epnt->virtual_bus_id;
260bc2bd45bSSriram Periyasamy
261bc2bd45bSSriram Periyasamy fmt = (struct nhlt_fmt *)(epnt->config.caps
262bc2bd45bSSriram Periyasamy + epnt->config.size);
263bc2bd45bSSriram Periyasamy
264bc2bd45bSSriram Periyasamy skl_get_ssp_clks(skl, ssp_clks, fmt, id);
265bc2bd45bSSriram Periyasamy skl_get_mclk(skl, ssp_clks, fmt, id);
266bc2bd45bSSriram Periyasamy }
267bc2bd45bSSriram Periyasamy epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
268bc2bd45bSSriram Periyasamy }
269bc2bd45bSSriram Periyasamy }
270