1 /* 2 * skl-nhlt.c - Intel SKL Platform NHLT parsing 3 * 4 * Copyright (C) 2015 Intel Corp 5 * Author: Sanjiv Kumar <sanjiv.kumar@intel.com> 6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; version 2 of the License. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 * 19 */ 20 #include <linux/pci.h> 21 #include "skl.h" 22 23 /* Unique identification for getting NHLT blobs */ 24 static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, 25 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53}; 26 27 #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" 28 29 struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) 30 { 31 acpi_handle handle; 32 union acpi_object *obj; 33 struct nhlt_resource_desc *nhlt_ptr = NULL; 34 struct nhlt_acpi_table *nhlt_table = NULL; 35 36 if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) { 37 dev_err(dev, "Requested NHLT device not found\n"); 38 return NULL; 39 } 40 41 obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL); 42 if (obj && obj->type == ACPI_TYPE_BUFFER) { 43 nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; 44 nhlt_table = (struct nhlt_acpi_table *) 45 memremap(nhlt_ptr->min_addr, nhlt_ptr->length, 46 MEMREMAP_WB); 47 ACPI_FREE(obj); 48 return nhlt_table; 49 } 50 51 dev_err(dev, "device specific method to extract NHLT blob failed\n"); 52 return NULL; 53 } 54 55 void skl_nhlt_free(struct nhlt_acpi_table *nhlt) 56 { 57 memunmap((void *) nhlt); 58 } 59 60 static struct nhlt_specific_cfg *skl_get_specific_cfg( 61 struct device *dev, struct nhlt_fmt *fmt, 62 u8 no_ch, u32 rate, u16 bps, u8 linktype) 63 { 64 struct nhlt_specific_cfg *sp_config; 65 struct wav_fmt *wfmt; 66 struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; 67 int i; 68 69 dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); 70 71 for (i = 0; i < fmt->fmt_count; i++) { 72 wfmt = &fmt_config->fmt_ext.fmt; 73 dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, 74 wfmt->bits_per_sample, wfmt->samples_per_sec); 75 if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { 76 /* 77 * if link type is dmic ignore rate check as the blob is 78 * generic for all rates 79 */ 80 sp_config = &fmt_config->config; 81 if (linktype == NHLT_LINK_DMIC) 82 return sp_config; 83 84 if (wfmt->samples_per_sec == rate) 85 return sp_config; 86 } 87 88 fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + 89 fmt_config->config.size); 90 } 91 92 return NULL; 93 } 94 95 static void dump_config(struct device *dev, u32 instance_id, u8 linktype, 96 u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) 97 { 98 dev_dbg(dev, "Input configuration\n"); 99 dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); 100 dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); 101 dev_dbg(dev, "bits_per_sample=%d\n", bps); 102 } 103 104 static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, 105 u32 instance_id, u8 link_type, u8 dirn, u8 dev_type) 106 { 107 dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n", 108 epnt->virtual_bus_id, epnt->linktype, 109 epnt->direction, epnt->device_type); 110 111 if ((epnt->virtual_bus_id == instance_id) && 112 (epnt->linktype == link_type) && 113 (epnt->direction == dirn) && 114 (epnt->device_type == dev_type)) 115 return true; 116 else 117 return false; 118 } 119 120 struct nhlt_specific_cfg 121 *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, 122 u8 s_fmt, u8 num_ch, u32 s_rate, 123 u8 dirn, u8 dev_type) 124 { 125 struct nhlt_fmt *fmt; 126 struct nhlt_endpoint *epnt; 127 struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); 128 struct device *dev = bus->dev; 129 struct nhlt_specific_cfg *sp_config; 130 struct nhlt_acpi_table *nhlt = skl->nhlt; 131 u16 bps = (s_fmt == 16) ? 16 : 32; 132 u8 j; 133 134 dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); 135 136 epnt = (struct nhlt_endpoint *)nhlt->desc; 137 138 dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); 139 140 for (j = 0; j < nhlt->endpoint_count; j++) { 141 if (skl_check_ep_match(dev, epnt, instance, link_type, 142 dirn, dev_type)) { 143 fmt = (struct nhlt_fmt *)(epnt->config.caps + 144 epnt->config.size); 145 sp_config = skl_get_specific_cfg(dev, fmt, num_ch, 146 s_rate, bps, link_type); 147 if (sp_config) 148 return sp_config; 149 } 150 151 epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 152 } 153 154 return NULL; 155 } 156 157 int skl_get_dmic_geo(struct skl *skl) 158 { 159 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 160 struct nhlt_endpoint *epnt; 161 struct nhlt_dmic_array_config *cfg; 162 struct device *dev = &skl->pci->dev; 163 unsigned int dmic_geo = 0; 164 u8 j; 165 166 epnt = (struct nhlt_endpoint *)nhlt->desc; 167 168 for (j = 0; j < nhlt->endpoint_count; j++) { 169 if (epnt->linktype == NHLT_LINK_DMIC) { 170 cfg = (struct nhlt_dmic_array_config *) 171 (epnt->config.caps); 172 switch (cfg->array_type) { 173 case NHLT_MIC_ARRAY_2CH_SMALL: 174 case NHLT_MIC_ARRAY_2CH_BIG: 175 dmic_geo |= MIC_ARRAY_2CH; 176 break; 177 178 case NHLT_MIC_ARRAY_4CH_1ST_GEOM: 179 case NHLT_MIC_ARRAY_4CH_L_SHAPED: 180 case NHLT_MIC_ARRAY_4CH_2ND_GEOM: 181 dmic_geo |= MIC_ARRAY_4CH; 182 break; 183 184 default: 185 dev_warn(dev, "undefined DMIC array_type 0x%0x\n", 186 cfg->array_type); 187 188 } 189 } 190 epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 191 } 192 193 return dmic_geo; 194 } 195 196 static void skl_nhlt_trim_space(char *trim) 197 { 198 char *s = trim; 199 int cnt; 200 int i; 201 202 cnt = 0; 203 for (i = 0; s[i]; i++) { 204 if (!isspace(s[i])) 205 s[cnt++] = s[i]; 206 } 207 208 s[cnt] = '\0'; 209 } 210 211 int skl_nhlt_update_topology_bin(struct skl *skl) 212 { 213 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 214 struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); 215 struct device *dev = bus->dev; 216 217 dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", 218 nhlt->header.oem_id, nhlt->header.oem_table_id, 219 nhlt->header.oem_revision); 220 221 snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", 222 skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, 223 nhlt->header.oem_revision, "-tplg.bin"); 224 225 skl_nhlt_trim_space(skl->tplg_name); 226 227 return 0; 228 } 229 230 static ssize_t skl_nhlt_platform_id_show(struct device *dev, 231 struct device_attribute *attr, char *buf) 232 { 233 struct pci_dev *pci = to_pci_dev(dev); 234 struct hdac_ext_bus *ebus = pci_get_drvdata(pci); 235 struct skl *skl = ebus_to_skl(ebus); 236 struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 237 char platform_id[32]; 238 239 sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id, 240 nhlt->header.oem_id, nhlt->header.oem_table_id, 241 nhlt->header.oem_revision); 242 243 skl_nhlt_trim_space(platform_id); 244 return sprintf(buf, "%s\n", platform_id); 245 } 246 247 static DEVICE_ATTR(platform_id, 0444, skl_nhlt_platform_id_show, NULL); 248 249 int skl_nhlt_create_sysfs(struct skl *skl) 250 { 251 struct device *dev = &skl->pci->dev; 252 253 if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr)) 254 dev_warn(dev, "Error creating sysfs entry\n"); 255 256 return 0; 257 } 258 259 void skl_nhlt_remove_sysfs(struct skl *skl) 260 { 261 struct device *dev = &skl->pci->dev; 262 263 sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); 264 } 265