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 int rate_index = 0; 103 u16 channels, bps; 104 u8 clk_src; 105 int i, j; 106 u32 fs; 107 108 sclk = &ssp_clks[SKL_SCLK_OFS]; 109 sclkfs = &ssp_clks[SKL_SCLKFS_OFS]; 110 111 if (fmt->fmt_count == 0) 112 return; 113 114 fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config; 115 for (i = 0; i < fmt->fmt_count; i++) { 116 struct nhlt_fmt_cfg *saved_fmt_cfg = fmt_cfg; 117 bool present = false; 118 119 wav_fmt = &saved_fmt_cfg->fmt_ext; 120 121 channels = wav_fmt->fmt.channels; 122 bps = wav_fmt->fmt.bits_per_sample; 123 fs = wav_fmt->fmt.samples_per_sec; 124 125 /* 126 * In case of TDM configuration on a ssp, there can 127 * be more than one blob in which channel masks are 128 * different for each usecase for a specific rate and bps. 129 * But the sclk rate will be generated for the total 130 * number of channels used for that endpoint. 131 * 132 * So for the given fs and bps, choose blob which has 133 * the superset of all channels for that endpoint and 134 * derive the rate. 135 */ 136 for (j = i; j < fmt->fmt_count; j++) { 137 struct nhlt_fmt_cfg *tmp_fmt_cfg = fmt_cfg; 138 139 wav_fmt = &tmp_fmt_cfg->fmt_ext; 140 if ((fs == wav_fmt->fmt.samples_per_sec) && 141 (bps == wav_fmt->fmt.bits_per_sample)) { 142 channels = max_t(u16, channels, 143 wav_fmt->fmt.channels); 144 saved_fmt_cfg = tmp_fmt_cfg; 145 } 146 /* Move to the next nhlt_fmt_cfg */ 147 tmp_fmt_cfg = (struct nhlt_fmt_cfg *)(tmp_fmt_cfg->config.caps + 148 tmp_fmt_cfg->config.size); 149 } 150 151 rate = channels * bps * fs; 152 153 /* check if the rate is added already to the given SSP's sclk */ 154 for (j = 0; (j < SKL_MAX_CLK_RATES) && 155 (sclk[id].rate_cfg[j].rate != 0); j++) { 156 if (sclk[id].rate_cfg[j].rate == rate) { 157 present = true; 158 break; 159 } 160 } 161 162 /* Fill rate and parent for sclk/sclkfs */ 163 if (!present) { 164 struct nhlt_fmt_cfg *first_fmt_cfg; 165 166 first_fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config; 167 i2s_config_ext = (struct skl_i2s_config_blob_ext *) 168 first_fmt_cfg->config.caps; 169 170 /* MCLK Divider Source Select */ 171 if (is_legacy_blob(i2s_config_ext->hdr.sig)) { 172 i2s_config = ext_to_legacy_blob(i2s_config_ext); 173 clk_src = get_clk_src(i2s_config->mclk, 174 SKL_MNDSS_DIV_CLK_SRC_MASK); 175 } else { 176 clk_src = get_clk_src(i2s_config_ext->mclk, 177 SKL_MNDSS_DIV_CLK_SRC_MASK); 178 } 179 180 parent = skl_get_parent_clk(clk_src); 181 182 /* Move to the next nhlt_fmt_cfg */ 183 fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps + 184 fmt_cfg->config.size); 185 /* 186 * Do not copy the config data if there is no parent 187 * clock available for this clock source select 188 */ 189 if (!parent) 190 continue; 191 192 sclk[id].rate_cfg[rate_index].rate = rate; 193 sclk[id].rate_cfg[rate_index].config = saved_fmt_cfg; 194 sclkfs[id].rate_cfg[rate_index].rate = rate; 195 sclkfs[id].rate_cfg[rate_index].config = saved_fmt_cfg; 196 sclk[id].parent_name = parent->name; 197 sclkfs[id].parent_name = parent->name; 198 199 rate_index++; 200 } 201 } 202 } 203 204 static void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk, 205 struct nhlt_fmt *fmt, u8 id) 206 { 207 struct skl_i2s_config_blob_ext *i2s_config_ext; 208 struct skl_i2s_config_blob_legacy *i2s_config; 209 struct nhlt_fmt_cfg *fmt_cfg; 210 struct skl_clk_parent_src *parent; 211 u32 clkdiv, div_ratio; 212 u8 clk_src; 213 214 fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config; 215 i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->config.caps; 216 217 /* MCLK Divider Source Select and divider */ 218 if (is_legacy_blob(i2s_config_ext->hdr.sig)) { 219 i2s_config = ext_to_legacy_blob(i2s_config_ext); 220 clk_src = get_clk_src(i2s_config->mclk, 221 SKL_MCLK_DIV_CLK_SRC_MASK); 222 clkdiv = i2s_config->mclk.mdivr & 223 SKL_MCLK_DIV_RATIO_MASK; 224 } else { 225 clk_src = get_clk_src(i2s_config_ext->mclk, 226 SKL_MCLK_DIV_CLK_SRC_MASK); 227 clkdiv = i2s_config_ext->mclk.mdivr[0] & 228 SKL_MCLK_DIV_RATIO_MASK; 229 } 230 231 /* bypass divider */ 232 div_ratio = 1; 233 234 if (clkdiv != SKL_MCLK_DIV_RATIO_MASK) 235 /* Divider is 2 + clkdiv */ 236 div_ratio = clkdiv + 2; 237 238 /* Calculate MCLK rate from source using div value */ 239 parent = skl_get_parent_clk(clk_src); 240 if (!parent) 241 return; 242 243 mclk[id].rate_cfg[0].rate = parent->rate/div_ratio; 244 mclk[id].rate_cfg[0].config = fmt_cfg; 245 mclk[id].parent_name = parent->name; 246 } 247 248 void skl_get_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks) 249 { 250 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 251 struct nhlt_endpoint *epnt; 252 struct nhlt_fmt *fmt; 253 int i; 254 u8 id; 255 256 epnt = (struct nhlt_endpoint *)nhlt->desc; 257 for (i = 0; i < nhlt->endpoint_count; i++) { 258 if (epnt->linktype == NHLT_LINK_SSP) { 259 id = epnt->virtual_bus_id; 260 261 fmt = (struct nhlt_fmt *)(epnt->config.caps 262 + epnt->config.size); 263 264 skl_get_ssp_clks(skl, ssp_clks, fmt, id); 265 skl_get_mclk(skl, ssp_clks, fmt, id); 266 } 267 epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 268 } 269 } 270