xref: /openbmc/linux/sound/soc/intel/skylake/skl-nhlt.c (revision 11af2b1e)
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