|
@@ -19,6 +19,7 @@
|
|
|
*/
|
|
|
#include <linux/pci.h>
|
|
|
#include "skl.h"
|
|
|
+#include "skl-i2s.h"
|
|
|
|
|
|
/* Unique identification for getting NHLT blobs */
|
|
|
static guid_t osc_guid =
|
|
@@ -262,3 +263,157 @@ void skl_nhlt_remove_sysfs(struct skl *skl)
|
|
|
|
|
|
sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr);
|
|
|
}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Queries NHLT for all the fmt configuration for a particular endpoint and
|
|
|
+ * stores all possible rates supported in a rate table for the corresponding
|
|
|
+ * sclk/sclkfs.
|
|
|
+ */
|
|
|
+void skl_get_ssp_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks,
|
|
|
+ struct nhlt_fmt *fmt, u8 id)
|
|
|
+{
|
|
|
+ struct skl_i2s_config_blob_legacy *i2s_config;
|
|
|
+ struct skl_clk_parent_src *parent;
|
|
|
+ struct skl_ssp_clk *sclk, *sclkfs;
|
|
|
+ struct nhlt_fmt_cfg *fmt_cfg;
|
|
|
+ struct wav_fmt_ext *wav_fmt;
|
|
|
+ unsigned long rate = 0;
|
|
|
+ bool present = false;
|
|
|
+ int rate_index = 0;
|
|
|
+ u16 channels, bps;
|
|
|
+ u8 clk_src;
|
|
|
+ int i, j;
|
|
|
+ u32 fs;
|
|
|
+
|
|
|
+ sclk = &ssp_clks[SKL_SCLK_OFS];
|
|
|
+ sclkfs = &ssp_clks[SKL_SCLKFS_OFS];
|
|
|
+
|
|
|
+ if (fmt->fmt_count == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for (i = 0; i < fmt->fmt_count; i++) {
|
|
|
+ fmt_cfg = &fmt->fmt_config[i];
|
|
|
+ wav_fmt = &fmt_cfg->fmt_ext;
|
|
|
+
|
|
|
+ channels = wav_fmt->fmt.channels;
|
|
|
+ bps = wav_fmt->fmt.bits_per_sample;
|
|
|
+ fs = wav_fmt->fmt.samples_per_sec;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * In case of TDM configuration on a ssp, there can
|
|
|
+ * be more than one blob in which channel masks are
|
|
|
+ * different for each usecase for a specific rate and bps.
|
|
|
+ * But the sclk rate will be generated for the total
|
|
|
+ * number of channels used for that endpoint.
|
|
|
+ *
|
|
|
+ * So for the given fs and bps, choose blob which has
|
|
|
+ * the superset of all channels for that endpoint and
|
|
|
+ * derive the rate.
|
|
|
+ */
|
|
|
+ for (j = i; j < fmt->fmt_count; j++) {
|
|
|
+ fmt_cfg = &fmt->fmt_config[j];
|
|
|
+ wav_fmt = &fmt_cfg->fmt_ext;
|
|
|
+ if ((fs == wav_fmt->fmt.samples_per_sec) &&
|
|
|
+ (bps == wav_fmt->fmt.bits_per_sample))
|
|
|
+ channels = max_t(u16, channels,
|
|
|
+ wav_fmt->fmt.channels);
|
|
|
+ }
|
|
|
+
|
|
|
+ rate = channels * bps * fs;
|
|
|
+
|
|
|
+ /* check if the rate is added already to the given SSP's sclk */
|
|
|
+ for (j = 0; (sclk[id].rate_cfg[j].rate != 0) &&
|
|
|
+ (j < SKL_MAX_CLK_RATES); j++) {
|
|
|
+ if (sclk[id].rate_cfg[j].rate == rate) {
|
|
|
+ present = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Fill rate and parent for sclk/sclkfs */
|
|
|
+ if (!present) {
|
|
|
+ /* MCLK Divider Source Select */
|
|
|
+ i2s_config = (struct skl_i2s_config_blob_legacy *)
|
|
|
+ fmt->fmt_config[0].config.caps;
|
|
|
+ clk_src = ((i2s_config->mclk.mdivctrl)
|
|
|
+ & SKL_MNDSS_DIV_CLK_SRC_MASK) >>
|
|
|
+ SKL_SHIFT(SKL_MNDSS_DIV_CLK_SRC_MASK);
|
|
|
+
|
|
|
+ parent = skl_get_parent_clk(clk_src);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do not copy the config data if there is no parent
|
|
|
+ * clock available for this clock source select
|
|
|
+ */
|
|
|
+ if (!parent)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ sclk[id].rate_cfg[rate_index].rate = rate;
|
|
|
+ sclk[id].rate_cfg[rate_index].config = fmt_cfg;
|
|
|
+ sclkfs[id].rate_cfg[rate_index].rate = rate;
|
|
|
+ sclkfs[id].rate_cfg[rate_index].config = fmt_cfg;
|
|
|
+ sclk[id].parent_name = parent->name;
|
|
|
+ sclkfs[id].parent_name = parent->name;
|
|
|
+
|
|
|
+ rate_index++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void skl_get_mclk(struct skl *skl, struct skl_ssp_clk *mclk,
|
|
|
+ struct nhlt_fmt *fmt, u8 id)
|
|
|
+{
|
|
|
+ struct skl_i2s_config_blob_legacy *i2s_config;
|
|
|
+ struct nhlt_specific_cfg *fmt_cfg;
|
|
|
+ struct skl_clk_parent_src *parent;
|
|
|
+ u32 clkdiv, div_ratio;
|
|
|
+ u8 clk_src;
|
|
|
+
|
|
|
+ fmt_cfg = &fmt->fmt_config[0].config;
|
|
|
+ i2s_config = (struct skl_i2s_config_blob_legacy *)fmt_cfg->caps;
|
|
|
+
|
|
|
+ /* MCLK Divider Source Select */
|
|
|
+ clk_src = ((i2s_config->mclk.mdivctrl) & SKL_MCLK_DIV_CLK_SRC_MASK) >>
|
|
|
+ SKL_SHIFT(SKL_MCLK_DIV_CLK_SRC_MASK);
|
|
|
+
|
|
|
+ clkdiv = i2s_config->mclk.mdivr & SKL_MCLK_DIV_RATIO_MASK;
|
|
|
+
|
|
|
+ /* bypass divider */
|
|
|
+ div_ratio = 1;
|
|
|
+
|
|
|
+ if (clkdiv != SKL_MCLK_DIV_RATIO_MASK)
|
|
|
+ /* Divider is 2 + clkdiv */
|
|
|
+ div_ratio = clkdiv + 2;
|
|
|
+
|
|
|
+ /* Calculate MCLK rate from source using div value */
|
|
|
+ parent = skl_get_parent_clk(clk_src);
|
|
|
+ if (!parent)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mclk[id].rate_cfg[0].rate = parent->rate/div_ratio;
|
|
|
+ mclk[id].rate_cfg[0].config = &fmt->fmt_config[0];
|
|
|
+ mclk[id].parent_name = parent->name;
|
|
|
+}
|
|
|
+
|
|
|
+void skl_get_clks(struct skl *skl, struct skl_ssp_clk *ssp_clks)
|
|
|
+{
|
|
|
+ struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
|
|
|
+ struct nhlt_endpoint *epnt;
|
|
|
+ struct nhlt_fmt *fmt;
|
|
|
+ int i;
|
|
|
+ u8 id;
|
|
|
+
|
|
|
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
|
|
|
+ for (i = 0; i < nhlt->endpoint_count; i++) {
|
|
|
+ if (epnt->linktype == NHLT_LINK_SSP) {
|
|
|
+ id = epnt->virtual_bus_id;
|
|
|
+
|
|
|
+ fmt = (struct nhlt_fmt *)(epnt->config.caps
|
|
|
+ + epnt->config.size);
|
|
|
+
|
|
|
+ skl_get_ssp_clks(skl, ssp_clks, fmt, id);
|
|
|
+ skl_get_mclk(skl, ssp_clks, fmt, id);
|
|
|
+ }
|
|
|
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
|
|
|
+ }
|
|
|
+}
|