xref: /openbmc/linux/sound/soc/intel/skylake/skl-nhlt.c (revision 219af251)
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 
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 
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 
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);
640cf5a171SSubhransu S. Prusty 	return sprintf(buf, "%s\n", platform_id);
650cf5a171SSubhransu S. Prusty }
660cf5a171SSubhransu S. Prusty 
67ae624a38SYueHaibing static DEVICE_ATTR_RO(platform_id);
680cf5a171SSubhransu S. Prusty 
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 
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  */
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 
114bc2bd45bSSriram Periyasamy 	for (i = 0; i < fmt->fmt_count; i++) {
115*219af251SPeter Ujfalusi 		bool present = false;
116*219af251SPeter Ujfalusi 
117bc2bd45bSSriram Periyasamy 		fmt_cfg = &fmt->fmt_config[i];
118bc2bd45bSSriram Periyasamy 		wav_fmt = &fmt_cfg->fmt_ext;
119bc2bd45bSSriram Periyasamy 
120bc2bd45bSSriram Periyasamy 		channels = wav_fmt->fmt.channels;
121bc2bd45bSSriram Periyasamy 		bps = wav_fmt->fmt.bits_per_sample;
122bc2bd45bSSriram Periyasamy 		fs = wav_fmt->fmt.samples_per_sec;
123bc2bd45bSSriram Periyasamy 
124bc2bd45bSSriram Periyasamy 		/*
125bc2bd45bSSriram Periyasamy 		 * In case of TDM configuration on a ssp, there can
126bc2bd45bSSriram Periyasamy 		 * be more than one blob in which channel masks are
127bc2bd45bSSriram Periyasamy 		 * different for each usecase for a specific rate and bps.
128bc2bd45bSSriram Periyasamy 		 * But the sclk rate will be generated for the total
129bc2bd45bSSriram Periyasamy 		 * number of channels used for that endpoint.
130bc2bd45bSSriram Periyasamy 		 *
131bc2bd45bSSriram Periyasamy 		 * So for the given fs and bps, choose blob which has
132bc2bd45bSSriram Periyasamy 		 * the superset of all channels for that endpoint and
133bc2bd45bSSriram Periyasamy 		 * derive the rate.
134bc2bd45bSSriram Periyasamy 		 */
135bc2bd45bSSriram Periyasamy 		for (j = i; j < fmt->fmt_count; j++) {
136bc2bd45bSSriram Periyasamy 			fmt_cfg = &fmt->fmt_config[j];
137bc2bd45bSSriram Periyasamy 			wav_fmt = &fmt_cfg->fmt_ext;
138bc2bd45bSSriram Periyasamy 			if ((fs == wav_fmt->fmt.samples_per_sec) &&
139bc2bd45bSSriram Periyasamy 			   (bps == wav_fmt->fmt.bits_per_sample))
140bc2bd45bSSriram Periyasamy 				channels = max_t(u16, channels,
141bc2bd45bSSriram Periyasamy 						wav_fmt->fmt.channels);
142bc2bd45bSSriram Periyasamy 		}
143bc2bd45bSSriram Periyasamy 
144bc2bd45bSSriram Periyasamy 		rate = channels * bps * fs;
145bc2bd45bSSriram Periyasamy 
146bc2bd45bSSriram Periyasamy 		/* check if the rate is added already to the given SSP's sclk */
14787684d33SDan Carpenter 		for (j = 0; (j < SKL_MAX_CLK_RATES) &&
14887684d33SDan Carpenter 			    (sclk[id].rate_cfg[j].rate != 0); j++) {
149bc2bd45bSSriram Periyasamy 			if (sclk[id].rate_cfg[j].rate == rate) {
150bc2bd45bSSriram Periyasamy 				present = true;
151bc2bd45bSSriram Periyasamy 				break;
152bc2bd45bSSriram Periyasamy 			}
153bc2bd45bSSriram Periyasamy 		}
154bc2bd45bSSriram Periyasamy 
155bc2bd45bSSriram Periyasamy 		/* Fill rate and parent for sclk/sclkfs */
156bc2bd45bSSriram Periyasamy 		if (!present) {
1579afbc5ecSSriram Periyasamy 			i2s_config_ext = (struct skl_i2s_config_blob_ext *)
158bc2bd45bSSriram Periyasamy 						fmt->fmt_config[0].config.caps;
1599afbc5ecSSriram Periyasamy 
1609afbc5ecSSriram Periyasamy 			/* MCLK Divider Source Select */
1619afbc5ecSSriram Periyasamy 			if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
1629afbc5ecSSriram Periyasamy 				i2s_config = ext_to_legacy_blob(i2s_config_ext);
1639afbc5ecSSriram Periyasamy 				clk_src = get_clk_src(i2s_config->mclk,
1649afbc5ecSSriram Periyasamy 						SKL_MNDSS_DIV_CLK_SRC_MASK);
1659afbc5ecSSriram Periyasamy 			} else {
1669afbc5ecSSriram Periyasamy 				clk_src = get_clk_src(i2s_config_ext->mclk,
1679afbc5ecSSriram Periyasamy 						SKL_MNDSS_DIV_CLK_SRC_MASK);
1689afbc5ecSSriram Periyasamy 			}
169bc2bd45bSSriram Periyasamy 
170bc2bd45bSSriram Periyasamy 			parent = skl_get_parent_clk(clk_src);
171bc2bd45bSSriram Periyasamy 
172bc2bd45bSSriram Periyasamy 			/*
173bc2bd45bSSriram Periyasamy 			 * Do not copy the config data if there is no parent
174bc2bd45bSSriram Periyasamy 			 * clock available for this clock source select
175bc2bd45bSSriram Periyasamy 			 */
176bc2bd45bSSriram Periyasamy 			if (!parent)
177bc2bd45bSSriram Periyasamy 				continue;
178bc2bd45bSSriram Periyasamy 
179bc2bd45bSSriram Periyasamy 			sclk[id].rate_cfg[rate_index].rate = rate;
180bc2bd45bSSriram Periyasamy 			sclk[id].rate_cfg[rate_index].config = fmt_cfg;
181bc2bd45bSSriram Periyasamy 			sclkfs[id].rate_cfg[rate_index].rate = rate;
182bc2bd45bSSriram Periyasamy 			sclkfs[id].rate_cfg[rate_index].config = fmt_cfg;
183bc2bd45bSSriram Periyasamy 			sclk[id].parent_name = parent->name;
184bc2bd45bSSriram Periyasamy 			sclkfs[id].parent_name = parent->name;
185bc2bd45bSSriram Periyasamy 
186bc2bd45bSSriram Periyasamy 			rate_index++;
187bc2bd45bSSriram Periyasamy 		}
188bc2bd45bSSriram Periyasamy 	}
189bc2bd45bSSriram Periyasamy }
190bc2bd45bSSriram Periyasamy 
191bcc2a2dcSCezary Rojewski static void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk,
192bc2bd45bSSriram Periyasamy 				struct nhlt_fmt *fmt, u8 id)
193bc2bd45bSSriram Periyasamy {
1949afbc5ecSSriram Periyasamy 	struct skl_i2s_config_blob_ext *i2s_config_ext;
195bc2bd45bSSriram Periyasamy 	struct skl_i2s_config_blob_legacy *i2s_config;
196bc2bd45bSSriram Periyasamy 	struct nhlt_specific_cfg *fmt_cfg;
197bc2bd45bSSriram Periyasamy 	struct skl_clk_parent_src *parent;
198bc2bd45bSSriram Periyasamy 	u32 clkdiv, div_ratio;
199bc2bd45bSSriram Periyasamy 	u8 clk_src;
200bc2bd45bSSriram Periyasamy 
201bc2bd45bSSriram Periyasamy 	fmt_cfg = &fmt->fmt_config[0].config;
2029afbc5ecSSriram Periyasamy 	i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps;
203bc2bd45bSSriram Periyasamy 
2049afbc5ecSSriram Periyasamy 	/* MCLK Divider Source Select and divider */
2059afbc5ecSSriram Periyasamy 	if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
2069afbc5ecSSriram Periyasamy 		i2s_config = ext_to_legacy_blob(i2s_config_ext);
2079afbc5ecSSriram Periyasamy 		clk_src = get_clk_src(i2s_config->mclk,
2089afbc5ecSSriram Periyasamy 				SKL_MCLK_DIV_CLK_SRC_MASK);
2099afbc5ecSSriram Periyasamy 		clkdiv = i2s_config->mclk.mdivr &
2109afbc5ecSSriram Periyasamy 				SKL_MCLK_DIV_RATIO_MASK;
2119afbc5ecSSriram Periyasamy 	} else {
2129afbc5ecSSriram Periyasamy 		clk_src = get_clk_src(i2s_config_ext->mclk,
2139afbc5ecSSriram Periyasamy 				SKL_MCLK_DIV_CLK_SRC_MASK);
2149afbc5ecSSriram Periyasamy 		clkdiv = i2s_config_ext->mclk.mdivr[0] &
2159afbc5ecSSriram Periyasamy 				SKL_MCLK_DIV_RATIO_MASK;
2169afbc5ecSSriram Periyasamy 	}
217bc2bd45bSSriram Periyasamy 
218bc2bd45bSSriram Periyasamy 	/* bypass divider */
219bc2bd45bSSriram Periyasamy 	div_ratio = 1;
220bc2bd45bSSriram Periyasamy 
221bc2bd45bSSriram Periyasamy 	if (clkdiv != SKL_MCLK_DIV_RATIO_MASK)
222bc2bd45bSSriram Periyasamy 		/* Divider is 2 + clkdiv */
223bc2bd45bSSriram Periyasamy 		div_ratio = clkdiv + 2;
224bc2bd45bSSriram Periyasamy 
225bc2bd45bSSriram Periyasamy 	/* Calculate MCLK rate from source using div value */
226bc2bd45bSSriram Periyasamy 	parent = skl_get_parent_clk(clk_src);
227bc2bd45bSSriram Periyasamy 	if (!parent)
228bc2bd45bSSriram Periyasamy 		return;
229bc2bd45bSSriram Periyasamy 
230bc2bd45bSSriram Periyasamy 	mclk[id].rate_cfg[0].rate = parent->rate/div_ratio;
231bc2bd45bSSriram Periyasamy 	mclk[id].rate_cfg[0].config = &fmt->fmt_config[0];
232bc2bd45bSSriram Periyasamy 	mclk[id].parent_name = parent->name;
233bc2bd45bSSriram Periyasamy }
234bc2bd45bSSriram Periyasamy 
235bcc2a2dcSCezary Rojewski void skl_get_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks)
236bc2bd45bSSriram Periyasamy {
237bc2bd45bSSriram Periyasamy 	struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
238bc2bd45bSSriram Periyasamy 	struct nhlt_endpoint *epnt;
239bc2bd45bSSriram Periyasamy 	struct nhlt_fmt *fmt;
240bc2bd45bSSriram Periyasamy 	int i;
241bc2bd45bSSriram Periyasamy 	u8 id;
242bc2bd45bSSriram Periyasamy 
243bc2bd45bSSriram Periyasamy 	epnt = (struct nhlt_endpoint *)nhlt->desc;
244bc2bd45bSSriram Periyasamy 	for (i = 0; i < nhlt->endpoint_count; i++) {
245bc2bd45bSSriram Periyasamy 		if (epnt->linktype == NHLT_LINK_SSP) {
246bc2bd45bSSriram Periyasamy 			id = epnt->virtual_bus_id;
247bc2bd45bSSriram Periyasamy 
248bc2bd45bSSriram Periyasamy 			fmt = (struct nhlt_fmt *)(epnt->config.caps
249bc2bd45bSSriram Periyasamy 					+ epnt->config.size);
250bc2bd45bSSriram Periyasamy 
251bc2bd45bSSriram Periyasamy 			skl_get_ssp_clks(skl, ssp_clks, fmt, id);
252bc2bd45bSSriram Periyasamy 			skl_get_mclk(skl, ssp_clks, fmt, id);
253bc2bd45bSSriram Periyasamy 		}
254bc2bd45bSSriram Periyasamy 		epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
255bc2bd45bSSriram Periyasamy 	}
256bc2bd45bSSriram Periyasamy }
257