18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * skl-nhlt.c - Intel SKL Platform NHLT parsing 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Intel Corp 68c2ecf20Sopenharmony_ci * Author: Sanjiv Kumar <sanjiv.kumar@intel.com> 78c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <sound/intel-nhlt.h> 138c2ecf20Sopenharmony_ci#include "skl.h" 148c2ecf20Sopenharmony_ci#include "skl-i2s.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic struct nhlt_specific_cfg *skl_get_specific_cfg( 178c2ecf20Sopenharmony_ci struct device *dev, struct nhlt_fmt *fmt, 188c2ecf20Sopenharmony_ci u8 no_ch, u32 rate, u16 bps, u8 linktype) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct nhlt_specific_cfg *sp_config; 218c2ecf20Sopenharmony_ci struct wav_fmt *wfmt; 228c2ecf20Sopenharmony_ci struct nhlt_fmt_cfg *fmt_config = fmt->fmt_config; 238c2ecf20Sopenharmony_ci int i; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci dev_dbg(dev, "Format count =%d\n", fmt->fmt_count); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci for (i = 0; i < fmt->fmt_count; i++) { 288c2ecf20Sopenharmony_ci wfmt = &fmt_config->fmt_ext.fmt; 298c2ecf20Sopenharmony_ci dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", wfmt->channels, 308c2ecf20Sopenharmony_ci wfmt->bits_per_sample, wfmt->samples_per_sec); 318c2ecf20Sopenharmony_ci if (wfmt->channels == no_ch && wfmt->bits_per_sample == bps) { 328c2ecf20Sopenharmony_ci /* 338c2ecf20Sopenharmony_ci * if link type is dmic ignore rate check as the blob is 348c2ecf20Sopenharmony_ci * generic for all rates 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci sp_config = &fmt_config->config; 378c2ecf20Sopenharmony_ci if (linktype == NHLT_LINK_DMIC) 388c2ecf20Sopenharmony_ci return sp_config; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (wfmt->samples_per_sec == rate) 418c2ecf20Sopenharmony_ci return sp_config; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci fmt_config = (struct nhlt_fmt_cfg *)(fmt_config->config.caps + 458c2ecf20Sopenharmony_ci fmt_config->config.size); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return NULL; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void dump_config(struct device *dev, u32 instance_id, u8 linktype, 528c2ecf20Sopenharmony_ci u8 s_fmt, u8 num_channels, u32 s_rate, u8 dirn, u16 bps) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci dev_dbg(dev, "Input configuration\n"); 558c2ecf20Sopenharmony_ci dev_dbg(dev, "ch=%d fmt=%d s_rate=%d\n", num_channels, s_fmt, s_rate); 568c2ecf20Sopenharmony_ci dev_dbg(dev, "vbus_id=%d link_type=%d\n", instance_id, linktype); 578c2ecf20Sopenharmony_ci dev_dbg(dev, "bits_per_sample=%d\n", bps); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt, 618c2ecf20Sopenharmony_ci u32 instance_id, u8 link_type, u8 dirn, u8 dev_type) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n", 648c2ecf20Sopenharmony_ci epnt->virtual_bus_id, epnt->linktype, 658c2ecf20Sopenharmony_ci epnt->direction, epnt->device_type); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if ((epnt->virtual_bus_id == instance_id) && 688c2ecf20Sopenharmony_ci (epnt->linktype == link_type) && 698c2ecf20Sopenharmony_ci (epnt->direction == dirn)) { 708c2ecf20Sopenharmony_ci /* do not check dev_type for DMIC link type */ 718c2ecf20Sopenharmony_ci if (epnt->linktype == NHLT_LINK_DMIC) 728c2ecf20Sopenharmony_ci return true; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (epnt->device_type == dev_type) 758c2ecf20Sopenharmony_ci return true; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return false; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistruct nhlt_specific_cfg 828c2ecf20Sopenharmony_ci*skl_get_ep_blob(struct skl_dev *skl, u32 instance, u8 link_type, 838c2ecf20Sopenharmony_ci u8 s_fmt, u8 num_ch, u32 s_rate, 848c2ecf20Sopenharmony_ci u8 dirn, u8 dev_type) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct nhlt_fmt *fmt; 878c2ecf20Sopenharmony_ci struct nhlt_endpoint *epnt; 888c2ecf20Sopenharmony_ci struct hdac_bus *bus = skl_to_bus(skl); 898c2ecf20Sopenharmony_ci struct device *dev = bus->dev; 908c2ecf20Sopenharmony_ci struct nhlt_specific_cfg *sp_config; 918c2ecf20Sopenharmony_ci struct nhlt_acpi_table *nhlt = skl->nhlt; 928c2ecf20Sopenharmony_ci u16 bps = (s_fmt == 16) ? 16 : 32; 938c2ecf20Sopenharmony_ci u8 j; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci dump_config(dev, instance, link_type, s_fmt, num_ch, s_rate, dirn, bps); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci epnt = (struct nhlt_endpoint *)nhlt->desc; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci for (j = 0; j < nhlt->endpoint_count; j++) { 1028c2ecf20Sopenharmony_ci if (skl_check_ep_match(dev, epnt, instance, link_type, 1038c2ecf20Sopenharmony_ci dirn, dev_type)) { 1048c2ecf20Sopenharmony_ci fmt = (struct nhlt_fmt *)(epnt->config.caps + 1058c2ecf20Sopenharmony_ci epnt->config.size); 1068c2ecf20Sopenharmony_ci sp_config = skl_get_specific_cfg(dev, fmt, num_ch, 1078c2ecf20Sopenharmony_ci s_rate, bps, link_type); 1088c2ecf20Sopenharmony_ci if (sp_config) 1098c2ecf20Sopenharmony_ci return sp_config; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return NULL; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void skl_nhlt_trim_space(char *trim) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci char *s = trim; 1218c2ecf20Sopenharmony_ci int cnt; 1228c2ecf20Sopenharmony_ci int i; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci cnt = 0; 1258c2ecf20Sopenharmony_ci for (i = 0; s[i]; i++) { 1268c2ecf20Sopenharmony_ci if (!isspace(s[i])) 1278c2ecf20Sopenharmony_ci s[cnt++] = s[i]; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci s[cnt] = '\0'; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciint skl_nhlt_update_topology_bin(struct skl_dev *skl) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 1368c2ecf20Sopenharmony_ci struct hdac_bus *bus = skl_to_bus(skl); 1378c2ecf20Sopenharmony_ci struct device *dev = bus->dev; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci dev_dbg(dev, "oem_id %.6s, oem_table_id %.8s oem_revision %d\n", 1408c2ecf20Sopenharmony_ci nhlt->header.oem_id, nhlt->header.oem_table_id, 1418c2ecf20Sopenharmony_ci nhlt->header.oem_revision); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s", 1448c2ecf20Sopenharmony_ci skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id, 1458c2ecf20Sopenharmony_ci nhlt->header.oem_revision, "-tplg.bin"); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci skl_nhlt_trim_space(skl->tplg_name); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic ssize_t skl_nhlt_platform_id_show(struct device *dev, 1538c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct pci_dev *pci = to_pci_dev(dev); 1568c2ecf20Sopenharmony_ci struct hdac_bus *bus = pci_get_drvdata(pci); 1578c2ecf20Sopenharmony_ci struct skl_dev *skl = bus_to_skl(bus); 1588c2ecf20Sopenharmony_ci struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 1598c2ecf20Sopenharmony_ci char platform_id[32]; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id, 1628c2ecf20Sopenharmony_ci nhlt->header.oem_id, nhlt->header.oem_table_id, 1638c2ecf20Sopenharmony_ci nhlt->header.oem_revision); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci skl_nhlt_trim_space(platform_id); 1668c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", platform_id); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic DEVICE_ATTR(platform_id, 0444, skl_nhlt_platform_id_show, NULL); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciint skl_nhlt_create_sysfs(struct skl_dev *skl) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct device *dev = &skl->pci->dev; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr)) 1768c2ecf20Sopenharmony_ci dev_warn(dev, "Error creating sysfs entry\n"); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_civoid skl_nhlt_remove_sysfs(struct skl_dev *skl) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct device *dev = &skl->pci->dev; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (skl->nhlt) 1868c2ecf20Sopenharmony_ci sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Queries NHLT for all the fmt configuration for a particular endpoint and 1918c2ecf20Sopenharmony_ci * stores all possible rates supported in a rate table for the corresponding 1928c2ecf20Sopenharmony_ci * sclk/sclkfs. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_cistatic void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks, 1958c2ecf20Sopenharmony_ci struct nhlt_fmt *fmt, u8 id) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct skl_i2s_config_blob_ext *i2s_config_ext; 1988c2ecf20Sopenharmony_ci struct skl_i2s_config_blob_legacy *i2s_config; 1998c2ecf20Sopenharmony_ci struct skl_clk_parent_src *parent; 2008c2ecf20Sopenharmony_ci struct skl_ssp_clk *sclk, *sclkfs; 2018c2ecf20Sopenharmony_ci struct nhlt_fmt_cfg *fmt_cfg; 2028c2ecf20Sopenharmony_ci struct wav_fmt_ext *wav_fmt; 2038c2ecf20Sopenharmony_ci unsigned long rate; 2048c2ecf20Sopenharmony_ci int rate_index = 0; 2058c2ecf20Sopenharmony_ci u16 channels, bps; 2068c2ecf20Sopenharmony_ci u8 clk_src; 2078c2ecf20Sopenharmony_ci int i, j; 2088c2ecf20Sopenharmony_ci u32 fs; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci sclk = &ssp_clks[SKL_SCLK_OFS]; 2118c2ecf20Sopenharmony_ci sclkfs = &ssp_clks[SKL_SCLKFS_OFS]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (fmt->fmt_count == 0) 2148c2ecf20Sopenharmony_ci return; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config; 2178c2ecf20Sopenharmony_ci for (i = 0; i < fmt->fmt_count; i++) { 2188c2ecf20Sopenharmony_ci struct nhlt_fmt_cfg *saved_fmt_cfg = fmt_cfg; 2198c2ecf20Sopenharmony_ci bool present = false; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci wav_fmt = &saved_fmt_cfg->fmt_ext; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci channels = wav_fmt->fmt.channels; 2248c2ecf20Sopenharmony_ci bps = wav_fmt->fmt.bits_per_sample; 2258c2ecf20Sopenharmony_ci fs = wav_fmt->fmt.samples_per_sec; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * In case of TDM configuration on a ssp, there can 2298c2ecf20Sopenharmony_ci * be more than one blob in which channel masks are 2308c2ecf20Sopenharmony_ci * different for each usecase for a specific rate and bps. 2318c2ecf20Sopenharmony_ci * But the sclk rate will be generated for the total 2328c2ecf20Sopenharmony_ci * number of channels used for that endpoint. 2338c2ecf20Sopenharmony_ci * 2348c2ecf20Sopenharmony_ci * So for the given fs and bps, choose blob which has 2358c2ecf20Sopenharmony_ci * the superset of all channels for that endpoint and 2368c2ecf20Sopenharmony_ci * derive the rate. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci for (j = i; j < fmt->fmt_count; j++) { 2398c2ecf20Sopenharmony_ci struct nhlt_fmt_cfg *tmp_fmt_cfg = fmt_cfg; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci wav_fmt = &tmp_fmt_cfg->fmt_ext; 2428c2ecf20Sopenharmony_ci if ((fs == wav_fmt->fmt.samples_per_sec) && 2438c2ecf20Sopenharmony_ci (bps == wav_fmt->fmt.bits_per_sample)) { 2448c2ecf20Sopenharmony_ci channels = max_t(u16, channels, 2458c2ecf20Sopenharmony_ci wav_fmt->fmt.channels); 2468c2ecf20Sopenharmony_ci saved_fmt_cfg = tmp_fmt_cfg; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci /* Move to the next nhlt_fmt_cfg */ 2498c2ecf20Sopenharmony_ci tmp_fmt_cfg = (struct nhlt_fmt_cfg *)(tmp_fmt_cfg->config.caps + 2508c2ecf20Sopenharmony_ci tmp_fmt_cfg->config.size); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci rate = channels * bps * fs; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* check if the rate is added already to the given SSP's sclk */ 2568c2ecf20Sopenharmony_ci for (j = 0; (j < SKL_MAX_CLK_RATES) && 2578c2ecf20Sopenharmony_ci (sclk[id].rate_cfg[j].rate != 0); j++) { 2588c2ecf20Sopenharmony_ci if (sclk[id].rate_cfg[j].rate == rate) { 2598c2ecf20Sopenharmony_ci present = true; 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Fill rate and parent for sclk/sclkfs */ 2658c2ecf20Sopenharmony_ci if (!present) { 2668c2ecf20Sopenharmony_ci struct nhlt_fmt_cfg *first_fmt_cfg; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci first_fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config; 2698c2ecf20Sopenharmony_ci i2s_config_ext = (struct skl_i2s_config_blob_ext *) 2708c2ecf20Sopenharmony_ci first_fmt_cfg->config.caps; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* MCLK Divider Source Select */ 2738c2ecf20Sopenharmony_ci if (is_legacy_blob(i2s_config_ext->hdr.sig)) { 2748c2ecf20Sopenharmony_ci i2s_config = ext_to_legacy_blob(i2s_config_ext); 2758c2ecf20Sopenharmony_ci clk_src = get_clk_src(i2s_config->mclk, 2768c2ecf20Sopenharmony_ci SKL_MNDSS_DIV_CLK_SRC_MASK); 2778c2ecf20Sopenharmony_ci } else { 2788c2ecf20Sopenharmony_ci clk_src = get_clk_src(i2s_config_ext->mclk, 2798c2ecf20Sopenharmony_ci SKL_MNDSS_DIV_CLK_SRC_MASK); 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci parent = skl_get_parent_clk(clk_src); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Move to the next nhlt_fmt_cfg */ 2858c2ecf20Sopenharmony_ci fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps + 2868c2ecf20Sopenharmony_ci fmt_cfg->config.size); 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * Do not copy the config data if there is no parent 2898c2ecf20Sopenharmony_ci * clock available for this clock source select 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci if (!parent) 2928c2ecf20Sopenharmony_ci continue; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci sclk[id].rate_cfg[rate_index].rate = rate; 2958c2ecf20Sopenharmony_ci sclk[id].rate_cfg[rate_index].config = saved_fmt_cfg; 2968c2ecf20Sopenharmony_ci sclkfs[id].rate_cfg[rate_index].rate = rate; 2978c2ecf20Sopenharmony_ci sclkfs[id].rate_cfg[rate_index].config = saved_fmt_cfg; 2988c2ecf20Sopenharmony_ci sclk[id].parent_name = parent->name; 2998c2ecf20Sopenharmony_ci sclkfs[id].parent_name = parent->name; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci rate_index++; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk, 3078c2ecf20Sopenharmony_ci struct nhlt_fmt *fmt, u8 id) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct skl_i2s_config_blob_ext *i2s_config_ext; 3108c2ecf20Sopenharmony_ci struct skl_i2s_config_blob_legacy *i2s_config; 3118c2ecf20Sopenharmony_ci struct nhlt_fmt_cfg *fmt_cfg; 3128c2ecf20Sopenharmony_ci struct skl_clk_parent_src *parent; 3138c2ecf20Sopenharmony_ci u32 clkdiv, div_ratio; 3148c2ecf20Sopenharmony_ci u8 clk_src; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config; 3178c2ecf20Sopenharmony_ci i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->config.caps; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* MCLK Divider Source Select and divider */ 3208c2ecf20Sopenharmony_ci if (is_legacy_blob(i2s_config_ext->hdr.sig)) { 3218c2ecf20Sopenharmony_ci i2s_config = ext_to_legacy_blob(i2s_config_ext); 3228c2ecf20Sopenharmony_ci clk_src = get_clk_src(i2s_config->mclk, 3238c2ecf20Sopenharmony_ci SKL_MCLK_DIV_CLK_SRC_MASK); 3248c2ecf20Sopenharmony_ci clkdiv = i2s_config->mclk.mdivr & 3258c2ecf20Sopenharmony_ci SKL_MCLK_DIV_RATIO_MASK; 3268c2ecf20Sopenharmony_ci } else { 3278c2ecf20Sopenharmony_ci clk_src = get_clk_src(i2s_config_ext->mclk, 3288c2ecf20Sopenharmony_ci SKL_MCLK_DIV_CLK_SRC_MASK); 3298c2ecf20Sopenharmony_ci clkdiv = i2s_config_ext->mclk.mdivr[0] & 3308c2ecf20Sopenharmony_ci SKL_MCLK_DIV_RATIO_MASK; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* bypass divider */ 3348c2ecf20Sopenharmony_ci div_ratio = 1; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (clkdiv != SKL_MCLK_DIV_RATIO_MASK) 3378c2ecf20Sopenharmony_ci /* Divider is 2 + clkdiv */ 3388c2ecf20Sopenharmony_ci div_ratio = clkdiv + 2; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* Calculate MCLK rate from source using div value */ 3418c2ecf20Sopenharmony_ci parent = skl_get_parent_clk(clk_src); 3428c2ecf20Sopenharmony_ci if (!parent) 3438c2ecf20Sopenharmony_ci return; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci mclk[id].rate_cfg[0].rate = parent->rate/div_ratio; 3468c2ecf20Sopenharmony_ci mclk[id].rate_cfg[0].config = fmt_cfg; 3478c2ecf20Sopenharmony_ci mclk[id].parent_name = parent->name; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_civoid skl_get_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt; 3538c2ecf20Sopenharmony_ci struct nhlt_endpoint *epnt; 3548c2ecf20Sopenharmony_ci struct nhlt_fmt *fmt; 3558c2ecf20Sopenharmony_ci int i; 3568c2ecf20Sopenharmony_ci u8 id; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci epnt = (struct nhlt_endpoint *)nhlt->desc; 3598c2ecf20Sopenharmony_ci for (i = 0; i < nhlt->endpoint_count; i++) { 3608c2ecf20Sopenharmony_ci if (epnt->linktype == NHLT_LINK_SSP) { 3618c2ecf20Sopenharmony_ci id = epnt->virtual_bus_id; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci fmt = (struct nhlt_fmt *)(epnt->config.caps 3648c2ecf20Sopenharmony_ci + epnt->config.size); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci skl_get_ssp_clks(skl, ssp_clks, fmt, id); 3678c2ecf20Sopenharmony_ci skl_get_mclk(skl, ssp_clks, fmt, id); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci} 372