1473eb87aSJeeja KP /* 2473eb87aSJeeja KP * skl-nhlt.c - Intel SKL Platform NHLT parsing 3473eb87aSJeeja KP * 4473eb87aSJeeja KP * Copyright (C) 2015 Intel Corp 5473eb87aSJeeja KP * Author: Sanjiv Kumar <sanjiv.kumar@intel.com> 6473eb87aSJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7473eb87aSJeeja KP * 8473eb87aSJeeja KP * This program is free software; you can redistribute it and/or modify 9473eb87aSJeeja KP * it under the terms of the GNU General Public License as published by 10473eb87aSJeeja KP * the Free Software Foundation; version 2 of the License. 11473eb87aSJeeja KP * 12473eb87aSJeeja KP * This program is distributed in the hope that it will be useful, but 13473eb87aSJeeja KP * WITHOUT ANY WARRANTY; without even the implied warranty of 14473eb87aSJeeja KP * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15473eb87aSJeeja KP * General Public License for more details. 16473eb87aSJeeja KP * 17473eb87aSJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18473eb87aSJeeja KP * 19473eb87aSJeeja KP */ 20f65cf7d6SYong Zhi #include <linux/pci.h> 21473eb87aSJeeja KP #include "skl.h" 22473eb87aSJeeja KP 23473eb87aSJeeja KP /* Unique identification for getting NHLT blobs */ 24473eb87aSJeeja KP static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45, 25473eb87aSJeeja KP 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53}; 26473eb87aSJeeja KP 27473eb87aSJeeja KP #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS" 28473eb87aSJeeja KP 29c286b3f9SJeeja KP struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) 30473eb87aSJeeja KP { 31473eb87aSJeeja KP acpi_handle handle; 32473eb87aSJeeja KP union acpi_object *obj; 33473eb87aSJeeja KP struct nhlt_resource_desc *nhlt_ptr = NULL; 34c286b3f9SJeeja KP struct nhlt_acpi_table *nhlt_table = NULL; 35473eb87aSJeeja KP 36473eb87aSJeeja KP if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) { 37473eb87aSJeeja KP dev_err(dev, "Requested NHLT device not found\n"); 38473eb87aSJeeja KP return NULL; 39473eb87aSJeeja KP } 40473eb87aSJeeja KP 41473eb87aSJeeja KP obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL); 42473eb87aSJeeja KP if (obj && obj->type == ACPI_TYPE_BUFFER) { 43473eb87aSJeeja KP nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; 44c286b3f9SJeeja KP nhlt_table = (struct nhlt_acpi_table *) 45c286b3f9SJeeja KP memremap(nhlt_ptr->min_addr, nhlt_ptr->length, 46ba40a854SDan Williams MEMREMAP_WB); 47c286b3f9SJeeja KP ACPI_FREE(obj); 48c286b3f9SJeeja KP return nhlt_table; 49473eb87aSJeeja KP } 50473eb87aSJeeja KP 51473eb87aSJeeja KP dev_err(dev, "device specific method to extract NHLT blob failed\n"); 52473eb87aSJeeja KP return NULL; 53473eb87aSJeeja KP } 54473eb87aSJeeja KP 55c286b3f9SJeeja KP void skl_nhlt_free(struct nhlt_acpi_table *nhlt) 56473eb87aSJeeja KP { 57c286b3f9SJeeja KP memunmap((void *) nhlt); 58473eb87aSJeeja KP } 59473eb87aSJeeja KP 60473eb87aSJeeja KP static struct nhlt_specific_cfg *skl_get_specific_cfg( 61473eb87aSJeeja KP struct device *dev, struct nhlt_fmt *fmt, 6216882d24SJeeja KP u8 no_ch, u32 rate, u16 bps, u8 linktype) 63473eb87aSJeeja KP { 64473eb87aSJeeja KP struct nhlt_specific_cfg *sp_config; 65473eb87aSJeeja KP struct wav_fmt *wfmt; 66473eb87aSJeeja KP struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; 67473eb87aSJeeja KP int i; 68473eb87aSJeeja KP 69473eb87aSJeeja KP dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); 70473eb87aSJeeja KP 71473eb87aSJeeja KP for (i = 0; i < fmt->fmt_count; i++) { 72473eb87aSJeeja KP wfmt = &fmt_config->fmt_ext.fmt; 73473eb87aSJeeja KP dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, 74473eb87aSJeeja KP wfmt->bits_per_sample, wfmt->samples_per_sec); 7516882d24SJeeja KP if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { 7616882d24SJeeja KP /* 7716882d24SJeeja KP * if link type is dmic ignore rate check as the blob is 7816882d24SJeeja KP * generic for all rates 7916882d24SJeeja KP */ 80473eb87aSJeeja KP sp_config = &fmt_config->config; 8116882d24SJeeja KP if (linktype == NHLT_LINK_DMIC) 8216882d24SJeeja KP return sp_config; 83473eb87aSJeeja KP 8416882d24SJeeja KP if (wfmt->samples_per_sec == rate) 85473eb87aSJeeja KP return sp_config; 86473eb87aSJeeja KP } 87473eb87aSJeeja KP 88473eb87aSJeeja KP fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + 89473eb87aSJeeja KP fmt_config->config.size); 90473eb87aSJeeja KP } 91473eb87aSJeeja KP 92473eb87aSJeeja KP return NULL; 93473eb87aSJeeja KP } 94473eb87aSJeeja KP 95473eb87aSJeeja KP static void dump_config(struct device *dev, u32 instance_id, u8 linktype, 96473eb87aSJeeja KP u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) 97473eb87aSJeeja KP { 98473eb87aSJeeja KP dev_dbg(dev, "Input configuration\n"); 99473eb87aSJeeja KP dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); 100473eb87aSJeeja KP dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); 101473eb87aSJeeja KP dev_dbg(dev, "bits_per_sample=%d\n", bps); 102473eb87aSJeeja KP } 103473eb87aSJeeja KP 104473eb87aSJeeja KP static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, 105473eb87aSJeeja KP u32 instance_id, u8 link_type, u8 dirn) 106473eb87aSJeeja KP { 107473eb87aSJeeja KP dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d\n", 108473eb87aSJeeja KP epnt->virtual_bus_id, epnt->linktype, epnt->direction); 109473eb87aSJeeja KP 110473eb87aSJeeja KP if ((epnt->virtual_bus_id == instance_id) && 111473eb87aSJeeja KP (epnt->linktype == link_type) && 112473eb87aSJeeja KP (epnt->direction == dirn)) 113473eb87aSJeeja KP return true; 114473eb87aSJeeja KP else 115473eb87aSJeeja KP return false; 116473eb87aSJeeja KP } 117473eb87aSJeeja KP 118473eb87aSJeeja KP struct nhlt_specific_cfg 119473eb87aSJeeja KP *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type, 120473eb87aSJeeja KP u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn) 121473eb87aSJeeja KP { 122473eb87aSJeeja KP struct nhlt_fmt *fmt; 123473eb87aSJeeja KP struct nhlt_endpoint *epnt; 124473eb87aSJeeja KP struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); 125473eb87aSJeeja KP struct device *dev = bus->dev; 126473eb87aSJeeja KP struct nhlt_specific_cfg *sp_config; 127c286b3f9SJeeja KP struct nhlt_acpi_table *nhlt = skl->nhlt; 12883b50246SJeeja KP u16 bps = (s_fmt == 16) ? 16 : 32; 129473eb87aSJeeja KP u8 j; 130473eb87aSJeeja KP 131473eb87aSJeeja KP dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); 132473eb87aSJeeja KP 133473eb87aSJeeja KP epnt = (struct nhlt_endpoint *)nhlt->desc; 134473eb87aSJeeja KP 135473eb87aSJeeja KP dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); 136473eb87aSJeeja KP 137473eb87aSJeeja KP for (j = 0; j < nhlt->endpoint_count; j++) { 138473eb87aSJeeja KP if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) { 139473eb87aSJeeja KP fmt = (struct nhlt_fmt *)(epnt->config.caps + 140473eb87aSJeeja KP epnt->config.size); 14116882d24SJeeja KP sp_config = skl_get_specific_cfg(dev, fmt, num_ch, 14216882d24SJeeja KP s_rate, bps, link_type); 143473eb87aSJeeja KP if (sp_config) 144473eb87aSJeeja KP return sp_config; 145473eb87aSJeeja KP } 146473eb87aSJeeja KP 147473eb87aSJeeja KP epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 148473eb87aSJeeja KP } 149473eb87aSJeeja KP 150473eb87aSJeeja KP return NULL; 151473eb87aSJeeja KP } 1524b235c43SVinod Koul 153f65cf7d6SYong Zhi int skl_get_dmic_geo(struct skl *skl) 154f65cf7d6SYong Zhi { 155f65cf7d6SYong Zhi struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 156f65cf7d6SYong Zhi struct nhlt_endpoint *epnt; 157f65cf7d6SYong Zhi struct nhlt_dmic_array_config *cfg; 158f65cf7d6SYong Zhi struct device *dev = &skl->pci->dev; 159f65cf7d6SYong Zhi unsigned int dmic_geo = 0; 160f65cf7d6SYong Zhi u8 j; 161f65cf7d6SYong Zhi 162f65cf7d6SYong Zhi epnt = (struct nhlt_endpoint *)nhlt->desc; 163f65cf7d6SYong Zhi 164f65cf7d6SYong Zhi for (j = 0; j < nhlt->endpoint_count; j++) { 165f65cf7d6SYong Zhi if (epnt->linktype == NHLT_LINK_DMIC) { 166f65cf7d6SYong Zhi cfg = (struct nhlt_dmic_array_config *) 167f65cf7d6SYong Zhi (epnt->config.caps); 168f65cf7d6SYong Zhi switch (cfg->array_type) { 169f65cf7d6SYong Zhi case NHLT_MIC_ARRAY_2CH_SMALL: 170f65cf7d6SYong Zhi case NHLT_MIC_ARRAY_2CH_BIG: 171f65cf7d6SYong Zhi dmic_geo |= MIC_ARRAY_2CH; 172f65cf7d6SYong Zhi break; 173f65cf7d6SYong Zhi 174f65cf7d6SYong Zhi case NHLT_MIC_ARRAY_4CH_1ST_GEOM: 175f65cf7d6SYong Zhi case NHLT_MIC_ARRAY_4CH_L_SHAPED: 176f65cf7d6SYong Zhi case NHLT_MIC_ARRAY_4CH_2ND_GEOM: 177f65cf7d6SYong Zhi dmic_geo |= MIC_ARRAY_4CH; 178f65cf7d6SYong Zhi break; 179f65cf7d6SYong Zhi 180f65cf7d6SYong Zhi default: 181f65cf7d6SYong Zhi dev_warn(dev, "undefined DMIC array_type 0x%0x\n", 182f65cf7d6SYong Zhi cfg->array_type); 183f65cf7d6SYong Zhi 184f65cf7d6SYong Zhi } 185f65cf7d6SYong Zhi } 186f65cf7d6SYong Zhi epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 187f65cf7d6SYong Zhi } 188f65cf7d6SYong Zhi 189f65cf7d6SYong Zhi return dmic_geo; 190f65cf7d6SYong Zhi } 191f65cf7d6SYong Zhi 1920cf5a171SSubhransu S. Prusty static void skl_nhlt_trim_space(char *trim) 1934b235c43SVinod Koul { 1940cf5a171SSubhransu S. Prusty char *s = trim; 1954b235c43SVinod Koul int cnt; 1964b235c43SVinod Koul int i; 1974b235c43SVinod Koul 1984b235c43SVinod Koul cnt = 0; 1994b235c43SVinod Koul for (i = 0; s[i]; i++) { 2004b235c43SVinod Koul if (!isspace(s[i])) 2014b235c43SVinod Koul s[cnt++] = s[i]; 2024b235c43SVinod Koul } 2034b235c43SVinod Koul 2044b235c43SVinod Koul s[cnt] = '\0'; 2054b235c43SVinod Koul } 2064b235c43SVinod Koul 2074b235c43SVinod Koul int skl_nhlt_update_topology_bin(struct skl *skl) 2084b235c43SVinod Koul { 2094b235c43SVinod Koul struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 2104b235c43SVinod Koul struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); 2114b235c43SVinod Koul struct device *dev = bus->dev; 2124b235c43SVinod Koul 2134b235c43SVinod Koul dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n", 2144b235c43SVinod Koul nhlt->header.oem_id, nhlt->header.oem_table_id, 2154b235c43SVinod Koul nhlt->header.oem_revision); 2164b235c43SVinod Koul 2174b235c43SVinod Koul snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", 2184b235c43SVinod Koul skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, 2194b235c43SVinod Koul nhlt->header.oem_revision, "-tplg.bin"); 2204b235c43SVinod Koul 2210cf5a171SSubhransu S. Prusty skl_nhlt_trim_space(skl->tplg_name); 2224b235c43SVinod Koul 2234b235c43SVinod Koul return 0; 2244b235c43SVinod Koul } 2250cf5a171SSubhransu S. Prusty 2260cf5a171SSubhransu S. Prusty static ssize_t skl_nhlt_platform_id_show(struct device *dev, 2270cf5a171SSubhransu S. Prusty struct device_attribute *attr, char *buf) 2280cf5a171SSubhransu S. Prusty { 2290cf5a171SSubhransu S. Prusty struct pci_dev *pci = to_pci_dev(dev); 2300cf5a171SSubhransu S. Prusty struct hdac_ext_bus *ebus = pci_get_drvdata(pci); 2310cf5a171SSubhransu S. Prusty struct skl *skl = ebus_to_skl(ebus); 2320cf5a171SSubhransu S. Prusty struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 2330cf5a171SSubhransu S. Prusty char platform_id[32]; 2340cf5a171SSubhransu S. Prusty 2350cf5a171SSubhransu S. Prusty sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id, 2360cf5a171SSubhransu S. Prusty nhlt->header.oem_id, nhlt->header.oem_table_id, 2370cf5a171SSubhransu S. Prusty nhlt->header.oem_revision); 2380cf5a171SSubhransu S. Prusty 2390cf5a171SSubhransu S. Prusty skl_nhlt_trim_space(platform_id); 2400cf5a171SSubhransu S. Prusty return sprintf(buf, "%s\n", platform_id); 2410cf5a171SSubhransu S. Prusty } 2420cf5a171SSubhransu S. Prusty 2430cf5a171SSubhransu S. Prusty static DEVICE_ATTR(platform_id, 0444, skl_nhlt_platform_id_show, NULL); 2440cf5a171SSubhransu S. Prusty 2450cf5a171SSubhransu S. Prusty int skl_nhlt_create_sysfs(struct skl *skl) 2460cf5a171SSubhransu S. Prusty { 2470cf5a171SSubhransu S. Prusty struct device *dev = &skl->pci->dev; 2480cf5a171SSubhransu S. Prusty 2490cf5a171SSubhransu S. Prusty if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr)) 2500cf5a171SSubhransu S. Prusty dev_warn(dev, "Error creating sysfs entry\n"); 2510cf5a171SSubhransu S. Prusty 2520cf5a171SSubhransu S. Prusty return 0; 2530cf5a171SSubhransu S. Prusty } 2540cf5a171SSubhransu S. Prusty 2550cf5a171SSubhransu S. Prusty void skl_nhlt_remove_sysfs(struct skl *skl) 2560cf5a171SSubhransu S. Prusty { 2570cf5a171SSubhransu S. Prusty struct device *dev = &skl->pci->dev; 2580cf5a171SSubhransu S. Prusty 2590cf5a171SSubhransu S. Prusty sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); 2600cf5a171SSubhransu S. Prusty } 261