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