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