162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  skl-nhlt.c - Intel SKL Platform NHLT parsing
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2015 Intel Corp
662306a36Sopenharmony_ci *  Author: Sanjiv Kumar <sanjiv.kumar@intel.com>
762306a36Sopenharmony_ci *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <sound/intel-nhlt.h>
1362306a36Sopenharmony_ci#include "skl.h"
1462306a36Sopenharmony_ci#include "skl-i2s.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic void skl_nhlt_trim_space(char *trim)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	char *s = trim;
1962306a36Sopenharmony_ci	int cnt;
2062306a36Sopenharmony_ci	int i;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	cnt = 0;
2362306a36Sopenharmony_ci	for (i = 0; s[i]; i++) {
2462306a36Sopenharmony_ci		if (!isspace(s[i]))
2562306a36Sopenharmony_ci			s[cnt++] = s[i];
2662306a36Sopenharmony_ci	}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	s[cnt] = '\0';
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint skl_nhlt_update_topology_bin(struct skl_dev *skl)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
3462306a36Sopenharmony_ci	struct hdac_bus *bus = skl_to_bus(skl);
3562306a36Sopenharmony_ci	struct device *dev = bus->dev;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	dev_dbg(dev, "oem_id %.6s, oem_table_id %.8s oem_revision %d\n",
3862306a36Sopenharmony_ci		nhlt->header.oem_id, nhlt->header.oem_table_id,
3962306a36Sopenharmony_ci		nhlt->header.oem_revision);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s",
4262306a36Sopenharmony_ci		skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,
4362306a36Sopenharmony_ci		nhlt->header.oem_revision, "-tplg.bin");
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	skl_nhlt_trim_space(skl->tplg_name);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic ssize_t platform_id_show(struct device *dev,
5162306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(dev);
5462306a36Sopenharmony_ci	struct hdac_bus *bus = pci_get_drvdata(pci);
5562306a36Sopenharmony_ci	struct skl_dev *skl = bus_to_skl(bus);
5662306a36Sopenharmony_ci	struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
5762306a36Sopenharmony_ci	char platform_id[32];
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id,
6062306a36Sopenharmony_ci			nhlt->header.oem_id, nhlt->header.oem_table_id,
6162306a36Sopenharmony_ci			nhlt->header.oem_revision);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	skl_nhlt_trim_space(platform_id);
6462306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", platform_id);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(platform_id);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciint skl_nhlt_create_sysfs(struct skl_dev *skl)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct device *dev = &skl->pci->dev;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr))
7462306a36Sopenharmony_ci		dev_warn(dev, "Error creating sysfs entry\n");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid skl_nhlt_remove_sysfs(struct skl_dev *skl)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct device *dev = &skl->pci->dev;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (skl->nhlt)
8462306a36Sopenharmony_ci		sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * Queries NHLT for all the fmt configuration for a particular endpoint and
8962306a36Sopenharmony_ci * stores all possible rates supported in a rate table for the corresponding
9062306a36Sopenharmony_ci * sclk/sclkfs.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
9362306a36Sopenharmony_ci				struct nhlt_fmt *fmt, u8 id)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct skl_i2s_config_blob_ext *i2s_config_ext;
9662306a36Sopenharmony_ci	struct skl_i2s_config_blob_legacy *i2s_config;
9762306a36Sopenharmony_ci	struct skl_clk_parent_src *parent;
9862306a36Sopenharmony_ci	struct skl_ssp_clk *sclk, *sclkfs;
9962306a36Sopenharmony_ci	struct nhlt_fmt_cfg *fmt_cfg;
10062306a36Sopenharmony_ci	struct wav_fmt_ext *wav_fmt;
10162306a36Sopenharmony_ci	unsigned long rate;
10262306a36Sopenharmony_ci	int rate_index = 0;
10362306a36Sopenharmony_ci	u16 channels, bps;
10462306a36Sopenharmony_ci	u8 clk_src;
10562306a36Sopenharmony_ci	int i, j;
10662306a36Sopenharmony_ci	u32 fs;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	sclk = &ssp_clks[SKL_SCLK_OFS];
10962306a36Sopenharmony_ci	sclkfs = &ssp_clks[SKL_SCLKFS_OFS];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (fmt->fmt_count == 0)
11262306a36Sopenharmony_ci		return;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
11562306a36Sopenharmony_ci	for (i = 0; i < fmt->fmt_count; i++) {
11662306a36Sopenharmony_ci		struct nhlt_fmt_cfg *saved_fmt_cfg = fmt_cfg;
11762306a36Sopenharmony_ci		bool present = false;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		wav_fmt = &saved_fmt_cfg->fmt_ext;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		channels = wav_fmt->fmt.channels;
12262306a36Sopenharmony_ci		bps = wav_fmt->fmt.bits_per_sample;
12362306a36Sopenharmony_ci		fs = wav_fmt->fmt.samples_per_sec;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		/*
12662306a36Sopenharmony_ci		 * In case of TDM configuration on a ssp, there can
12762306a36Sopenharmony_ci		 * be more than one blob in which channel masks are
12862306a36Sopenharmony_ci		 * different for each usecase for a specific rate and bps.
12962306a36Sopenharmony_ci		 * But the sclk rate will be generated for the total
13062306a36Sopenharmony_ci		 * number of channels used for that endpoint.
13162306a36Sopenharmony_ci		 *
13262306a36Sopenharmony_ci		 * So for the given fs and bps, choose blob which has
13362306a36Sopenharmony_ci		 * the superset of all channels for that endpoint and
13462306a36Sopenharmony_ci		 * derive the rate.
13562306a36Sopenharmony_ci		 */
13662306a36Sopenharmony_ci		for (j = i; j < fmt->fmt_count; j++) {
13762306a36Sopenharmony_ci			struct nhlt_fmt_cfg *tmp_fmt_cfg = fmt_cfg;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci			wav_fmt = &tmp_fmt_cfg->fmt_ext;
14062306a36Sopenharmony_ci			if ((fs == wav_fmt->fmt.samples_per_sec) &&
14162306a36Sopenharmony_ci			   (bps == wav_fmt->fmt.bits_per_sample)) {
14262306a36Sopenharmony_ci				channels = max_t(u16, channels,
14362306a36Sopenharmony_ci						wav_fmt->fmt.channels);
14462306a36Sopenharmony_ci				saved_fmt_cfg = tmp_fmt_cfg;
14562306a36Sopenharmony_ci			}
14662306a36Sopenharmony_ci			/* Move to the next nhlt_fmt_cfg */
14762306a36Sopenharmony_ci			tmp_fmt_cfg = (struct nhlt_fmt_cfg *)(tmp_fmt_cfg->config.caps +
14862306a36Sopenharmony_ci							      tmp_fmt_cfg->config.size);
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		rate = channels * bps * fs;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		/* check if the rate is added already to the given SSP's sclk */
15462306a36Sopenharmony_ci		for (j = 0; (j < SKL_MAX_CLK_RATES) &&
15562306a36Sopenharmony_ci			    (sclk[id].rate_cfg[j].rate != 0); j++) {
15662306a36Sopenharmony_ci			if (sclk[id].rate_cfg[j].rate == rate) {
15762306a36Sopenharmony_ci				present = true;
15862306a36Sopenharmony_ci				break;
15962306a36Sopenharmony_ci			}
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		/* Fill rate and parent for sclk/sclkfs */
16362306a36Sopenharmony_ci		if (!present) {
16462306a36Sopenharmony_ci			struct nhlt_fmt_cfg *first_fmt_cfg;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci			first_fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
16762306a36Sopenharmony_ci			i2s_config_ext = (struct skl_i2s_config_blob_ext *)
16862306a36Sopenharmony_ci						first_fmt_cfg->config.caps;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci			/* MCLK Divider Source Select */
17162306a36Sopenharmony_ci			if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
17262306a36Sopenharmony_ci				i2s_config = ext_to_legacy_blob(i2s_config_ext);
17362306a36Sopenharmony_ci				clk_src = get_clk_src(i2s_config->mclk,
17462306a36Sopenharmony_ci						SKL_MNDSS_DIV_CLK_SRC_MASK);
17562306a36Sopenharmony_ci			} else {
17662306a36Sopenharmony_ci				clk_src = get_clk_src(i2s_config_ext->mclk,
17762306a36Sopenharmony_ci						SKL_MNDSS_DIV_CLK_SRC_MASK);
17862306a36Sopenharmony_ci			}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci			parent = skl_get_parent_clk(clk_src);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci			/* Move to the next nhlt_fmt_cfg */
18362306a36Sopenharmony_ci			fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
18462306a36Sopenharmony_ci							  fmt_cfg->config.size);
18562306a36Sopenharmony_ci			/*
18662306a36Sopenharmony_ci			 * Do not copy the config data if there is no parent
18762306a36Sopenharmony_ci			 * clock available for this clock source select
18862306a36Sopenharmony_ci			 */
18962306a36Sopenharmony_ci			if (!parent)
19062306a36Sopenharmony_ci				continue;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci			sclk[id].rate_cfg[rate_index].rate = rate;
19362306a36Sopenharmony_ci			sclk[id].rate_cfg[rate_index].config = saved_fmt_cfg;
19462306a36Sopenharmony_ci			sclkfs[id].rate_cfg[rate_index].rate = rate;
19562306a36Sopenharmony_ci			sclkfs[id].rate_cfg[rate_index].config = saved_fmt_cfg;
19662306a36Sopenharmony_ci			sclk[id].parent_name = parent->name;
19762306a36Sopenharmony_ci			sclkfs[id].parent_name = parent->name;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci			rate_index++;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk,
20562306a36Sopenharmony_ci				struct nhlt_fmt *fmt, u8 id)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct skl_i2s_config_blob_ext *i2s_config_ext;
20862306a36Sopenharmony_ci	struct skl_i2s_config_blob_legacy *i2s_config;
20962306a36Sopenharmony_ci	struct nhlt_fmt_cfg *fmt_cfg;
21062306a36Sopenharmony_ci	struct skl_clk_parent_src *parent;
21162306a36Sopenharmony_ci	u32 clkdiv, div_ratio;
21262306a36Sopenharmony_ci	u8 clk_src;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
21562306a36Sopenharmony_ci	i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->config.caps;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* MCLK Divider Source Select and divider */
21862306a36Sopenharmony_ci	if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
21962306a36Sopenharmony_ci		i2s_config = ext_to_legacy_blob(i2s_config_ext);
22062306a36Sopenharmony_ci		clk_src = get_clk_src(i2s_config->mclk,
22162306a36Sopenharmony_ci				SKL_MCLK_DIV_CLK_SRC_MASK);
22262306a36Sopenharmony_ci		clkdiv = i2s_config->mclk.mdivr &
22362306a36Sopenharmony_ci				SKL_MCLK_DIV_RATIO_MASK;
22462306a36Sopenharmony_ci	} else {
22562306a36Sopenharmony_ci		clk_src = get_clk_src(i2s_config_ext->mclk,
22662306a36Sopenharmony_ci				SKL_MCLK_DIV_CLK_SRC_MASK);
22762306a36Sopenharmony_ci		clkdiv = i2s_config_ext->mclk.mdivr[0] &
22862306a36Sopenharmony_ci				SKL_MCLK_DIV_RATIO_MASK;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* bypass divider */
23262306a36Sopenharmony_ci	div_ratio = 1;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (clkdiv != SKL_MCLK_DIV_RATIO_MASK)
23562306a36Sopenharmony_ci		/* Divider is 2 + clkdiv */
23662306a36Sopenharmony_ci		div_ratio = clkdiv + 2;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Calculate MCLK rate from source using div value */
23962306a36Sopenharmony_ci	parent = skl_get_parent_clk(clk_src);
24062306a36Sopenharmony_ci	if (!parent)
24162306a36Sopenharmony_ci		return;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	mclk[id].rate_cfg[0].rate = parent->rate/div_ratio;
24462306a36Sopenharmony_ci	mclk[id].rate_cfg[0].config = fmt_cfg;
24562306a36Sopenharmony_ci	mclk[id].parent_name = parent->name;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_civoid skl_get_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
25162306a36Sopenharmony_ci	struct nhlt_endpoint *epnt;
25262306a36Sopenharmony_ci	struct nhlt_fmt *fmt;
25362306a36Sopenharmony_ci	int i;
25462306a36Sopenharmony_ci	u8 id;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	epnt = (struct nhlt_endpoint *)nhlt->desc;
25762306a36Sopenharmony_ci	for (i = 0; i < nhlt->endpoint_count; i++) {
25862306a36Sopenharmony_ci		if (epnt->linktype == NHLT_LINK_SSP) {
25962306a36Sopenharmony_ci			id = epnt->virtual_bus_id;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci			fmt = (struct nhlt_fmt *)(epnt->config.caps
26262306a36Sopenharmony_ci					+ epnt->config.size);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci			skl_get_ssp_clks(skl, ssp_clks, fmt, id);
26562306a36Sopenharmony_ci			skl_get_mclk(skl, ssp_clks, fmt, id);
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci		epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci}
270