1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * skl-nhlt.c - Intel SKL Platform NHLT parsing 4 * 5 * Copyright (C) 2015 Intel Corp 6 * Author: Sanjiv Kumar <sanjiv.kumar@intel.com> 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 */ 11 #include <linux/pci.h> 12 #include <sound/intel-nhlt.h> 13 #include "skl.h" 14 #include "skl-i2s.h" 15 16 static void skl_nhlt_trim_space(char *trim) 17 { 18 char *s = trim; 19 int cnt; 20 int i; 21 22 cnt = 0; 23 for (i = 0; s[i]; i++) { 24 if (!isspace(s[i])) 25 s[cnt++] = s[i]; 26 } 27 28 s[cnt] = '\0'; 29 } 30 31 int skl_nhlt_update_topology_bin(struct skl_dev *skl) 32 { 33 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 34 struct hdac_bus *bus = skl_to_bus(skl); 35 struct device *dev = bus->dev; 36 37 dev_dbg(dev, "oem_id %.6s, oem_table_id %.8s oem_revision %d\n", 38 nhlt->header.oem_id, nhlt->header.oem_table_id, 39 nhlt->header.oem_revision); 40 41 snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", 42 skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, 43 nhlt->header.oem_revision, "-tplg.bin"); 44 45 skl_nhlt_trim_space(skl->tplg_name); 46 47 return 0; 48 } 49 50 static ssize_t platform_id_show(struct device *dev, 51 struct device_attribute *attr, char *buf) 52 { 53 struct pci_dev *pci = to_pci_dev(dev); 54 struct hdac_bus *bus = pci_get_drvdata(pci); 55 struct skl_dev *skl = bus_to_skl(bus); 56 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 57 char platform_id[32]; 58 59 sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id, 60 nhlt->header.oem_id, nhlt->header.oem_table_id, 61 nhlt->header.oem_revision); 62 63 skl_nhlt_trim_space(platform_id); 64 return sprintf(buf, "%s\n", platform_id); 65 } 66 67 static DEVICE_ATTR_RO(platform_id); 68 69 int skl_nhlt_create_sysfs(struct skl_dev *skl) 70 { 71 struct device *dev = &skl->pci->dev; 72 73 if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr)) 74 dev_warn(dev, "Error creating sysfs entry\n"); 75 76 return 0; 77 } 78 79 void skl_nhlt_remove_sysfs(struct skl_dev *skl) 80 { 81 struct device *dev = &skl->pci->dev; 82 83 if (skl->nhlt) 84 sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); 85 } 86 87 /* 88 * Queries NHLT for all the fmt configuration for a particular endpoint and 89 * stores all possible rates supported in a rate table for the corresponding 90 * sclk/sclkfs. 91 */ 92 static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks, 93 struct nhlt_fmt *fmt, u8 id) 94 { 95 struct skl_i2s_config_blob_ext *i2s_config_ext; 96 struct skl_i2s_config_blob_legacy *i2s_config; 97 struct skl_clk_parent_src *parent; 98 struct skl_ssp_clk *sclk, *sclkfs; 99 struct nhlt_fmt_cfg *fmt_cfg; 100 struct wav_fmt_ext *wav_fmt; 101 unsigned long rate; 102 bool present = false; 103 int rate_index = 0; 104 u16 channels, bps; 105 u8 clk_src; 106 int i, j; 107 u32 fs; 108 109 sclk = &ssp_clks[SKL_SCLK_OFS]; 110 sclkfs = &ssp_clks[SKL_SCLKFS_OFS]; 111 112 if (fmt->fmt_count == 0) 113 return; 114 115 for (i = 0; i < fmt->fmt_count; i++) { 116 fmt_cfg = &fmt->fmt_config[i]; 117 wav_fmt = &fmt_cfg->fmt_ext; 118 119 channels = wav_fmt->fmt.channels; 120 bps = wav_fmt->fmt.bits_per_sample; 121 fs = wav_fmt->fmt.samples_per_sec; 122 123 /* 124 * In case of TDM configuration on a ssp, there can 125 * be more than one blob in which channel masks are 126 * different for each usecase for a specific rate and bps. 127 * But the sclk rate will be generated for the total 128 * number of channels used for that endpoint. 129 * 130 * So for the given fs and bps, choose blob which has 131 * the superset of all channels for that endpoint and 132 * derive the rate. 133 */ 134 for (j = i; j < fmt->fmt_count; j++) { 135 fmt_cfg = &fmt->fmt_config[j]; 136 wav_fmt = &fmt_cfg->fmt_ext; 137 if ((fs == wav_fmt->fmt.samples_per_sec) && 138 (bps == wav_fmt->fmt.bits_per_sample)) 139 channels = max_t(u16, channels, 140 wav_fmt->fmt.channels); 141 } 142 143 rate = channels * bps * fs; 144 145 /* check if the rate is added already to the given SSP's sclk */ 146 for (j = 0; (j < SKL_MAX_CLK_RATES) && 147 (sclk[id].rate_cfg[j].rate != 0); j++) { 148 if (sclk[id].rate_cfg[j].rate == rate) { 149 present = true; 150 break; 151 } 152 } 153 154 /* Fill rate and parent for sclk/sclkfs */ 155 if (!present) { 156 i2s_config_ext = (struct skl_i2s_config_blob_ext *) 157 fmt->fmt_config[0].config.caps; 158 159 /* MCLK Divider Source Select */ 160 if (is_legacy_blob(i2s_config_ext->hdr.sig)) { 161 i2s_config = ext_to_legacy_blob(i2s_config_ext); 162 clk_src = get_clk_src(i2s_config->mclk, 163 SKL_MNDSS_DIV_CLK_SRC_MASK); 164 } else { 165 clk_src = get_clk_src(i2s_config_ext->mclk, 166 SKL_MNDSS_DIV_CLK_SRC_MASK); 167 } 168 169 parent = skl_get_parent_clk(clk_src); 170 171 /* 172 * Do not copy the config data if there is no parent 173 * clock available for this clock source select 174 */ 175 if (!parent) 176 continue; 177 178 sclk[id].rate_cfg[rate_index].rate = rate; 179 sclk[id].rate_cfg[rate_index].config = fmt_cfg; 180 sclkfs[id].rate_cfg[rate_index].rate = rate; 181 sclkfs[id].rate_cfg[rate_index].config = fmt_cfg; 182 sclk[id].parent_name = parent->name; 183 sclkfs[id].parent_name = parent->name; 184 185 rate_index++; 186 } 187 } 188 } 189 190 static void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk, 191 struct nhlt_fmt *fmt, u8 id) 192 { 193 struct skl_i2s_config_blob_ext *i2s_config_ext; 194 struct skl_i2s_config_blob_legacy *i2s_config; 195 struct nhlt_specific_cfg *fmt_cfg; 196 struct skl_clk_parent_src *parent; 197 u32 clkdiv, div_ratio; 198 u8 clk_src; 199 200 fmt_cfg = &fmt->fmt_config[0].config; 201 i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps; 202 203 /* MCLK Divider Source Select and divider */ 204 if (is_legacy_blob(i2s_config_ext->hdr.sig)) { 205 i2s_config = ext_to_legacy_blob(i2s_config_ext); 206 clk_src = get_clk_src(i2s_config->mclk, 207 SKL_MCLK_DIV_CLK_SRC_MASK); 208 clkdiv = i2s_config->mclk.mdivr & 209 SKL_MCLK_DIV_RATIO_MASK; 210 } else { 211 clk_src = get_clk_src(i2s_config_ext->mclk, 212 SKL_MCLK_DIV_CLK_SRC_MASK); 213 clkdiv = i2s_config_ext->mclk.mdivr[0] & 214 SKL_MCLK_DIV_RATIO_MASK; 215 } 216 217 /* bypass divider */ 218 div_ratio = 1; 219 220 if (clkdiv != SKL_MCLK_DIV_RATIO_MASK) 221 /* Divider is 2 + clkdiv */ 222 div_ratio = clkdiv + 2; 223 224 /* Calculate MCLK rate from source using div value */ 225 parent = skl_get_parent_clk(clk_src); 226 if (!parent) 227 return; 228 229 mclk[id].rate_cfg[0].rate = parent->rate/div_ratio; 230 mclk[id].rate_cfg[0].config = &fmt->fmt_config[0]; 231 mclk[id].parent_name = parent->name; 232 } 233 234 void skl_get_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks) 235 { 236 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 237 struct nhlt_endpoint *epnt; 238 struct nhlt_fmt *fmt; 239 int i; 240 u8 id; 241 242 epnt = (struct nhlt_endpoint *)nhlt->desc; 243 for (i = 0; i < nhlt->endpoint_count; i++) { 244 if (epnt->linktype == NHLT_LINK_SSP) { 245 id = epnt->virtual_bus_id; 246 247 fmt = (struct nhlt_fmt *)(epnt->config.caps 248 + epnt->config.size); 249 250 skl_get_ssp_clks(skl, ssp_clks, fmt, id); 251 skl_get_mclk(skl, ssp_clks, fmt, id); 252 } 253 epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 254 } 255 } 256