Merge remote-tracking branch 'asoc/topic/intel' into asoc-next
authorMark Brown <broonie@kernel.org>
Thu, 29 Sep 2016 19:43:59 +0000 (12:43 -0700)
committerMark Brown <broonie@kernel.org>
Thu, 29 Sep 2016 19:43:59 +0000 (12:43 -0700)
38 files changed:
include/sound/hda_register.h
include/sound/hdaudio.h
include/sound/hdaudio_ext.h
include/uapi/sound/Kbuild
include/uapi/sound/snd_sst_tokens.h [new file with mode: 0644]
sound/hda/ext/hdac_ext_controller.c
sound/hda/ext/hdac_ext_stream.c
sound/hda/hdac_controller.c
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_controller.h
sound/pci/hda/hda_intel.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5640.h
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst-atom-controls.c
sound/soc/intel/atom/sst-atom-controls.h
sound/soc/intel/atom/sst/sst.c
sound/soc/intel/atom/sst/sst_acpi.c
sound/soc/intel/boards/Makefile
sound/soc/intel/boards/bdw-rt5677.c [new file with mode: 0644]
sound/soc/intel/boards/bxt_da7219_max98357a.c
sound/soc/intel/boards/bxt_rt298.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/common/sst-acpi.c
sound/soc/intel/skylake/bxt-sst.c
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-sst-cldma.c
sound/soc/intel/skylake/skl-sst-dsp.h
sound/soc/intel/skylake/skl-sst-ipc.c
sound/soc/intel/skylake/skl-sst-ipc.h
sound/soc/intel/skylake/skl-sst-utils.c
sound/soc/intel/skylake/skl-sst.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl-tplg-interface.h
sound/soc/intel/skylake/skl.c
sound/soc/intel/skylake/skl.h

index ff1aecf..0013063 100644 (file)
@@ -89,6 +89,19 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_REG_SD_BDLPL               0x18
 #define AZX_REG_SD_BDLPU               0x1c
 
+/* GTS registers */
+#define AZX_REG_LLCH                   0x14
+
+#define AZX_REG_GTS_BASE               0x520
+
+#define AZX_REG_GTSCC  (AZX_REG_GTS_BASE + 0x00)
+#define AZX_REG_WALFCC (AZX_REG_GTS_BASE + 0x04)
+#define AZX_REG_TSCCL  (AZX_REG_GTS_BASE + 0x08)
+#define AZX_REG_TSCCU  (AZX_REG_GTS_BASE + 0x0C)
+#define AZX_REG_LLPFOC (AZX_REG_GTS_BASE + 0x14)
+#define AZX_REG_LLPCL  (AZX_REG_GTS_BASE + 0x18)
+#define AZX_REG_LLPCU  (AZX_REG_GTS_BASE + 0x1C)
+
 /* Haswell/Broadwell display HD-A controller Extended Mode registers */
 #define AZX_REG_HSW_EM4                        0x100c
 #define AZX_REG_HSW_EM5                        0x1010
@@ -242,6 +255,29 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 /* Interval used to calculate the iterating register offset */
 #define AZX_DRSM_INTERVAL              0x08
 
+/* Global time synchronization registers */
+#define GTSCC_TSCCD_MASK               0x80000000
+#define GTSCC_TSCCD_SHIFT              BIT(31)
+#define GTSCC_TSCCI_MASK               0x20
+#define GTSCC_CDMAS_DMA_DIR_SHIFT      4
+
+#define WALFCC_CIF_MASK                        0x1FF
+#define WALFCC_FN_SHIFT                        9
+#define HDA_CLK_CYCLES_PER_FRAME       512
+
+/*
+ * An error occurs near frame "rollover". The clocks in frame value indicates
+ * whether this error may have occurred. Here we use the value of 10. Please
+ * see the errata for the right number [<10]
+ */
+#define HDA_MAX_CYCLE_VALUE            499
+#define HDA_MAX_CYCLE_OFFSET           10
+#define HDA_MAX_CYCLE_READ_RETRY       10
+
+#define TSCCU_CCU_SHIFT                        32
+#define LLPC_CCU_SHIFT                 32
+
+
 /*
  * helpers to read the stream position
  */
index 93e63c5..56004ec 100644 (file)
@@ -245,6 +245,12 @@ struct hdac_rb {
 
 /*
  * HD-audio bus base driver
+ *
+ * @ppcap: pp capabilities pointer
+ * @spbcap: SPIB capabilities pointer
+ * @mlcap: MultiLink capabilities pointer
+ * @gtscap: gts capabilities pointer
+ * @drsmcap: dma resume capabilities pointer
  */
 struct hdac_bus {
        struct device *dev;
@@ -256,6 +262,12 @@ struct hdac_bus {
        void __iomem *remap_addr;
        int irq;
 
+       void __iomem *ppcap;
+       void __iomem *spbcap;
+       void __iomem *mlcap;
+       void __iomem *gtscap;
+       void __iomem *drsmcap;
+
        /* codec linked list */
        struct list_head codec_list;
        unsigned int num_codecs;
@@ -335,6 +347,7 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
 int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val);
 int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
                              unsigned int *res);
+int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus);
 int snd_hdac_link_power(struct hdac_device *codec, bool enable);
 
 bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
index b9593b2..8660a7f 100644 (file)
@@ -8,11 +8,6 @@
  *
  * @bus: hdac bus
  * @num_streams: streams supported
- * @ppcap: pp capabilities pointer
- * @spbcap: SPIB capabilities pointer
- * @mlcap: MultiLink capabilities pointer
- * @gtscap: gts capabilities pointer
- * @drsmcap: dma resume capabilities pointer
  * @hlink_list: link list of HDA links
  * @lock: lock for link mgmt
  * @cmd_dma_state: state of cmd DMAs: CORB and RIRB
@@ -22,12 +17,6 @@ struct hdac_ext_bus {
        int num_streams;
        int idx;
 
-       void __iomem *ppcap;
-       void __iomem *spbcap;
-       void __iomem *mlcap;
-       void __iomem *gtscap;
-       void __iomem *drsmcap;
-
        struct list_head hlink_list;
 
        struct mutex lock;
@@ -54,7 +43,6 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus);
 #define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \
        HDA_CODEC_REV_EXT_ENTRY(_vid, _revid, _name, _drv_data)
 
-int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *sbus);
 void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable);
 void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable);
 
index 691984c..9578d8b 100644 (file)
@@ -13,3 +13,4 @@ header-y += sb16_csp.h
 header-y += sfnt_info.h
 header-y += tlv.h
 header-y += usb_stream.h
+header-y += snd_sst_tokens.h
diff --git a/include/uapi/sound/snd_sst_tokens.h b/include/uapi/sound/snd_sst_tokens.h
new file mode 100644 (file)
index 0000000..1ee2e94
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * snd_sst_tokens.h - Intel SST tokens definition
+ *
+ * Copyright (C) 2016 Intel Corp
+ * Author: Shreyas NC <shreyas.nc@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef __SND_SST_TOKENS_H__
+#define __SND_SST_TOKENS_H__
+
+/**
+ * %SKL_TKN_UUID:               Module UUID
+ *
+ * %SKL_TKN_U8_BLOCK_TYPE:      Type of the private data block.Can be:
+ *                              tuples, bytes, short and words
+ *
+ * %SKL_TKN_U8_IN_PIN_TYPE:     Input pin type,
+ *                              homogenous=0, heterogenous=1
+ *
+ * %SKL_TKN_U8_OUT_PIN_TYPE:    Output pin type,
+ *                              homogenous=0, heterogenous=1
+ * %SKL_TKN_U8_DYN_IN_PIN:      Configure Input pin dynamically
+ *                              if true
+ *
+ * %SKL_TKN_U8_DYN_OUT_PIN:     Configure Output pin dynamically
+ *                              if true
+ *
+ * %SKL_TKN_U8_IN_QUEUE_COUNT:  Store the number of Input pins
+ *
+ * %SKL_TKN_U8_OUT_QUEUE_COUNT: Store the number of Output pins
+ *
+ * %SKL_TKN_U8_TIME_SLOT:       TDM slot number
+ *
+ * %SKL_TKN_U8_CORE_ID:         Stores module affinity value.Can take
+ *                              the values:
+ *                              SKL_AFFINITY_CORE_0 = 0,
+ *                              SKL_AFFINITY_CORE_1,
+ *                              SKL_AFFINITY_CORE_MAX
+ *
+ * %SKL_TKN_U8_MOD_TYPE:        Module type value.
+ *
+ * %SKL_TKN_U8_CONN_TYPE:       Module connection type can be a FE,
+ *                              BE or NONE as defined :
+ *                              SKL_PIPE_CONN_TYPE_NONE = 0,
+ *                              SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA)
+ *                              SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA)
+ *
+ * %SKL_TKN_U8_DEV_TYPE:        Type of device to which the module is
+ *                              connected
+ *                              Can take the values:
+ *                              SKL_DEVICE_BT = 0x0,
+ *                              SKL_DEVICE_DMIC = 0x1,
+ *                              SKL_DEVICE_I2S = 0x2,
+ *                              SKL_DEVICE_SLIMBUS = 0x3,
+ *                              SKL_DEVICE_HDALINK = 0x4,
+ *                              SKL_DEVICE_HDAHOST = 0x5,
+ *                              SKL_DEVICE_NONE
+ *
+ * %SKL_TKN_U8_HW_CONN_TYPE:    Connection type of the HW to which the
+ *                              module is connected
+ *                              SKL_CONN_NONE = 0,
+ *                              SKL_CONN_SOURCE = 1,
+ *                              SKL_CONN_SINK = 2
+ *
+ * %SKL_TKN_U16_PIN_INST_ID:    Stores the pin instance id
+ *
+ * %SKL_TKN_U16_MOD_INST_ID:    Stores the mdule instance id
+ *
+ * %SKL_TKN_U32_MAX_MCPS:       Module max mcps value
+ *
+ * %SKL_TKN_U32_MEM_PAGES:      Module resource pages
+ *
+ * %SKL_TKN_U32_OBS:            Stores Output Buffer size
+ *
+ * %SKL_TKN_U32_IBS:            Stores input buffer size
+ *
+ * %SKL_TKN_U32_VBUS_ID:        Module VBUS_ID. PDM=0, SSP0=0,
+ *                              SSP1=1,SSP2=2,
+ *                              SSP3=3, SSP4=4,
+ *                              SSP5=5, SSP6=6,INVALID
+ *
+ * %SKL_TKN_U32_PARAMS_FIXUP:   Module Params fixup mask
+ * %SKL_TKN_U32_CONVERTER:      Module params converter mask
+ * %SKL_TKN_U32_PIPE_ID:        Stores the pipe id
+ *
+ * %SKL_TKN_U32_PIPE_CONN_TYPE: Type of the token to which the pipe is
+ *                              connected to. It can be
+ *                              SKL_PIPE_CONN_TYPE_NONE = 0,
+ *                              SKL_PIPE_CONN_TYPE_FE = 1 (HOST_DMA),
+ *                              SKL_PIPE_CONN_TYPE_BE = 2 (LINK_DMA),
+ *
+ * %SKL_TKN_U32_PIPE_PRIORITY:  Pipe priority value
+ * %SKL_TKN_U32_PIPE_MEM_PGS:   Pipe resource pages
+ *
+ * %SKL_TKN_U32_DIR_PIN_COUNT:  Value for the direction to set input/output
+ *                              formats and the pin count.
+ *                              The first 4 bits have the direction
+ *                              value and the next 4 have
+ *                              the pin count value.
+ *                              SKL_DIR_IN = 0, SKL_DIR_OUT = 1.
+ *                              The input and output formats
+ *                              share the same set of tokens
+ *                              with the distinction between input
+ *                              and output made by reading direction
+ *                              token.
+ *
+ * %SKL_TKN_U32_FMT_CH:         Supported channel count
+ *
+ * %SKL_TKN_U32_FMT_FREQ:       Supported frequency/sample rate
+ *
+ * %SKL_TKN_U32_FMT_BIT_DEPTH:  Supported container size
+ *
+ * %SKL_TKN_U32_FMT_SAMPLE_SIZE:Number of samples in the container
+ *
+ * %SKL_TKN_U32_FMT_CH_CONFIG:  Supported channel configurations for the
+ *                              input/output.
+ *
+ * %SKL_TKN_U32_FMT_INTERLEAVE: Interleaving style which can be per
+ *                              channel or per sample. The values can be :
+ *                              SKL_INTERLEAVING_PER_CHANNEL = 0,
+ *                              SKL_INTERLEAVING_PER_SAMPLE = 1,
+ *
+ * %SKL_TKN_U32_FMT_SAMPLE_TYPE:
+ *                              Specifies the sample type. Can take the
+ *                              values: SKL_SAMPLE_TYPE_INT_MSB = 0,
+ *                              SKL_SAMPLE_TYPE_INT_LSB = 1,
+ *                              SKL_SAMPLE_TYPE_INT_SIGNED = 2,
+ *                              SKL_SAMPLE_TYPE_INT_UNSIGNED = 3,
+ *                              SKL_SAMPLE_TYPE_FLOAT = 4
+ *
+ * %SKL_TKN_U32_CH_MAP:         Channel map values
+ * %SKL_TKN_U32_MOD_SET_PARAMS: It can take these values:
+ *                              SKL_PARAM_DEFAULT, SKL_PARAM_INIT,
+ *                              SKL_PARAM_SET, SKL_PARAM_BIND
+ *
+ * %SKL_TKN_U32_MOD_PARAM_ID:   ID of the module params
+ *
+ * %SKL_TKN_U32_CAPS_SET_PARAMS:
+ *                              Set params value
+ *
+ * %SKL_TKN_U32_CAPS_PARAMS_ID: Params ID
+ *
+ * %SKL_TKN_U32_CAPS_SIZE:      Caps size
+ *
+ * %SKL_TKN_U32_PROC_DOMAIN:    Specify processing domain
+ *
+ * %SKL_TKN_U32_LIB_COUNT:      Specifies the number of libraries
+ *
+ * %SKL_TKN_STR_LIB_NAME:       Specifies the library name
+ *
+ * module_id and loadable flags dont have tokens as these values will be
+ * read from the DSP FW manifest
+ */
+enum SKL_TKNS {
+       SKL_TKN_UUID = 1,
+       SKL_TKN_U8_NUM_BLOCKS,
+       SKL_TKN_U8_BLOCK_TYPE,
+       SKL_TKN_U8_IN_PIN_TYPE,
+       SKL_TKN_U8_OUT_PIN_TYPE,
+       SKL_TKN_U8_DYN_IN_PIN,
+       SKL_TKN_U8_DYN_OUT_PIN,
+       SKL_TKN_U8_IN_QUEUE_COUNT,
+       SKL_TKN_U8_OUT_QUEUE_COUNT,
+       SKL_TKN_U8_TIME_SLOT,
+       SKL_TKN_U8_CORE_ID,
+       SKL_TKN_U8_MOD_TYPE,
+       SKL_TKN_U8_CONN_TYPE,
+       SKL_TKN_U8_DEV_TYPE,
+       SKL_TKN_U8_HW_CONN_TYPE,
+       SKL_TKN_U16_MOD_INST_ID,
+       SKL_TKN_U16_BLOCK_SIZE,
+       SKL_TKN_U32_MAX_MCPS,
+       SKL_TKN_U32_MEM_PAGES,
+       SKL_TKN_U32_OBS,
+       SKL_TKN_U32_IBS,
+       SKL_TKN_U32_VBUS_ID,
+       SKL_TKN_U32_PARAMS_FIXUP,
+       SKL_TKN_U32_CONVERTER,
+       SKL_TKN_U32_PIPE_ID,
+       SKL_TKN_U32_PIPE_CONN_TYPE,
+       SKL_TKN_U32_PIPE_PRIORITY,
+       SKL_TKN_U32_PIPE_MEM_PGS,
+       SKL_TKN_U32_DIR_PIN_COUNT,
+       SKL_TKN_U32_FMT_CH,
+       SKL_TKN_U32_FMT_FREQ,
+       SKL_TKN_U32_FMT_BIT_DEPTH,
+       SKL_TKN_U32_FMT_SAMPLE_SIZE,
+       SKL_TKN_U32_FMT_CH_CONFIG,
+       SKL_TKN_U32_FMT_INTERLEAVE,
+       SKL_TKN_U32_FMT_SAMPLE_TYPE,
+       SKL_TKN_U32_FMT_CH_MAP,
+       SKL_TKN_U32_PIN_MOD_ID,
+       SKL_TKN_U32_PIN_INST_ID,
+       SKL_TKN_U32_MOD_SET_PARAMS,
+       SKL_TKN_U32_MOD_PARAM_ID,
+       SKL_TKN_U32_CAPS_SET_PARAMS,
+       SKL_TKN_U32_CAPS_PARAMS_ID,
+       SKL_TKN_U32_CAPS_SIZE,
+       SKL_TKN_U32_PROC_DOMAIN,
+       SKL_TKN_U32_LIB_COUNT,
+       SKL_TKN_STR_LIB_NAME,
+       SKL_TKN_MAX = SKL_TKN_STR_LIB_NAME,
+};
+
+#endif
index 860f8ca..2614691 100644 (file)
  */
 #define HDAC_MAX_CAPS 10
 
-/**
- * snd_hdac_ext_bus_parse_capabilities - parse capablity structure
- * @ebus: the pointer to extended bus object
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus)
-{
-       unsigned int cur_cap;
-       unsigned int offset;
-       struct hdac_bus *bus = &ebus->bus;
-       unsigned int counter = 0;
-
-       offset = snd_hdac_chip_readl(bus, LLCH);
-
-       /* Lets walk the linked capabilities list */
-       do {
-               cur_cap = _snd_hdac_chip_read(l, bus, offset);
-
-               dev_dbg(bus->dev, "Capability version: 0x%x\n",
-                               ((cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF));
-
-               dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
-                               (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
-
-               switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
-               case AZX_ML_CAP_ID:
-                       dev_dbg(bus->dev, "Found ML capability\n");
-                       ebus->mlcap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_GTS_CAP_ID:
-                       dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
-                       ebus->gtscap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_PP_CAP_ID:
-                       /* PP capability found, the Audio DSP is present */
-                       dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
-                       ebus->ppcap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_SPB_CAP_ID:
-                       /* SPIB capability found, handler function */
-                       dev_dbg(bus->dev, "Found SPB capability\n");
-                       ebus->spbcap = bus->remap_addr + offset;
-                       break;
-
-               case AZX_DRSM_CAP_ID:
-                       /* DMA resume  capability found, handler function */
-                       dev_dbg(bus->dev, "Found DRSM capability\n");
-                       ebus->drsmcap = bus->remap_addr + offset;
-                       break;
-
-               default:
-                       dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
-                       break;
-               }
-
-               counter++;
-
-               if (counter > HDAC_MAX_CAPS) {
-                       dev_err(bus->dev, "We exceeded HDAC Ext capablities!!!\n");
-                       break;
-               }
-
-               /* read the offset of next capabiity */
-               offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
-
-       } while (offset);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_parse_capabilities);
-
 /*
  * processing pipe helpers - these helpers are useful for dealing with HDA
  * new capability of processing pipelines
@@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
 {
        struct hdac_bus *bus = &ebus->bus;
 
-       if (!ebus->ppcap) {
+       if (!bus->ppcap) {
                dev_err(bus->dev, "Address of PP capability is NULL");
                return;
        }
 
        if (enable)
-               snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
        else
-               snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
 
@@ -139,15 +64,15 @@ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable)
 {
        struct hdac_bus *bus = &ebus->bus;
 
-       if (!ebus->ppcap) {
+       if (!bus->ppcap) {
                dev_err(bus->dev, "Address of PP capability is NULL\n");
                return;
        }
 
        if (enable)
-               snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
        else
-               snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
 
@@ -171,7 +96,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
        struct hdac_ext_link *hlink;
        struct hdac_bus *bus = &ebus->bus;
 
-       link_count = readl(ebus->mlcap + AZX_REG_ML_MLCD) + 1;
+       link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
 
        dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
 
@@ -181,7 +106,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
                        return -ENOMEM;
                hlink->index = idx;
                hlink->bus = bus;
-               hlink->ml_addr = ebus->mlcap + AZX_ML_BASE +
+               hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
                                        (AZX_ML_INTERVAL * idx);
                hlink->lcaps  = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
                hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
index 626f3bb..2441273 100644 (file)
@@ -40,27 +40,27 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
 {
        struct hdac_bus *bus = &ebus->bus;
 
-       if (ebus->ppcap) {
-               stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE +
+       if (bus->ppcap) {
+               stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
                                AZX_PPHC_INTERVAL * idx;
 
-               stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE +
+               stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
                                AZX_PPLC_MULTI * ebus->num_streams +
                                AZX_PPLC_INTERVAL * idx;
        }
 
-       if (ebus->spbcap) {
-               stream->spib_addr = ebus->spbcap + AZX_SPB_BASE +
+       if (bus->spbcap) {
+               stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
                                        AZX_SPB_INTERVAL * idx +
                                        AZX_SPB_SPIB;
 
-               stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE +
+               stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
                                        AZX_SPB_INTERVAL * idx +
                                        AZX_SPB_MAXFIFO;
        }
 
-       if (ebus->drsmcap)
-               stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE +
+       if (bus->drsmcap)
+               stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
                                        AZX_DRSM_INTERVAL * idx;
 
        stream->decoupled = false;
@@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
 
        spin_lock_irq(&bus->reg_lock);
        if (decouple)
-               snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0,
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0,
                                AZX_PPCTL_PROCEN(hstream->index));
        else
-               snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL,
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
                                        AZX_PPCTL_PROCEN(hstream->index), 0);
        stream->decoupled = decouple;
        spin_unlock_irq(&bus->reg_lock);
@@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
        struct hdac_stream *stream = NULL;
        struct hdac_bus *hbus = &ebus->bus;
 
-       if (!ebus->ppcap) {
+       if (!hbus->ppcap) {
                dev_err(hbus->dev, "stream type not supported\n");
                return NULL;
        }
@@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
        struct hdac_stream *stream = NULL;
        struct hdac_bus *hbus = &ebus->bus;
 
-       if (!ebus->ppcap) {
+       if (!hbus->ppcap) {
                dev_err(hbus->dev, "stream type not supported\n");
                return NULL;
        }
@@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
        u32 register_mask = 0;
        struct hdac_bus *bus = &ebus->bus;
 
-       if (!ebus->spbcap) {
+       if (!bus->spbcap) {
                dev_err(bus->dev, "Address of SPB capability is NULL");
                return;
        }
 
        mask |= (1 << index);
 
-       register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL);
+       register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL);
 
        mask |= register_mask;
 
        if (enable)
-               snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
+               snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
        else
-               snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
+               snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
 
@@ -452,7 +452,7 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
 {
        struct hdac_bus *bus = &ebus->bus;
 
-       if (!ebus->spbcap) {
+       if (!bus->spbcap) {
                dev_err(bus->dev, "Address of SPB capability is NULL");
                return -EINVAL;
        }
@@ -475,7 +475,7 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
 {
        struct hdac_bus *bus = &ebus->bus;
 
-       if (!ebus->spbcap) {
+       if (!bus->spbcap) {
                dev_err(bus->dev, "Address of SPB capability is NULL");
                return -EINVAL;
        }
@@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
        u32 register_mask = 0;
        struct hdac_bus *bus = &ebus->bus;
 
-       if (!ebus->drsmcap) {
+       if (!bus->drsmcap) {
                dev_err(bus->dev, "Address of DRSM capability is NULL");
                return;
        }
 
        mask |= (1 << index);
 
-       register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL);
+       register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL);
 
        mask |= register_mask;
 
        if (enable)
-               snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
+               snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
        else
-               snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
+               snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
 
@@ -544,7 +544,7 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
 {
        struct hdac_bus *bus = &ebus->bus;
 
-       if (!ebus->drsmcap) {
+       if (!bus->drsmcap) {
                dev_err(bus->dev, "Address of DRSM capability is NULL");
                return -EINVAL;
        }
index 9fee464..0430658 100644 (file)
@@ -255,6 +255,81 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
 }
 EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
 
+#define HDAC_MAX_CAPS 10
+/**
+ * snd_hdac_bus_parse_capabilities - parse capability structure
+ * @bus: the pointer to bus object
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
+{
+       unsigned int cur_cap;
+       unsigned int offset;
+       unsigned int counter = 0;
+
+       offset = snd_hdac_chip_readl(bus, LLCH);
+
+       /* Lets walk the linked capabilities list */
+       do {
+               cur_cap = _snd_hdac_chip_read(l, bus, offset);
+
+               dev_dbg(bus->dev, "Capability version: 0x%x\n",
+                       (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
+
+               dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
+                       (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
+
+               switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
+               case AZX_ML_CAP_ID:
+                       dev_dbg(bus->dev, "Found ML capability\n");
+                       bus->mlcap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_GTS_CAP_ID:
+                       dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
+                       bus->gtscap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_PP_CAP_ID:
+                       /* PP capability found, the Audio DSP is present */
+                       dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
+                       bus->ppcap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_SPB_CAP_ID:
+                       /* SPIB capability found, handler function */
+                       dev_dbg(bus->dev, "Found SPB capability\n");
+                       bus->spbcap = bus->remap_addr + offset;
+                       break;
+
+               case AZX_DRSM_CAP_ID:
+                       /* DMA resume  capability found, handler function */
+                       dev_dbg(bus->dev, "Found DRSM capability\n");
+                       bus->drsmcap = bus->remap_addr + offset;
+                       break;
+
+               default:
+                       dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
+                       break;
+               }
+
+               counter++;
+
+               if (counter > HDAC_MAX_CAPS) {
+                       dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
+                       break;
+               }
+
+               /* read the offset of next capability */
+               offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
+
+       } while (offset);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities);
+
 /*
  * Lowlevel interface
  */
index 27de801..2ad3b44 100644 (file)
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
+
+#ifdef CONFIG_X86
+/* for art-tsc conversion */
+#include <asm/tsc.h>
+#endif
+
 #include <sound/core.h>
 #include <sound/initval.h>
 #include "hda_controller.h"
@@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
                               azx_get_position(chip, azx_dev));
 }
 
+/*
+ * azx_scale64: Scale base by mult/div while not overflowing sanely
+ *
+ * Derived from scale64_check_overflow in kernel/time/timekeeping.c
+ *
+ * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which
+ * is about 384307 ie ~4.5 days.
+ *
+ * This scales the calculation so that overflow will happen but after 2^64 /
+ * 48000 secs, which is pretty large!
+ *
+ * In caln below:
+ *     base may overflow, but since there isn’t any additional division
+ *     performed on base it’s OK
+ *     rem can’t overflow because both are 32-bit values
+ */
+
+#ifdef CONFIG_X86
+static u64 azx_scale64(u64 base, u32 num, u32 den)
+{
+       u64 rem;
+
+       rem = do_div(base, den);
+
+       base *= num;
+       rem *= num;
+
+       do_div(rem, den);
+
+       return base + rem;
+}
+
+static int azx_get_sync_time(ktime_t *device,
+               struct system_counterval_t *system, void *ctx)
+{
+       struct snd_pcm_substream *substream = ctx;
+       struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       struct azx *chip = apcm->chip;
+       struct snd_pcm_runtime *runtime;
+       u64 ll_counter, ll_counter_l, ll_counter_h;
+       u64 tsc_counter, tsc_counter_l, tsc_counter_h;
+       u32 wallclk_ctr, wallclk_cycles;
+       bool direction;
+       u32 dma_select;
+       u32 timeout = 200;
+       u32 retry_count = 0;
+
+       runtime = substream->runtime;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               direction = 1;
+       else
+               direction = 0;
+
+       /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */
+       do {
+               timeout = 100;
+               dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) |
+                                       (azx_dev->core.stream_tag - 1);
+               snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select);
+
+               /* Enable the capture */
+               snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK);
+
+               while (timeout) {
+                       if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) &
+                                               GTSCC_TSCCD_MASK)
+                               break;
+
+                       timeout--;
+               }
+
+               if (!timeout) {
+                       dev_err(chip->card->dev, "GTSCC capture Timedout!\n");
+                       return -EIO;
+               }
+
+               /* Read wall clock counter */
+               wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC);
+
+               /* Read TSC counter */
+               tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL);
+               tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU);
+
+               /* Read Link counter */
+               ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL);
+               ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU);
+
+               /* Ack: registers read done */
+               snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT);
+
+               tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) |
+                                               tsc_counter_l;
+
+               ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l;
+               wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK;
+
+               /*
+                * An error occurs near frame "rollover". The clocks in
+                * frame value indicates whether this error may have
+                * occurred. Here we use the value of 10 i.e.,
+                * HDA_MAX_CYCLE_OFFSET
+                */
+               if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET
+                                       && wallclk_cycles > HDA_MAX_CYCLE_OFFSET)
+                       break;
+
+               /*
+                * Sleep before we read again, else we may again get
+                * value near to MAX_CYCLE. Try to sleep for different
+                * amount of time so we dont hit the same number again
+                */
+               udelay(retry_count++);
+
+       } while (retry_count != HDA_MAX_CYCLE_READ_RETRY);
+
+       if (retry_count == HDA_MAX_CYCLE_READ_RETRY) {
+               dev_err_ratelimited(chip->card->dev,
+                       "Error in WALFCC cycle count\n");
+               return -EIO;
+       }
+
+       *device = ns_to_ktime(azx_scale64(ll_counter,
+                               NSEC_PER_SEC, runtime->rate));
+       *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
+                              ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
+
+       *system = convert_art_to_tsc(tsc_counter);
+
+       return 0;
+}
+
+#else
+static int azx_get_sync_time(ktime_t *device,
+               struct system_counterval_t *system, void *ctx)
+{
+       return -ENXIO;
+}
+#endif
+
+static int azx_get_crosststamp(struct snd_pcm_substream *substream,
+                             struct system_device_crosststamp *xtstamp)
+{
+       return get_device_system_crosststamp(azx_get_sync_time,
+                                       substream, NULL, xtstamp);
+}
+
+static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
+                               struct snd_pcm_audio_tstamp_config *ts)
+{
+       if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME)
+               if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)
+                       return true;
+
+       return false;
+}
+
 static int azx_get_time_info(struct snd_pcm_substream *substream,
                        struct timespec *system_ts, struct timespec *audio_ts,
                        struct snd_pcm_audio_tstamp_config *audio_tstamp_config,
                        struct snd_pcm_audio_tstamp_report *audio_tstamp_report)
 {
        struct azx_dev *azx_dev = get_azx_dev(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct system_device_crosststamp xtstamp;
+       int ret;
        u64 nsec;
 
        if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
@@ -361,8 +528,37 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
                audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
                audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
 
-       } else
+       } else if (is_link_time_supported(runtime, audio_tstamp_config)) {
+
+               ret = azx_get_crosststamp(substream, &xtstamp);
+               if (ret)
+                       return ret;
+
+               switch (runtime->tstamp_type) {
+               case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
+                       return -EINVAL;
+
+               case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
+                       *system_ts = ktime_to_timespec(xtstamp.sys_monoraw);
+                       break;
+
+               default:
+                       *system_ts = ktime_to_timespec(xtstamp.sys_realtime);
+                       break;
+
+               }
+
+               *audio_ts = ktime_to_timespec(xtstamp.device);
+
+               audio_tstamp_report->actual_type =
+                       SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
+               audio_tstamp_report->accuracy_report = 1;
+               /* 24 MHz WallClock == 42ns resolution */
+               audio_tstamp_report->accuracy = 42;
+
+       } else {
                audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+       }
 
        return 0;
 }
@@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
                goto unlock;
        }
        runtime->private_data = azx_dev;
+
+       if (chip->gts_present)
+               azx_pcm_hw.info = azx_pcm_hw.info |
+                       SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
+
        runtime->hw = azx_pcm_hw;
        runtime->hw.channels_min = hinfo->channels_min;
        runtime->hw.channels_max = hinfo->channels_max;
index ec63bbf..a50e053 100644 (file)
@@ -159,6 +159,9 @@ struct azx {
        unsigned int region_requested:1;
        unsigned int disabled:1; /* disabled by vga_switcheroo */
 
+       /* GTS present */
+       unsigned int gts_present:1;
+
 #ifdef CONFIG_SND_HDA_DSP_LOADER
        struct azx_dev saved_azx_dev;
 #endif
index 160c7f7..c3469f7 100644 (file)
@@ -54,6 +54,7 @@
 /* for snoop control */
 #include <asm/pgtable.h>
 #include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
 #endif
 #include <sound/core.h>
 #include <sound/initval.h>
@@ -1663,6 +1664,22 @@ static int azx_first_init(struct azx *chip)
                return -ENXIO;
        }
 
+       if (IS_SKL_PLUS(pci))
+               snd_hdac_bus_parse_capabilities(bus);
+
+       /*
+        * Some Intel CPUs has always running timer (ART) feature and
+        * controller may have Global time sync reporting capability, so
+        * check both of these before declaring synchronized time reporting
+        * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME
+        */
+       chip->gts_present = false;
+
+#ifdef CONFIG_X86
+       if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART))
+               chip->gts_present = true;
+#endif
+
        if (chip->msi) {
                if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
                        dev_dbg(card->dev, "Disabling 64bit MSI\n");
index 09e8988..b0f6f07 100644 (file)
@@ -1870,6 +1870,9 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
        case RT5640_SCLK_S_PLL1:
                reg_val |= RT5640_SCLK_SRC_PLL1;
                break;
+       case RT5640_SCLK_S_RCCLK:
+               reg_val |= RT5640_SCLK_SRC_RCCLK;
+               break;
        default:
                dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
                return -EINVAL;
index 58b664b..90c8871 100644 (file)
 #define RT5640_SCLK_SRC_SFT                    14
 #define RT5640_SCLK_SRC_MCLK                   (0x0 << 14)
 #define RT5640_SCLK_SRC_PLL1                   (0x1 << 14)
+#define RT5640_SCLK_SRC_RCCLK                  (0x2 << 14)
 #define RT5640_PLL1_SRC_MASK                   (0x3 << 12)
 #define RT5640_PLL1_SRC_SFT                    12
 #define RT5640_PLL1_SRC_MCLK                   (0x0 << 12)
index a20c3df..26eb5a0 100644 (file)
@@ -25,6 +25,7 @@ config SND_SST_IPC_ACPI
        tristate
        select SND_SST_IPC
        select SND_SOC_INTEL_SST
+       select IOSF_MBI
 
 config SND_SOC_INTEL_SST
        tristate
@@ -120,6 +121,17 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
          This adds audio driver for Intel Baytrail platform based boards
          with the MAX98090 audio codec.
 
+config SND_SOC_INTEL_BDW_RT5677_MACH
+       tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec"
+       depends on X86_INTEL_LPSS && GPIOLIB && I2C && DW_DMAC
+       depends on DW_DMAC_CORE=y
+       select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_HASWELL
+       select SND_SOC_RT5677
+       help
+         This adds support for Intel Broadwell platform based boards with
+         the RT5677 audio codec.
+
 config SND_SOC_INTEL_BROADWELL_MACH
        tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
        depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
index 98720a9..0838478 100644 (file)
@@ -1,4 +1,4 @@
-/*
+ /*
  *  sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
  *
  *  Copyright (C) 2013-14 Intel Corp
@@ -534,6 +534,7 @@ static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10,
 
 /* Look up table to convert MIXER SW bit regs to SWM inputs */
 static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+       [SST_IP_MODEM]          = SST_SWM_IN_MODEM,
        [SST_IP_CODEC0]         = SST_SWM_IN_CODEC0,
        [SST_IP_CODEC1]         = SST_SWM_IN_CODEC1,
        [SST_IP_LOOP0]          = SST_SWM_IN_SPROT_LOOP,
@@ -674,6 +675,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
 /* SBA mixers - 16 inputs */
 #define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name)                                                        \
        static const struct snd_kcontrol_new kctl_name[] = {                                    \
+               SOC_DAPM_SINGLE("modem_in Switch", SND_SOC_NOPM, SST_IP_MODEM, 1, 0),           \
                SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0),         \
                SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0),         \
                SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0),      \
@@ -684,6 +686,7 @@ static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
        }
 
 #define SST_SBA_MIXER_GRAPH_MAP(mix_name)                      \
+       { mix_name, "modem_in Switch",  "modem_in" },           \
        { mix_name, "codec_in0 Switch", "codec_in0" },          \
        { mix_name, "codec_in1 Switch", "codec_in1" },          \
        { mix_name, "sprot_loop_in Switch",     "sprot_loop_in" },      \
@@ -713,6 +716,7 @@ SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
 SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
 SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
 SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls);
 
 /*
  * sst_handle_vb_timer - Start/Stop the DSP scheduler
@@ -931,17 +935,26 @@ void sst_fill_ssp_defaults(struct snd_soc_dai *dai)
 int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
 {
        struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
-       const struct sst_ssp_config *config;
+       int ssp_id;
 
        dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
 
+       if (strcmp(id, "ssp0-port") == 0)
+               ssp_id = SSP_MODEM;
+       else if (strcmp(id, "ssp2-port") == 0)
+               ssp_id = SSP_CODEC;
+       else {
+               dev_dbg(dai->dev, "port %s is not supported\n", id);
+               return -1;
+       }
+
        SST_FILL_DEFAULT_DESTINATION(drv->ssp_cmd.header.dst);
        drv->ssp_cmd.header.command_id = SBA_HW_SET_SSP;
        drv->ssp_cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
                                - sizeof(struct sst_dsp_header);
 
-       config = &sst_ssp_configs;
-       dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
+       drv->ssp_cmd.selection = ssp_id;
+       dev_dbg(dai->dev, "ssp_id: %u\n", ssp_id);
 
        if (enable)
                drv->ssp_cmd.switch_state = SST_SWITCH_ON;
@@ -1047,8 +1060,10 @@ static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
 }
 
 static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+       SST_AIF_IN("modem_in", sst_set_be_modules),
        SST_AIF_IN("codec_in0", sst_set_be_modules),
        SST_AIF_IN("codec_in1", sst_set_be_modules),
+       SST_AIF_OUT("modem_out", sst_set_be_modules),
        SST_AIF_OUT("codec_out0", sst_set_be_modules),
        SST_AIF_OUT("codec_out1", sst_set_be_modules),
 
@@ -1103,6 +1118,9 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
                      sst_mix_codec0_controls, sst_swm_mixer_event),
        SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
                      sst_mix_codec1_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("modem_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MODEM,
+                     sst_mix_modem_controls, sst_swm_mixer_event),
+
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
@@ -1148,6 +1166,9 @@ static const struct snd_soc_dapm_route intercon[] = {
        SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
        {"codec_out1", NULL, "codec_out1 mix 0"},
        SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+       {"modem_out", NULL, "modem_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"),
+
 
 };
 static const char * const slot_names[] = {
@@ -1217,6 +1238,9 @@ static const struct snd_kcontrol_new sst_gain_controls[] = {
        SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
        SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
        SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+       SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[16]),
+       SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[17]),
+
 };
 
 #define SST_GAIN_NUM_CONTROLS 3
index e011311..351d814 100644 (file)
@@ -35,6 +35,8 @@ enum {
 /* define a bit for each mixer input */
 #define SST_MIX_IP(x)          (x)
 
+#define SST_IP_MODEM           SST_MIX_IP(0)
+#define SST_IP_BT              SST_MIX_IP(1)
 #define SST_IP_CODEC0          SST_MIX_IP(2)
 #define SST_IP_CODEC1          SST_MIX_IP(3)
 #define SST_IP_LOOP0           SST_MIX_IP(4)
@@ -63,6 +65,7 @@ enum {
  * Audio DSP Path Ids. Specified by the audio DSP FW
  */
 enum sst_path_index {
+       SST_PATH_INDEX_MODEM_OUT                = (0x00 << SST_PATH_ID_SHIFT),
        SST_PATH_INDEX_CODEC_OUT0               = (0x02 << SST_PATH_ID_SHIFT),
        SST_PATH_INDEX_CODEC_OUT1               = (0x03 << SST_PATH_ID_SHIFT),
 
@@ -80,6 +83,7 @@ enum sst_path_index {
 
 
        /* Start of input paths */
+       SST_PATH_INDEX_MODEM_IN                 = (0x80 << SST_PATH_ID_SHIFT),
        SST_PATH_INDEX_CODEC_IN0                = (0x82 << SST_PATH_ID_SHIFT),
        SST_PATH_INDEX_CODEC_IN1                = (0x83 << SST_PATH_ID_SHIFT),
 
@@ -105,6 +109,7 @@ enum sst_path_index {
  * path IDs
  */
 enum sst_swm_inputs {
+       SST_SWM_IN_MODEM        = (SST_PATH_INDEX_MODEM_IN        | SST_DEFAULT_CELL_NBR),
        SST_SWM_IN_CODEC0       = (SST_PATH_INDEX_CODEC_IN0       | SST_DEFAULT_CELL_NBR),
        SST_SWM_IN_CODEC1       = (SST_PATH_INDEX_CODEC_IN1       | SST_DEFAULT_CELL_NBR),
        SST_SWM_IN_SPROT_LOOP   = (SST_PATH_INDEX_SPROT_LOOP_IN   | SST_DEFAULT_CELL_NBR),
@@ -124,6 +129,7 @@ enum sst_swm_inputs {
  * path IDs
  */
 enum sst_swm_outputs {
+       SST_SWM_OUT_MODEM       = (SST_PATH_INDEX_MODEM_OUT       | SST_DEFAULT_CELL_NBR),
        SST_SWM_OUT_CODEC0      = (SST_PATH_INDEX_CODEC_OUT0      | SST_DEFAULT_CELL_NBR),
        SST_SWM_OUT_CODEC1      = (SST_PATH_INDEX_CODEC_OUT1      | SST_DEFAULT_CELL_NBR),
        SST_SWM_OUT_SPROT_LOOP  = (SST_PATH_INDEX_SPROT_LOOP_OUT  | SST_DEFAULT_CELL_NBR),
index a4b458e..9b6e273 100644 (file)
@@ -190,7 +190,8 @@ int sst_driver_ops(struct intel_sst_drv *sst)
 
        default:
                dev_err(sst->dev,
-                       "SST Driver capablities missing for dev_id: %x", sst->dev_id);
+                       "SST Driver capabilities missing for dev_id: %x",
+                       sst->dev_id);
                return -EINVAL;
        };
 }
@@ -441,7 +442,7 @@ static int intel_sst_suspend(struct device *dev)
                struct stream_info *stream = &ctx->streams[i];
 
                if (stream->status == STREAM_RUNNING) {
-                       dev_err(dev, "stream %d is running, cant susupend, abort\n", i);
+                       dev_err(dev, "stream %d is running, can't suspend, abort\n", i);
                        return -EBUSY;
                }
        }
index 4d31849..ba5c0d7 100644 (file)
@@ -39,6 +39,8 @@
 #include <acpi/platform/aclinux.h>
 #include <acpi/actypes.h>
 #include <acpi/acpi_bus.h>
+#include <asm/cpu_device_id.h>
+#include <asm/iosf_mbi.h>
 #include "../sst-mfld-platform.h"
 #include "../../common/sst-dsp.h"
 #include "../../common/sst-acpi.h"
@@ -113,6 +115,28 @@ static const struct sst_res_info byt_rvp_res_info = {
        .acpi_ipc_irq_index = 5,
 };
 
+/* BYTCR has different BIOS from BYT */
+static const struct sst_res_info bytcr_res_info = {
+       .shim_offset = 0x140000,
+       .shim_size = 0x000100,
+       .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+       .ssp0_offset = 0xa0000,
+       .ssp0_size = 0x1000,
+       .dma0_offset = 0x98000,
+       .dma0_size = 0x4000,
+       .dma1_offset = 0x9c000,
+       .dma1_size = 0x4000,
+       .iram_offset = 0x0c0000,
+       .iram_size = 0x14000,
+       .dram_offset = 0x100000,
+       .dram_size = 0x28000,
+       .mbox_offset = 0x144000,
+       .mbox_size = 0x1000,
+       .acpi_lpe_res_index = 0,
+       .acpi_ddr_index = 2,
+       .acpi_ipc_irq_index = 0
+};
+
 static struct sst_platform_info byt_rvp_platform_data = {
        .probe_data = &byt_fwparse_info,
        .ipc_info = &byt_ipc_info,
@@ -142,7 +166,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
                                        ctx->pdata->res_info->acpi_lpe_res_index);
        if (!rsrc) {
-               dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+               dev_err(ctx->dev, "Invalid SHIM base from IFWI\n");
                return -EIO;
        }
        dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
@@ -154,7 +178,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
                                         ctx->pdata->res_info->iram_size);
        if (!ctx->iram) {
-               dev_err(ctx->dev, "unable to map IRAM");
+               dev_err(ctx->dev, "unable to map IRAM\n");
                return -EIO;
        }
 
@@ -164,7 +188,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
                                         ctx->pdata->res_info->dram_size);
        if (!ctx->dram) {
-               dev_err(ctx->dev, "unable to map DRAM");
+               dev_err(ctx->dev, "unable to map DRAM\n");
                return -EIO;
        }
 
@@ -173,7 +197,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
                                        ctx->pdata->res_info->shim_size);
        if (!ctx->shim) {
-               dev_err(ctx->dev, "unable to map SHIM");
+               dev_err(ctx->dev, "unable to map SHIM\n");
                return -EIO;
        }
 
@@ -186,7 +210,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
                                            ctx->pdata->res_info->mbox_size);
        if (!ctx->mailbox) {
-               dev_err(ctx->dev, "unable to map mailbox");
+               dev_err(ctx->dev, "unable to map mailbox\n");
                return -EIO;
        }
 
@@ -196,7 +220,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
                                        ctx->pdata->res_info->acpi_ddr_index);
        if (!rsrc) {
-               dev_err(ctx->dev, "Invalid DDR base from IFWI");
+               dev_err(ctx->dev, "Invalid DDR base from IFWI\n");
                return -EIO;
        }
        ctx->ddr_base = rsrc->start;
@@ -205,7 +229,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
                                        resource_size(rsrc));
        if (!ctx->ddr) {
-               dev_err(ctx->dev, "unable to map DDR");
+               dev_err(ctx->dev, "unable to map DDR\n");
                return -EIO;
        }
 
@@ -215,6 +239,46 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
        return 0;
 }
 
+
+static int is_byt_cr(struct device *dev, bool *bytcr)
+{
+       int status = 0;
+
+       if (IS_ENABLED(CONFIG_IOSF_MBI)) {
+               static const struct x86_cpu_id cpu_ids[] = {
+                       { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
+                       {}
+               };
+               u32 bios_status;
+
+               if (!x86_match_cpu(cpu_ids) || !iosf_mbi_available()) {
+                       /* bail silently */
+                       return status;
+               }
+
+               status = iosf_mbi_read(BT_MBI_UNIT_PMC, /* 0x04 PUNIT */
+                                      MBI_REG_READ, /* 0x10 */
+                                      0x006, /* BIOS_CONFIG */
+                                      &bios_status);
+
+               if (status) {
+                       dev_err(dev, "could not read PUNIT BIOS_CONFIG\n");
+               } else {
+                       /* bits 26:27 mirror PMIC options */
+                       bios_status = (bios_status >> 26) & 3;
+
+                       if ((bios_status == 1) || (bios_status == 3))
+                               *bytcr = true;
+                       else
+                               dev_info(dev, "BYT-CR not detected\n");
+               }
+       } else {
+               dev_info(dev, "IOSF_MBI not enabled, no BYT-CR detection\n");
+       }
+       return status;
+}
+
+
 static int sst_acpi_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -226,11 +290,12 @@ static int sst_acpi_probe(struct platform_device *pdev)
        struct platform_device *plat_dev;
        struct sst_platform_info *pdata;
        unsigned int dev_id;
+       bool bytcr = false;
 
        id = acpi_match_device(dev->driver->acpi_match_table, dev);
        if (!id)
                return -ENODEV;
-       dev_dbg(dev, "for %s", id->id);
+       dev_dbg(dev, "for %s\n", id->id);
 
        mach = (struct sst_acpi_mach *)id->driver_data;
        mach = sst_acpi_find_machine(mach);
@@ -251,6 +316,18 @@ static int sst_acpi_probe(struct platform_device *pdev)
 
        dev_dbg(dev, "ACPI device id: %x\n", dev_id);
 
+       ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+       if (ret < 0)
+               return ret;
+
+       ret = is_byt_cr(dev, &bytcr);
+       if (!((ret < 0) || (bytcr == false))) {
+               dev_info(dev, "Detected Baytrail-CR platform\n");
+
+               /* override resource info */
+               byt_rvp_platform_data.res_info = &bytcr_res_info;
+       }
+
        plat_dev = platform_device_register_data(dev, pdata->platform, -1,
                                                NULL, 0);
        if (IS_ERR(plat_dev)) {
@@ -271,10 +348,6 @@ static int sst_acpi_probe(struct platform_device *pdev)
                return PTR_ERR(mdev);
        }
 
-       ret = sst_alloc_drv_context(&ctx, dev, dev_id);
-       if (ret < 0)
-               return ret;
-
        /* Fill sst platform data */
        ctx->pdata = pdata;
        strcpy(ctx->firmware_name, mach->fw_filename);
index dac03a0..5639f10 100644 (file)
@@ -1,6 +1,7 @@
 snd-soc-sst-haswell-objs := haswell.o
 snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
 snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
+snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o
 snd-soc-sst-broadwell-objs := broadwell.o
 snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
 snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o
 obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
 obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
 obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
new file mode 100644 (file)
index 0000000..547e670
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * ASoC machine driver for Intel Broadwell platforms with RT5677 codec
+ *
+ * Copyright (c) 2014, The Chromium OS Authors.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+
+#include "../common/sst-dsp.h"
+#include "../haswell/sst-haswell-ipc.h"
+
+#include "../../codecs/rt5677.h"
+
+struct bdw_rt5677_priv {
+       struct gpio_desc *gpio_hp_en;
+       struct snd_soc_codec *codec;
+};
+
+static int bdw_rt5677_event_hp(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_soc_card *card = dapm->card;
+       struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card);
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               msleep(70);
+
+       gpiod_set_value_cansleep(bdw_rt5677->gpio_hp_en,
+               SND_SOC_DAPM_EVENT_ON(event));
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget bdw_rt5677_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", bdw_rt5677_event_hp),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Local DMICs", NULL),
+       SND_SOC_DAPM_MIC("Remote DMICs", NULL),
+};
+
+static const struct snd_soc_dapm_route bdw_rt5677_map[] = {
+       /* Speakers */
+       {"Speaker", NULL, "PDM1L"},
+       {"Speaker", NULL, "PDM1R"},
+
+       /* Headset jack connectors */
+       {"Headphone", NULL, "LOUT1"},
+       {"Headphone", NULL, "LOUT2"},
+       {"IN1P", NULL, "Headset Mic"},
+       {"IN1N", NULL, "Headset Mic"},
+
+       /* Digital MICs
+        * Local DMICs: the two DMICs on the mainboard
+        * Remote DMICs: the two DMICs on the camera module
+        */
+       {"DMIC L1", NULL, "Remote DMICs"},
+       {"DMIC R1", NULL, "Remote DMICs"},
+       {"DMIC L2", NULL, "Local DMICs"},
+       {"DMIC R2", NULL, "Local DMICs"},
+
+       /* CODEC BE connections */
+       {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+       {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static const struct snd_kcontrol_new bdw_rt5677_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Local DMICs"),
+       SOC_DAPM_PIN_SWITCH("Remote DMICs"),
+};
+
+
+static struct snd_soc_jack headphone_jack;
+static struct snd_soc_jack mic_jack;
+
+static struct snd_soc_jack_pin headphone_jack_pin = {
+       .pin    = "Headphone",
+       .mask   = SND_JACK_HEADPHONE,
+};
+
+static struct snd_soc_jack_pin mic_jack_pin = {
+       .pin    = "Headset Mic",
+       .mask   = SND_JACK_MICROPHONE,
+};
+
+static struct snd_soc_jack_gpio headphone_jack_gpio = {
+       .name                   = "plug-det",
+       .report                 = SND_JACK_HEADPHONE,
+       .debounce_time          = 200,
+};
+
+static struct snd_soc_jack_gpio mic_jack_gpio = {
+       .name                   = "mic-present",
+       .report                 = SND_JACK_MICROPHONE,
+       .debounce_time          = 200,
+       .invert                 = 1,
+};
+
+static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+                       struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       /* The ADSP will covert the FE rate to 48k, stereo */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP0 to 16 bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S16_LE);
+       return 0;
+}
+
+static int bdw_rt5677_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, 24576000,
+               SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+               return ret;
+       }
+
+       return ret;
+}
+
+static struct snd_soc_ops bdw_rt5677_ops = {
+       .hw_params = bdw_rt5677_hw_params,
+};
+
+static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
+       struct sst_hsw *broadwell = pdata->dsp;
+       int ret;
+
+       /* Set ADSP SSP port settings */
+       ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
+               SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+               SST_HSW_DEVICE_CLOCK_MASTER, 9);
+       if (ret < 0) {
+               dev_err(rtd->dev, "error: failed to set device config\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct bdw_rt5677_priv *bdw_rt5677 =
+                       snd_soc_card_get_drvdata(rtd->card);
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+       /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
+        * The ASRC clock source is clk_i2s1_asrc.
+        */
+       rt5677_sel_asrc_clk_src(codec, RT5677_DA_STEREO_FILTER |
+                       RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE,
+                       RT5677_CLK_SEL_I2S1_ASRC);
+
+       /* Request rt5677 GPIO for headphone amp control */
+       bdw_rt5677->gpio_hp_en = devm_gpiod_get_index(codec->dev,
+                       "headphone-enable", 0, 0);
+       if (IS_ERR(bdw_rt5677->gpio_hp_en)) {
+               dev_err(codec->dev, "Can't find HP_AMP_SHDN_L gpio\n");
+               return PTR_ERR(bdw_rt5677->gpio_hp_en);
+       }
+       gpiod_direction_output(bdw_rt5677->gpio_hp_en, 0);
+
+       /* Create and initialize headphone jack */
+       if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+                       SND_JACK_HEADPHONE, &headphone_jack,
+                       &headphone_jack_pin, 1)) {
+               headphone_jack_gpio.gpiod_dev = codec->dev;
+               if (snd_soc_jack_add_gpios(&headphone_jack, 1,
+                               &headphone_jack_gpio))
+                       dev_err(codec->dev, "Can't add headphone jack gpio\n");
+       } else {
+               dev_err(codec->dev, "Can't create headphone jack\n");
+       }
+
+       /* Create and initialize mic jack */
+       if (!snd_soc_card_jack_new(rtd->card, "Mic Jack",
+                       SND_JACK_MICROPHONE, &mic_jack,
+                       &mic_jack_pin, 1)) {
+               mic_jack_gpio.gpiod_dev = codec->dev;
+               if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio))
+                       dev_err(codec->dev, "Can't add mic jack gpio\n");
+       } else {
+               dev_err(codec->dev, "Can't create mic jack\n");
+       }
+       bdw_rt5677->codec = codec;
+
+       snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+       return 0;
+}
+
+/* broadwell digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link bdw_rt5677_dais[] = {
+       /* Front End DAI links */
+       {
+               .name = "System PCM",
+               .stream_name = "System Playback/Capture",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "haswell-pcm-audio",
+               .dynamic = 1,
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .init = bdw_rt5677_rtd_init,
+               .trigger = {
+                       SND_SOC_DPCM_TRIGGER_POST,
+                       SND_SOC_DPCM_TRIGGER_POST
+               },
+               .dpcm_capture = 1,
+               .dpcm_playback = 1,
+       },
+
+       /* Back End DAI links */
+       {
+               /* SSP0 - Codec */
+               .name = "Codec",
+               .id = 0,
+               .cpu_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "snd-soc-dummy",
+               .no_pcm = 1,
+               .codec_name = "i2c-RT5677CE:00",
+               .codec_dai_name = "rt5677-aif1",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBS_CFS,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = broadwell_ssp0_fixup,
+               .ops = &bdw_rt5677_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .init = bdw_rt5677_init,
+       },
+};
+
+static int bdw_rt5677_suspend_pre(struct snd_soc_card *card)
+{
+       struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card);
+       struct snd_soc_dapm_context *dapm;
+
+       if (bdw_rt5677->codec) {
+               dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec);
+               snd_soc_dapm_disable_pin(dapm, "MICBIAS1");
+       }
+       return 0;
+}
+
+static int bdw_rt5677_resume_post(struct snd_soc_card *card)
+{
+       struct bdw_rt5677_priv *bdw_rt5677 = snd_soc_card_get_drvdata(card);
+       struct snd_soc_dapm_context *dapm;
+
+       if (bdw_rt5677->codec) {
+               dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec);
+               snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+       }
+       return 0;
+}
+
+/* ASoC machine driver for Broadwell DSP + RT5677 */
+static struct snd_soc_card bdw_rt5677_card = {
+       .name = "bdw-rt5677",
+       .owner = THIS_MODULE,
+       .dai_link = bdw_rt5677_dais,
+       .num_links = ARRAY_SIZE(bdw_rt5677_dais),
+       .dapm_widgets = bdw_rt5677_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(bdw_rt5677_widgets),
+       .dapm_routes = bdw_rt5677_map,
+       .num_dapm_routes = ARRAY_SIZE(bdw_rt5677_map),
+       .controls = bdw_rt5677_controls,
+       .num_controls = ARRAY_SIZE(bdw_rt5677_controls),
+       .fully_routed = true,
+       .suspend_pre = bdw_rt5677_suspend_pre,
+       .resume_post = bdw_rt5677_resume_post,
+};
+
+static int bdw_rt5677_probe(struct platform_device *pdev)
+{
+       struct bdw_rt5677_priv *bdw_rt5677;
+
+       bdw_rt5677_card.dev = &pdev->dev;
+
+       /* Allocate driver private struct */
+       bdw_rt5677 = devm_kzalloc(&pdev->dev, sizeof(struct bdw_rt5677_priv),
+               GFP_KERNEL);
+       if (!bdw_rt5677) {
+               dev_err(&pdev->dev, "Can't allocate bdw_rt5677\n");
+               return -ENOMEM;
+       }
+
+       snd_soc_card_set_drvdata(&bdw_rt5677_card, bdw_rt5677);
+
+       return devm_snd_soc_register_card(&pdev->dev, &bdw_rt5677_card);
+}
+
+static struct platform_driver bdw_rt5677_audio = {
+       .probe = bdw_rt5677_probe,
+       .driver = {
+               .name = "bdw-rt5677",
+       },
+};
+
+module_platform_driver(bdw_rt5677_audio)
+
+/* Module information */
+MODULE_AUTHOR("Ben Zhang");
+MODULE_DESCRIPTION("Intel Broadwell RT5677 machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bdw-rt5677");
index 3774b11..6532b8f 100644 (file)
@@ -37,6 +37,7 @@ enum {
        BXT_DPCM_AUDIO_PB = 0,
        BXT_DPCM_AUDIO_CP,
        BXT_DPCM_AUDIO_REF_CP,
+       BXT_DPCM_AUDIO_DMIC_CP,
        BXT_DPCM_AUDIO_HDMI1_PB,
        BXT_DPCM_AUDIO_HDMI2_PB,
        BXT_DPCM_AUDIO_HDMI3_PB,
@@ -252,10 +253,56 @@ static struct snd_soc_ops broxton_da7219_ops = {
        .hw_free = broxton_da7219_hw_free,
 };
 
+static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
+                       struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+       channels->min = channels->max = DUAL_CHANNEL;
+
+       return 0;
+}
+
+static int broxton_dmic_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       runtime->hw.channels_max = DUAL_CHANNEL;
+       snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                       &constraints_channels);
+
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+}
+
+static const struct snd_soc_ops broxton_dmic_ops = {
+       .startup = broxton_dmic_startup,
+};
+
+static const unsigned int rates_16000[] = {
+       16000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_16000 = {
+       .count = ARRAY_SIZE(rates_16000),
+       .list  = rates_16000,
+};
+
+static int broxton_refcap_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE,
+                       &constraints_16000);
+};
+
+static struct snd_soc_ops broxton_refcap_ops = {
+       .startup = broxton_refcap_startup,
+};
+
 /* broxton digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link broxton_dais[] = {
        /* Front End DAI links */
-       [BXT_DPCM_AUDIO_PB]
+       [BXT_DPCM_AUDIO_PB] =
        {
                .name = "Bxt Audio Port",
                .stream_name = "Audio",
@@ -271,7 +318,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
                .dpcm_playback = 1,
                .ops = &broxton_da7219_fe_ops,
        },
-       [BXT_DPCM_AUDIO_CP]
+       [BXT_DPCM_AUDIO_CP] =
        {
                .name = "Bxt Audio Capture Port",
                .stream_name = "Audio Record",
@@ -286,7 +333,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
                .dpcm_capture = 1,
                .ops = &broxton_da7219_fe_ops,
        },
-       [BXT_DPCM_AUDIO_REF_CP]
+       [BXT_DPCM_AUDIO_REF_CP] =
        {
                .name = "Bxt Audio Reference cap",
                .stream_name = "Refcap",
@@ -299,8 +346,23 @@ static struct snd_soc_dai_link broxton_dais[] = {
                .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
+               .ops = &broxton_refcap_ops,
+       },
+       [BXT_DPCM_AUDIO_DMIC_CP]
+       {
+               .name = "Bxt Audio DMIC cap",
+               .stream_name = "dmiccap",
+               .cpu_dai_name = "DMIC Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .init = NULL,
+               .dpcm_capture = 1,
+               .nonatomic = 1,
+               .dynamic = 1,
+               .ops = &broxton_dmic_ops,
        },
-       [BXT_DPCM_AUDIO_HDMI1_PB]
+       [BXT_DPCM_AUDIO_HDMI1_PB] =
        {
                .name = "Bxt HDMI Port1",
                .stream_name = "Hdmi1",
@@ -313,7 +375,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
                .nonatomic = 1,
                .dynamic = 1,
        },
-       [BXT_DPCM_AUDIO_HDMI2_PB]
+       [BXT_DPCM_AUDIO_HDMI2_PB] =
        {
                .name = "Bxt HDMI Port2",
                .stream_name = "Hdmi2",
@@ -326,7 +388,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
                .nonatomic = 1,
                .dynamic = 1,
        },
-       [BXT_DPCM_AUDIO_HDMI3_PB]
+       [BXT_DPCM_AUDIO_HDMI3_PB] =
        {
                .name = "Bxt HDMI Port3",
                .stream_name = "Hdmi3",
@@ -382,6 +444,7 @@ static struct snd_soc_dai_link broxton_dais[] = {
                .codec_dai_name = "dmic-hifi",
                .platform_name = "0000:00:0e.0",
                .ignore_suspend = 1,
+               .be_hw_params_fixup = broxton_dmic_fixup,
                .dpcm_capture = 1,
                .no_pcm = 1,
        },
index 253d7bf..d610bdc 100644 (file)
@@ -271,7 +271,7 @@ static const struct snd_soc_ops broxton_rt286_fe_ops = {
 /* broxton digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link broxton_rt298_dais[] = {
        /* Front End DAI links */
-       [BXT_DPCM_AUDIO_PB]
+       [BXT_DPCM_AUDIO_PB] =
        {
                .name = "Bxt Audio Port",
                .stream_name = "Audio",
@@ -286,7 +286,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .dpcm_playback = 1,
                .ops = &broxton_rt286_fe_ops,
        },
-       [BXT_DPCM_AUDIO_CP]
+       [BXT_DPCM_AUDIO_CP] =
        {
                .name = "Bxt Audio Capture Port",
                .stream_name = "Audio Record",
@@ -300,7 +300,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .dpcm_capture = 1,
                .ops = &broxton_rt286_fe_ops,
        },
-       [BXT_DPCM_AUDIO_REF_CP]
+       [BXT_DPCM_AUDIO_REF_CP] =
        {
                .name = "Bxt Audio Reference cap",
                .stream_name = "refcap",
@@ -313,7 +313,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .nonatomic = 1,
                .dynamic = 1,
        },
-       [BXT_DPCM_AUDIO_DMIC_CP]
+       [BXT_DPCM_AUDIO_DMIC_CP] =
        {
                .name = "Bxt Audio DMIC cap",
                .stream_name = "dmiccap",
@@ -327,7 +327,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .dynamic = 1,
                .ops = &broxton_dmic_ops,
        },
-       [BXT_DPCM_AUDIO_HDMI1_PB]
+       [BXT_DPCM_AUDIO_HDMI1_PB] =
        {
                .name = "Bxt HDMI Port1",
                .stream_name = "Hdmi1",
@@ -340,7 +340,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .nonatomic = 1,
                .dynamic = 1,
        },
-       [BXT_DPCM_AUDIO_HDMI2_PB]
+       [BXT_DPCM_AUDIO_HDMI2_PB] =
        {
                .name = "Bxt HDMI Port2",
                .stream_name = "Hdmi2",
@@ -353,7 +353,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .nonatomic = 1,
                .dynamic = 1,
        },
-       [BXT_DPCM_AUDIO_HDMI3_PB]
+       [BXT_DPCM_AUDIO_HDMI3_PB] =
        {
                .name = "Bxt HDMI Port3",
                .stream_name = "Hdmi3",
index 88efb62..bff77a1 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/device.h>
 #include <linux/dmi.h>
 #include <linux/slab.h>
+#include <asm/cpu_device_id.h>
+#include <asm/platform_sst_audio.h>
+#include <linux/clk.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include "../../codecs/rt5640.h"
 #include "../atom/sst-atom-controls.h"
 #include "../common/sst-acpi.h"
+#include "../common/sst-dsp.h"
 
 enum {
        BYT_RT5640_DMIC1_MAP,
        BYT_RT5640_DMIC2_MAP,
        BYT_RT5640_IN1_MAP,
+       BYT_RT5640_IN3_MAP,
 };
 
 #define BYT_RT5640_MAP(quirk)  ((quirk) & 0xff)
 #define BYT_RT5640_DMIC_EN     BIT(16)
+#define BYT_RT5640_MONO_SPEAKER BIT(17)
+#define BYT_RT5640_DIFF_MIC     BIT(18) /* defaut is single-ended */
+#define BYT_RT5640_SSP2_AIF2     BIT(19) /* default is using AIF1  */
+#define BYT_RT5640_SSP0_AIF1     BIT(20)
+#define BYT_RT5640_SSP0_AIF2     BIT(21)
+#define BYT_RT5640_MCLK_EN     BIT(22)
+#define BYT_RT5640_MCLK_25MHZ  BIT(23)
+
+struct byt_rt5640_private {
+       struct clk *mclk;
+};
 
 static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
-                                       BYT_RT5640_DMIC_EN;
+                                       BYT_RT5640_DMIC_EN |
+                                       BYT_RT5640_MCLK_EN;
+
+static void log_quirks(struct device *dev)
+{
+       if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC1_MAP)
+               dev_info(dev, "quirk DMIC1_MAP enabled");
+       if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC2_MAP)
+               dev_info(dev, "quirk DMIC2_MAP enabled");
+       if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN1_MAP)
+               dev_info(dev, "quirk IN1_MAP enabled");
+       if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN3_MAP)
+               dev_info(dev, "quirk IN3_MAP enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN)
+               dev_info(dev, "quirk DMIC enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER)
+               dev_info(dev, "quirk MONO_SPEAKER enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC)
+               dev_info(dev, "quirk DIFF_MIC enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2)
+               dev_info(dev, "quirk SSP2_AIF2 enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1)
+               dev_info(dev, "quirk SSP0_AIF1 enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)
+               dev_info(dev, "quirk SSP0_AIF2 enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN)
+               dev_info(dev, "quirk MCLK_EN enabled");
+       if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
+               dev_info(dev, "quirk MCLK_25MHZ enabled");
+}
+
+
+#define BYT_CODEC_DAI1 "rt5640-aif1"
+#define BYT_CODEC_DAI2 "rt5640-aif2"
+
+static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card)
+{
+       struct snd_soc_pcm_runtime *rtd;
+
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1,
+                            strlen(BYT_CODEC_DAI1)))
+                       return rtd->codec_dai;
+               if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2,
+                               strlen(BYT_CODEC_DAI2)))
+                       return rtd->codec_dai;
+
+       }
+       return NULL;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+                                 struct snd_kcontrol *k, int  event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_soc_card *card = dapm->card;
+       struct snd_soc_dai *codec_dai;
+       struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+       int ret;
+
+       codec_dai = byt_get_codec_dai(card);
+       if (!codec_dai) {
+               dev_err(card->dev,
+                       "Codec dai not found; Unable to set platform clock\n");
+               return -EIO;
+       }
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) {
+                       ret = clk_prepare_enable(priv->mclk);
+                       if (ret < 0) {
+                               dev_err(card->dev,
+                                       "could not configure MCLK state");
+                               return ret;
+                       }
+               }
+               ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+                                            48000 * 512,
+                                            SND_SOC_CLOCK_IN);
+       } else {
+               /*
+                * Set codec clock source to internal clock before
+                * turning off the platform clock. Codec needs clock
+                * for Jack detection and button press
+                */
+               ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_RCCLK,
+                                            0,
+                                            SND_SOC_CLOCK_IN);
+               if (!ret) {
+                       if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk)
+                               clk_disable_unprepare(priv->mclk);
+               }
+       }
+
+       if (ret < 0) {
+               dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
 
 static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
        SND_SOC_DAPM_MIC("Internal Mic", NULL),
        SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+                           platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+                           SND_SOC_DAPM_POST_PMD),
+
 };
 
 static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
-       {"AIF1 Playback", NULL, "ssp2 Tx"},
-       {"ssp2 Tx", NULL, "codec_out0"},
-       {"ssp2 Tx", NULL, "codec_out1"},
-       {"codec_in0", NULL, "ssp2 Rx"},
-       {"codec_in1", NULL, "ssp2 Rx"},
-       {"ssp2 Rx", NULL, "AIF1 Capture"},
+       {"Headphone", NULL, "Platform Clock"},
+       {"Headset Mic", NULL, "Platform Clock"},
+       {"Internal Mic", NULL, "Platform Clock"},
+       {"Speaker", NULL, "Platform Clock"},
 
        {"Headset Mic", NULL, "MICBIAS1"},
        {"IN2P", NULL, "Headset Mic"},
        {"Headphone", NULL, "HPOL"},
        {"Headphone", NULL, "HPOR"},
-       {"Speaker", NULL, "SPOLP"},
-       {"Speaker", NULL, "SPOLN"},
-       {"Speaker", NULL, "SPORP"},
-       {"Speaker", NULL, "SPORN"},
 };
 
 static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
@@ -82,6 +196,59 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
        {"IN1P", NULL, "Internal Mic"},
 };
 
+static const struct snd_soc_dapm_route byt_rt5640_intmic_in3_map[] = {
+       {"Internal Mic", NULL, "MICBIAS1"},
+       {"IN3P", NULL, "Internal Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif1_map[] = {
+       {"ssp2 Tx", NULL, "codec_out0"},
+       {"ssp2 Tx", NULL, "codec_out1"},
+       {"codec_in0", NULL, "ssp2 Rx"},
+       {"codec_in1", NULL, "ssp2 Rx"},
+
+       {"AIF1 Playback", NULL, "ssp2 Tx"},
+       {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_ssp2_aif2_map[] = {
+       {"ssp2 Tx", NULL, "codec_out0"},
+       {"ssp2 Tx", NULL, "codec_out1"},
+       {"codec_in0", NULL, "ssp2 Rx"},
+       {"codec_in1", NULL, "ssp2 Rx"},
+
+       {"AIF2 Playback", NULL, "ssp2 Tx"},
+       {"ssp2 Rx", NULL, "AIF2 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif1_map[] = {
+       {"ssp0 Tx", NULL, "modem_out"},
+       {"modem_in", NULL, "ssp0 Rx"},
+
+       {"AIF1 Playback", NULL, "ssp0 Tx"},
+       {"ssp0 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_ssp0_aif2_map[] = {
+       {"ssp0 Tx", NULL, "modem_out"},
+       {"modem_in", NULL, "ssp0 Rx"},
+
+       {"AIF2 Playback", NULL, "ssp0 Tx"},
+       {"ssp0 Rx", NULL, "AIF2 Capture"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_stereo_spk_map[] = {
+       {"Speaker", NULL, "SPOLP"},
+       {"Speaker", NULL, "SPOLN"},
+       {"Speaker", NULL, "SPORP"},
+       {"Speaker", NULL, "SPORN"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_mono_spk_map[] = {
+       {"Speaker", NULL, "SPOLP"},
+       {"Speaker", NULL, "SPOLN"},
+};
+
 static const struct snd_kcontrol_new byt_rt5640_controls[] = {
        SOC_DAPM_PIN_SWITCH("Headphone"),
        SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -96,19 +263,46 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        int ret;
 
-       snd_soc_dai_set_bclk_ratio(codec_dai, 50);
-
        ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
                                     params_rate(params) * 512,
                                     SND_SOC_CLOCK_IN);
+
        if (ret < 0) {
                dev_err(rtd->dev, "can't set codec clock %d\n", ret);
                return ret;
        }
 
-       ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
-                                 params_rate(params) * 50,
-                                 params_rate(params) * 512);
+       if (!(byt_rt5640_quirk & BYT_RT5640_MCLK_EN)) {
+               /* use bitclock as PLL input */
+               if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
+                       (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+
+                       /* 2x16 bit slots on SSP0 */
+                       ret = snd_soc_dai_set_pll(codec_dai, 0,
+                                               RT5640_PLL1_S_BCLK1,
+                                               params_rate(params) * 32,
+                                               params_rate(params) * 512);
+               } else {
+                       /* 2x15 bit slots on SSP2 */
+                       ret = snd_soc_dai_set_pll(codec_dai, 0,
+                                               RT5640_PLL1_S_BCLK1,
+                                               params_rate(params) * 50,
+                                               params_rate(params) * 512);
+               }
+       } else {
+               if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) {
+                       ret = snd_soc_dai_set_pll(codec_dai, 0,
+                                               RT5640_PLL1_S_MCLK,
+                                               25000000,
+                                               params_rate(params) * 512);
+               } else {
+                       ret = snd_soc_dai_set_pll(codec_dai, 0,
+                                               RT5640_PLL1_S_MCLK,
+                                               19200000,
+                                               params_rate(params) * 512);
+               }
+       }
+
        if (ret < 0) {
                dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
                return ret;
@@ -127,27 +321,73 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
        {
                .callback = byt_rt5640_quirk_cb,
                .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+               },
+               .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+                                                BYT_RT5640_MCLK_EN),
+       },
+       {
+               .callback = byt_rt5640_quirk_cb,
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"),
                },
-               .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+               .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+                                                BYT_RT5640_MONO_SPEAKER |
+                                                BYT_RT5640_DIFF_MIC |
+                                                BYT_RT5640_SSP0_AIF2 |
+                                                BYT_RT5640_MCLK_EN
+                                                ),
        },
        {
                .callback = byt_rt5640_quirk_cb,
                .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
                },
                .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
+                                                BYT_RT5640_DMIC_EN |
+                                                BYT_RT5640_MCLK_EN),
+       },
+       {
+               .callback = byt_rt5640_quirk_cb,
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
+               },
+               .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+                                                BYT_RT5640_MCLK_EN),
+       },
+       {
+               .callback = byt_rt5640_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
+               },
+               .driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP |
                                                 BYT_RT5640_DMIC_EN),
        },
        {
                .callback = byt_rt5640_quirk_cb,
                .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
+                       DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
+                       DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
                },
-               .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+               .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP |
+                                               BYT_RT5640_MCLK_EN |
+                                               BYT_RT5640_SSP0_AIF1),
+       },
+       {
+               .callback = byt_rt5640_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
+               },
+               .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+                                                BYT_RT5640_MCLK_EN |
+                                                BYT_RT5640_SSP0_AIF1),
+
        },
        {}
 };
@@ -158,13 +398,18 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
        struct snd_soc_codec *codec = runtime->codec;
        struct snd_soc_card *card = runtime->card;
        const struct snd_soc_dapm_route *custom_map;
+       struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
        int num_routes;
 
        card->dapm.idle_bias_off = true;
 
        rt5640_sel_asrc_clk_src(codec,
                                RT5640_DA_STEREO_FILTER |
-                               RT5640_AD_STEREO_FILTER,
+                               RT5640_DA_MONO_L_FILTER |
+                               RT5640_DA_MONO_R_FILTER |
+                               RT5640_AD_STEREO_FILTER |
+                               RT5640_AD_MONO_L_FILTER |
+                               RT5640_AD_MONO_R_FILTER,
                                RT5640_CLK_SEL_ASRC);
 
        ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
@@ -179,6 +424,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
                custom_map = byt_rt5640_intmic_in1_map;
                num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
                break;
+       case BYT_RT5640_IN3_MAP:
+               custom_map = byt_rt5640_intmic_in3_map;
+               num_routes = ARRAY_SIZE(byt_rt5640_intmic_in3_map);
+               break;
        case BYT_RT5640_DMIC2_MAP:
                custom_map = byt_rt5640_intmic_dmic2_map;
                num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
@@ -192,6 +441,43 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
        if (ret)
                return ret;
 
+       if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       byt_rt5640_ssp2_aif2_map,
+                                       ARRAY_SIZE(byt_rt5640_ssp2_aif2_map));
+       } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       byt_rt5640_ssp0_aif1_map,
+                                       ARRAY_SIZE(byt_rt5640_ssp0_aif1_map));
+       } else if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       byt_rt5640_ssp0_aif2_map,
+                                       ARRAY_SIZE(byt_rt5640_ssp0_aif2_map));
+       } else {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       byt_rt5640_ssp2_aif1_map,
+                                       ARRAY_SIZE(byt_rt5640_ssp2_aif1_map));
+       }
+       if (ret)
+               return ret;
+
+       if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       byt_rt5640_mono_spk_map,
+                                       ARRAY_SIZE(byt_rt5640_mono_spk_map));
+       } else {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       byt_rt5640_stereo_spk_map,
+                                       ARRAY_SIZE(byt_rt5640_stereo_spk_map));
+       }
+       if (ret)
+               return ret;
+
+       if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) {
+               snd_soc_update_bits(codec,  RT5640_IN1_IN2, RT5640_IN_DF1,
+                                   RT5640_IN_DF1);
+       }
+
        if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
                ret = rt5640_dmic_enable(codec, 0, 0);
                if (ret)
@@ -201,6 +487,30 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
        snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
        snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
 
+       if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) {
+               /*
+                * The firmware might enable the clock at
+                * boot (this information may or may not
+                * be reflected in the enable clock register).
+                * To change the rate we must disable the clock
+                * first to cover these cases. Due to common
+                * clock framework restrictions that do not allow
+                * to disable a clock that has not been enabled,
+                * we need to enable the clock first.
+                */
+               ret = clk_prepare_enable(priv->mclk);
+               if (!ret)
+                       clk_disable_unprepare(priv->mclk);
+
+               if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
+                       ret = clk_set_rate(priv->mclk, 25000000);
+               else
+                       ret = clk_set_rate(priv->mclk, 19200000);
+
+               if (ret)
+                       dev_err(card->dev, "unable to set MCLK rate\n");
+       }
+
        return ret;
 }
 
@@ -221,34 +531,63 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
                                                SNDRV_PCM_HW_PARAM_CHANNELS);
        int ret;
 
-       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       /* The DSP will covert the FE rate to 48k, stereo */
        rate->min = rate->max = 48000;
        channels->min = channels->max = 2;
 
-       /* set SSP2 to 24-bit */
-       params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+       if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
+               (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+
+               /* set SSP0 to 16-bit */
+               params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+               /*
+                * Default mode for SSP configuration is TDM 4 slot, override config
+                * with explicit setting to I2S 2ch 16-bit. The word length is set with
+                * dai_set_tdm_slot() since there is no other API exposed
+                */
+               ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+                                       SND_SOC_DAIFMT_I2S     |
+                                       SND_SOC_DAIFMT_NB_IF   |
+                                       SND_SOC_DAIFMT_CBS_CFS
+                       );
+               if (ret < 0) {
+                       dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+                       return ret;
+               }
 
-       /*
-        * Default mode for SSP configuration is TDM 4 slot, override config
-        * with explicit setting to I2S 2ch 24-bit. The word length is set with
-        * dai_set_tdm_slot() since there is no other API exposed
-        */
-       ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
-                                 SND_SOC_DAIFMT_I2S     |
-                                 SND_SOC_DAIFMT_NB_IF   |
-                                 SND_SOC_DAIFMT_CBS_CFS
-                                 );
-       if (ret < 0) {
-               dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
-               return ret;
-       }
+               ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16);
+               if (ret < 0) {
+                       dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+                       return ret;
+               }
 
-       ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
-       if (ret < 0) {
-               dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
-               return ret;
-       }
+       } else {
+
+               /* set SSP2 to 24-bit */
+               params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+               /*
+                * Default mode for SSP configuration is TDM 4 slot, override config
+                * with explicit setting to I2S 2ch 24-bit. The word length is set with
+                * dai_set_tdm_slot() since there is no other API exposed
+                */
+               ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+                                       SND_SOC_DAIFMT_I2S     |
+                                       SND_SOC_DAIFMT_NB_IF   |
+                                       SND_SOC_DAIFMT_CBS_CFS
+                       );
+               if (ret < 0) {
+                       dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+                       return ret;
+               }
 
+               ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
+               if (ret < 0) {
+                       dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+                       return ret;
+               }
+       }
        return 0;
 }
 
@@ -305,10 +644,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
        {
                .name = "SSP2-Codec",
                .id = 1,
-               .cpu_dai_name = "ssp2-port",
+               .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */
                .platform_name = "sst-mfld-platform",
                .no_pcm = 1,
-               .codec_dai_name = "rt5640-aif1",
+               .codec_dai_name = "rt5640-aif1", /* changed w/ quirk */
                .codec_name = "i2c-10EC5640:00", /* overwritten with HID */
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
                                                | SND_SOC_DAIFMT_CBS_CFS,
@@ -335,6 +674,21 @@ static struct snd_soc_card byt_rt5640_card = {
 };
 
 static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+static char byt_rt5640_codec_aif_name[12]; /*  = "rt5640-aif[1|2]" */
+static char byt_rt5640_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
+
+static bool is_valleyview(void)
+{
+       static const struct x86_cpu_id cpu_ids[] = {
+               { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
+               {}
+       };
+
+       if (!x86_match_cpu(cpu_ids))
+               return false;
+       return true;
+}
+
 
 static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
 {
@@ -343,10 +697,16 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
        const char *i2c_name = NULL;
        int i;
        int dai_index;
+       struct byt_rt5640_private *priv;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+       if (!priv)
+               return -ENOMEM;
 
        /* register the soc card */
        byt_rt5640_card.dev = &pdev->dev;
        mach = byt_rt5640_card.dev->platform_data;
+       snd_soc_card_set_drvdata(&byt_rt5640_card, priv);
 
        /* fix index of codec dai */
        dai_index = MERR_DPCM_COMPR + 1;
@@ -366,8 +726,57 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
                byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name;
        }
 
+       /*
+        * swap SSP0 if bytcr is detected
+        * (will be overridden if DMI quirk is detected)
+        */
+       if (is_valleyview()) {
+               struct sst_platform_info *p_info = mach->pdata;
+               const struct sst_res_info *res_info = p_info->res_info;
+
+               /* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */
+               if (res_info->acpi_ipc_irq_index == 0) {
+                       byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
+               }
+       }
+
        /* check quirks before creating card */
        dmi_check_system(byt_rt5640_quirk_table);
+       log_quirks(&pdev->dev);
+
+       if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) ||
+           (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+
+               /* fixup codec aif name */
+               snprintf(byt_rt5640_codec_aif_name,
+                       sizeof(byt_rt5640_codec_aif_name),
+                       "%s", "rt5640-aif2");
+
+               byt_rt5640_dais[dai_index].codec_dai_name =
+                       byt_rt5640_codec_aif_name;
+       }
+
+       if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
+           (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+
+               /* fixup cpu dai name name */
+               snprintf(byt_rt5640_cpu_dai_name,
+                       sizeof(byt_rt5640_cpu_dai_name),
+                       "%s", "ssp0-port");
+
+               byt_rt5640_dais[dai_index].cpu_dai_name =
+                       byt_rt5640_cpu_dai_name;
+       }
+
+       if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) {
+               priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+               if (IS_ERR(priv->mclk)) {
+                       dev_err(&pdev->dev,
+                               "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
+                               PTR_ERR(priv->mclk));
+                       return PTR_ERR(priv->mclk);
+               }
+       }
 
        ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
 
index 2c5eda1..1285cc5 100644 (file)
@@ -199,6 +199,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = {
 
 static struct sst_acpi_mach broadwell_machines[] = {
        { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL },
+       { "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL },
        {}
 };
 
index 2663781..1d251d5 100644 (file)
@@ -23,6 +23,7 @@
 #include "../common/sst-dsp.h"
 #include "../common/sst-dsp-priv.h"
 #include "skl-sst-ipc.h"
+#include "skl-tplg-interface.h"
 
 #define BXT_BASEFW_TIMEOUT     3000
 #define BXT_INIT_TIMEOUT       500
 #define BXT_INSTANCE_ID 0
 #define BXT_BASE_FW_MODULE_ID 0
 
+#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
+
 static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
 {
         return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
 }
 
+static int
+bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo)
+{
+       struct snd_dma_buffer dmab;
+       struct skl_sst *skl = ctx->thread_context;
+       const struct firmware *fw = NULL;
+       struct firmware stripped_fw;
+       int ret = 0, i, dma_id, stream_tag;
+
+       /* library indices start from 1 to N. 0 represents base FW */
+       for (i = 1; i < minfo->lib_count; i++) {
+               ret = request_firmware(&fw, minfo->lib[i].name, ctx->dev);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "Request lib %s failed:%d\n",
+                                       minfo->lib[i].name, ret);
+                       return ret;
+               }
+
+               if (skl->is_first_boot) {
+                       ret = snd_skl_parse_uuids(ctx, fw,
+                                       BXT_ADSP_FW_BIN_HDR_OFFSET, i);
+                       if (ret < 0)
+                               goto load_library_failed;
+               }
+
+               stripped_fw.data = fw->data;
+               stripped_fw.size = fw->size;
+               skl_dsp_strip_extended_manifest(&stripped_fw);
+
+               stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40,
+                                       stripped_fw.size, &dmab);
+               if (stream_tag <= 0) {
+                       dev_err(ctx->dev, "Lib prepare DMA err: %x\n",
+                                       stream_tag);
+                       ret = stream_tag;
+                       goto load_library_failed;
+               }
+
+               dma_id = stream_tag - 1;
+               memcpy(dmab.area, stripped_fw.data, stripped_fw.size);
+
+               ctx->dsp_ops.trigger(ctx->dev, true, stream_tag);
+               ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i);
+               if (ret < 0)
+                       dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n",
+                                       minfo->lib[i].name, ret);
+
+               ctx->dsp_ops.trigger(ctx->dev, false, stream_tag);
+               ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag);
+               release_firmware(fw);
+               fw = NULL;
+       }
+
+       return ret;
+
+load_library_failed:
+       release_firmware(fw);
+       return ret;
+}
+
 /*
  * First boot sequence has some extra steps. Core 0 waits for power
  * status on core 1, so power up core 1 also momentarily, keep it in
@@ -157,8 +220,6 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
        return ret;
 }
 
-#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
-
 static int bxt_load_base_firmware(struct sst_dsp *ctx)
 {
        struct firmware stripped_fw;
@@ -175,9 +236,12 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
        if (ctx->fw == NULL)
                goto sst_load_base_firmware_failed;
 
-       ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET);
-       if (ret < 0)
-               goto sst_load_base_firmware_failed;
+       /* prase uuids on first boot */
+       if (skl->is_first_boot) {
+               ret = snd_skl_parse_uuids(ctx, ctx->fw, BXT_ADSP_FW_BIN_HDR_OFFSET, 0);
+               if (ret < 0)
+                       goto sst_load_base_firmware_failed;
+       }
 
        stripped_fw.data = ctx->fw->data;
        stripped_fw.size = ctx->fw->size;
@@ -230,12 +294,23 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
        int ret;
        struct skl_ipc_dxstate_info dx;
        unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
+       struct skl_dfw_manifest *minfo = &skl->manifest;
 
        if (skl->fw_loaded == false) {
                skl->boot_complete = false;
                ret = bxt_load_base_firmware(ctx);
-               if (ret < 0)
+               if (ret < 0) {
                        dev_err(ctx->dev, "reload fw failed: %d\n", ret);
+                       return ret;
+               }
+
+               if (minfo->lib_count > 1) {
+                       ret = bxt_load_library(ctx, minfo);
+                       if (ret < 0) {
+                               dev_err(ctx->dev, "reload libs failed: %d\n", ret);
+                               return ret;
+                       }
+               }
                return ret;
        }
 
@@ -329,7 +404,7 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
 
        ret = skl_dsp_disable_core(ctx, core_mask);
        if (ret < 0) {
-               dev_err(ctx->dev, "Failed to disable core %d", ret);
+               dev_err(ctx->dev, "Failed to disable core %d\n", ret);
                return ret;
        }
        skl->cores.state[core_id] = SKL_DSP_RESET;
@@ -341,6 +416,7 @@ static struct skl_dsp_fw_ops bxt_fw_ops = {
        .set_state_D3 = bxt_set_dsp_D3,
        .load_fw = bxt_load_base_firmware,
        .get_fw_errcode = bxt_get_errorcode,
+       .load_library = bxt_load_library,
 };
 
 static struct sst_ops skl_ops = {
@@ -397,22 +473,40 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
        skl->cores.count = 2;
        skl->boot_complete = false;
        init_waitqueue_head(&skl->boot_wait);
+       skl->is_first_boot = true;
+
+       if (dsp)
+               *dsp = skl;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
+
+int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx)
+{
+       int ret;
+       struct sst_dsp *sst = ctx->dsp;
 
        ret = sst->fw_ops.load_fw(sst);
        if (ret < 0) {
-               dev_err(dev, "Load base fw failed: %x", ret);
+               dev_err(dev, "Load base fw failed: %x\n", ret);
                return ret;
        }
 
        skl_dsp_init_core_state(sst);
 
-       if (dsp)
-               *dsp = skl;
+       if (ctx->manifest.lib_count > 1) {
+               ret = sst->fw_ops.load_library(sst, &ctx->manifest);
+               if (ret < 0) {
+                       dev_err(dev, "Load Library failed : %x\n", ret);
+                       return ret;
+               }
+       }
+       ctx->is_first_boot = false;
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
-
+EXPORT_SYMBOL_GPL(bxt_sst_init_fw);
 
 void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
 {
index 44ab595..805b7f2 100644 (file)
@@ -203,32 +203,35 @@ static const struct skl_dsp_ops dsp_ops[] = {
                .id = 0x9d70,
                .loader_ops = skl_get_loader_ops,
                .init = skl_sst_dsp_init,
+               .init_fw = skl_sst_init_fw,
                .cleanup = skl_sst_dsp_cleanup
        },
        {
                .id = 0x9d71,
                .loader_ops = skl_get_loader_ops,
                .init = skl_sst_dsp_init,
+               .init_fw = skl_sst_init_fw,
                .cleanup = skl_sst_dsp_cleanup
        },
        {
                .id = 0x5a98,
                .loader_ops = bxt_get_loader_ops,
                .init = bxt_sst_dsp_init,
+               .init_fw = bxt_sst_init_fw,
                .cleanup = bxt_sst_dsp_cleanup
        },
 };
 
-static int skl_get_dsp_ops(int pci_id)
+const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id)
 {
        int i;
 
        for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) {
                if (dsp_ops[i].id == pci_id)
-                       return i;
+                       return &dsp_ops[i];
        }
 
-       return -EINVAL;
+       return NULL;
 }
 
 int skl_init_dsp(struct skl *skl)
@@ -238,7 +241,8 @@ int skl_init_dsp(struct skl *skl)
        struct hdac_bus *bus = ebus_to_hbus(ebus);
        struct skl_dsp_loader_ops loader_ops;
        int irq = bus->irq;
-       int ret, index;
+       const struct skl_dsp_ops *ops;
+       int ret;
 
        /* enable ppcap interrupt */
        snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
@@ -251,18 +255,18 @@ int skl_init_dsp(struct skl *skl)
                return -ENXIO;
        }
 
-       index  = skl_get_dsp_ops(skl->pci->device);
-       if (index  < 0)
-               return -EINVAL;
+       ops = skl_get_dsp_ops(skl->pci->device);
+       if (!ops)
+               return -EIO;
 
-       loader_ops = dsp_ops[index].loader_ops();
-       ret = dsp_ops[index].init(bus->dev, mmio_base, irq,
-                       skl->fw_name, loader_ops, &skl->skl_sst);
+       loader_ops = ops->loader_ops();
+       ret = ops->init(bus->dev, mmio_base, irq,
+                               skl->fw_name, loader_ops,
+                               &skl->skl_sst);
 
        if (ret < 0)
                return ret;
 
-       skl_dsp_enable_notification(skl->skl_sst, false);
        dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
 
        return ret;
@@ -273,16 +277,16 @@ int skl_free_dsp(struct skl *skl)
        struct hdac_ext_bus *ebus = &skl->ebus;
        struct hdac_bus *bus = ebus_to_hbus(ebus);
        struct skl_sst *ctx = skl->skl_sst;
-       int index;
+       const struct skl_dsp_ops *ops;
 
        /* disable  ppcap interrupt */
        snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
 
-       index = skl_get_dsp_ops(skl->pci->device);
-       if (index  < 0)
+       ops = skl_get_dsp_ops(skl->pci->device);
+       if (!ops)
                return -EIO;
 
-       dsp_ops[index].cleanup(bus->dev, ctx);
+       ops->cleanup(bus->dev, ctx);
 
        if (ctx->dsp->addr.lpe)
                iounmap(ctx->dsp->addr.lpe);
@@ -296,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl)
        int ret;
 
        /* if ppcap is not supported return 0 */
-       if (!skl->ebus.ppcap)
+       if (!skl->ebus.bus.ppcap)
                return 0;
 
        ret = skl_dsp_sleep(ctx->dsp);
@@ -316,13 +320,17 @@ int skl_resume_dsp(struct skl *skl)
        int ret;
 
        /* if ppcap is not supported return 0 */
-       if (!skl->ebus.ppcap)
+       if (!skl->ebus.bus.ppcap)
                return 0;
 
        /* enable ppcap interrupt */
        snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
        snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
 
+       /* check if DSP 1st boot is done */
+       if (skl->skl_sst->is_first_boot == true)
+               return 0;
+
        ret = skl_dsp_wake(ctx->dsp);
        if (ret < 0)
                return ret;
@@ -672,6 +680,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx,
                return param_size;
 
        case SKL_MODULE_TYPE_BASE_OUTFMT:
+       case SKL_MODULE_TYPE_KPB:
                return sizeof(struct skl_base_outfmt_cfg);
 
        default:
@@ -725,6 +734,7 @@ static int skl_set_module_format(struct skl_sst *ctx,
                break;
 
        case SKL_MODULE_TYPE_BASE_OUTFMT:
+       case SKL_MODULE_TYPE_KPB:
                skl_set_base_outfmt_format(ctx, module_config, *param_data);
                break;
 
@@ -779,6 +789,7 @@ static int skl_alloc_queue(struct skl_module_pin *mpin,
                                mpin[i].in_use = true;
                                mpin[i].id.module_id = id.module_id;
                                mpin[i].id.instance_id = id.instance_id;
+                               mpin[i].id.pvt_id = id.pvt_id;
                                mpin[i].tgt_mcfg = tgt_cfg;
                                return i;
                        }
@@ -802,6 +813,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
                mpin[q_index].in_use = false;
                mpin[q_index].id.module_id = 0;
                mpin[q_index].id.instance_id = 0;
+               mpin[q_index].id.pvt_id = 0;
        }
        mpin[q_index].pin_state = SKL_PIN_UNBIND;
        mpin[q_index].tgt_mcfg = NULL;
@@ -842,7 +854,7 @@ int skl_init_module(struct skl_sst *ctx,
        struct skl_ipc_init_instance_msg msg;
 
        dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__,
-                mconfig->id.module_id, mconfig->id.instance_id);
+                mconfig->id.module_id, mconfig->id.pvt_id);
 
        if (mconfig->pipe->state != SKL_PIPE_CREATED) {
                dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n",
@@ -858,10 +870,11 @@ int skl_init_module(struct skl_sst *ctx,
        }
 
        msg.module_id = mconfig->id.module_id;
-       msg.instance_id = mconfig->id.instance_id;
+       msg.instance_id = mconfig->id.pvt_id;
        msg.ppl_instance_id = mconfig->pipe->ppl_id;
        msg.param_data_size = module_config_size;
        msg.core_id = mconfig->core_id;
+       msg.domain = mconfig->domain;
 
        ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data);
        if (ret < 0) {
@@ -878,9 +891,9 @@ static void skl_dump_bind_info(struct skl_sst *ctx, struct skl_module_cfg
        *src_module, struct skl_module_cfg *dst_module)
 {
        dev_dbg(ctx->dev, "%s: src module_id = %d  src_instance=%d\n",
-               __func__, src_module->id.module_id, src_module->id.instance_id);
+               __func__, src_module->id.module_id, src_module->id.pvt_id);
        dev_dbg(ctx->dev, "%s: dst_module=%d dst_instacne=%d\n", __func__,
-                dst_module->id.module_id, dst_module->id.instance_id);
+                dst_module->id.module_id, dst_module->id.pvt_id);
 
        dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n",
                src_module->m_state, dst_module->m_state);
@@ -927,9 +940,9 @@ int skl_unbind_modules(struct skl_sst *ctx,
                return 0;
 
        msg.module_id = src_mcfg->id.module_id;
-       msg.instance_id = src_mcfg->id.instance_id;
+       msg.instance_id = src_mcfg->id.pvt_id;
        msg.dst_module_id = dst_mcfg->id.module_id;
-       msg.dst_instance_id = dst_mcfg->id.instance_id;
+       msg.dst_instance_id = dst_mcfg->id.pvt_id;
        msg.bind = false;
 
        ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
@@ -988,9 +1001,9 @@ int skl_bind_modules(struct skl_sst *ctx,
                         msg.src_queue, msg.dst_queue);
 
        msg.module_id = src_mcfg->id.module_id;
-       msg.instance_id = src_mcfg->id.instance_id;
+       msg.instance_id = src_mcfg->id.pvt_id;
        msg.dst_module_id = dst_mcfg->id.module_id;
-       msg.dst_instance_id = dst_mcfg->id.instance_id;
+       msg.dst_instance_id = dst_mcfg->id.pvt_id;
        msg.bind = true;
 
        ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
@@ -1168,7 +1181,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
        struct skl_ipc_large_config_msg msg;
 
        msg.module_id = mcfg->id.module_id;
-       msg.instance_id = mcfg->id.instance_id;
+       msg.instance_id = mcfg->id.pvt_id;
        msg.param_data_size = size;
        msg.large_param_id = param_id;
 
@@ -1181,7 +1194,7 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
        struct skl_ipc_large_config_msg msg;
 
        msg.module_id = mcfg->id.module_id;
-       msg.instance_id = mcfg->id.instance_id;
+       msg.instance_id = mcfg->id.pvt_id;
        msg.param_data_size = size;
        msg.large_param_id = param_id;
 
index 6e05bf8..c7cdcba 100644 (file)
@@ -106,7 +106,7 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus,
 
 static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus)
 {
-       if (ebus->ppcap)
+       if ((ebus_to_hbus(ebus))->ppcap)
                return HDAC_EXT_STREAM_TYPE_HOST;
        else
                return HDAC_EXT_STREAM_TYPE_COUPLED;
@@ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream,
        struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
        int format_val = 0;
 
-       if (ebus->ppcap) {
+       if ((ebus_to_hbus(ebus))->ppcap) {
                struct snd_pcm_runtime *runtime = substream->runtime;
 
                format_val = snd_hdac_calc_stream_format(runtime->rate,
@@ -648,7 +648,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
                .channels_min = HDA_MONO,
                .channels_max = HDA_STEREO,
                .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
        },
        .capture = {
                .stream_name = "System Capture",
@@ -1020,7 +1021,7 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
 {
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
 
-       if (!ebus->ppcap)
+       if (!(ebus_to_hbus(ebus))->ppcap)
                return skl_coupled_trigger(substream, cmd);
 
        return 0;
@@ -1138,20 +1139,67 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
        return retval;
 }
 
+static int skl_populate_modules(struct skl *skl)
+{
+       struct skl_pipeline *p;
+       struct skl_pipe_module *m;
+       struct snd_soc_dapm_widget *w;
+       struct skl_module_cfg *mconfig;
+       int ret;
+
+       list_for_each_entry(p, &skl->ppl_list, node) {
+               list_for_each_entry(m, &p->pipe->w_list, node) {
+
+                       w = m->w;
+                       mconfig = w->priv;
+
+                       ret = snd_skl_get_module_info(skl->skl_sst, mconfig);
+                       if (ret < 0) {
+                               dev_err(skl->skl_sst->dev,
+                                       "query module info failed:%d\n", ret);
+                               goto err;
+                       }
+               }
+       }
+err:
+       return ret;
+}
+
 static int skl_platform_soc_probe(struct snd_soc_platform *platform)
 {
        struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
        struct skl *skl = ebus_to_skl(ebus);
+       const struct skl_dsp_ops *ops;
        int ret;
 
-       if (ebus->ppcap) {
+       pm_runtime_get_sync(platform->dev);
+       if ((ebus_to_hbus(ebus))->ppcap) {
                ret = skl_tplg_init(platform, ebus);
                if (ret < 0) {
                        dev_err(platform->dev, "Failed to init topology!\n");
                        return ret;
                }
                skl->platform = platform;
+
+               /* load the firmwares, since all is set */
+               ops = skl_get_dsp_ops(skl->pci->device);
+               if (!ops)
+                       return -EIO;
+
+               if (skl->skl_sst->is_first_boot == false) {
+                       dev_err(platform->dev, "DSP reports first boot done!!!\n");
+                       return -EIO;
+               }
+
+               ret = ops->init_fw(platform->dev, skl->skl_sst);
+               if (ret < 0) {
+                       dev_err(platform->dev, "Failed to boot first fw: %d\n", ret);
+                       return ret;
+               }
+               skl_populate_modules(skl);
        }
+       pm_runtime_mark_last_busy(platform->dev);
+       pm_runtime_put_autosuspend(platform->dev);
 
        return 0;
 }
index da2329d..efa2532 100644 (file)
@@ -341,14 +341,14 @@ int skl_cldma_prepare(struct sst_dsp *ctx)
        ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
                        &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize);
        if (ret < 0) {
-               dev_err(ctx->dev, "Alloc buffer for base fw failed: %x", ret);
+               dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret);
                return ret;
        }
        /* Setup Code loader BDL */
        ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev,
                        &ctx->cl_dev.dmab_bdl, PAGE_SIZE);
        if (ret < 0) {
-               dev_err(ctx->dev, "Alloc buffer for blde failed: %x", ret);
+               dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret);
                ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
                return ret;
        }
index 0f8629e..b9e71d0 100644 (file)
@@ -20,6 +20,7 @@
 #include <sound/memalloc.h>
 #include "skl-sst-cldma.h"
 #include "skl-tplg-interface.h"
+#include "skl-topology.h"
 
 struct sst_dsp;
 struct skl_sst;
@@ -133,6 +134,8 @@ enum skl_dsp_states {
 struct skl_dsp_fw_ops {
        int (*load_fw)(struct sst_dsp  *ctx);
        /* FW module parser/loader */
+       int (*load_library)(struct sst_dsp *ctx,
+               struct skl_dfw_manifest *minfo);
        int (*parse_fw)(struct sst_dsp *ctx);
        int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
        int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id);
@@ -203,12 +206,21 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
                const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
                struct skl_sst **dsp);
+int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx);
+int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx);
 void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
 void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
 
-int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
-               struct skl_dfw_module *dfw_config);
-int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset);
+int snd_skl_get_module_info(struct skl_sst *ctx,
+                               struct skl_module_cfg *mconfig);
+int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw,
+                               unsigned int offset, int index);
+int skl_get_pvt_id(struct skl_sst *ctx,
+                               struct skl_module_cfg *mconfig);
+int skl_put_pvt_id(struct skl_sst *ctx,
+                               struct skl_module_cfg *mconfig);
+int skl_get_pvt_instance_id_map(struct skl_sst *ctx,
+                               int module_id, int instance_id);
 void skl_freeup_uuid_list(struct skl_sst *ctx);
 
 int skl_dsp_strip_extended_manifest(struct firmware *fw);
index 96f2f68..0bd01e6 100644 (file)
 #define IPC_CORE_ID(x)                 (((x) & IPC_CORE_ID_MASK) \
                                        << IPC_CORE_ID_SHIFT)
 
+#define IPC_DOMAIN_SHIFT                28
+#define IPC_DOMAIN_MASK                 0x1
+#define IPC_DOMAIN(x)                   (((x) & IPC_DOMAIN_MASK) \
+                                       << IPC_DOMAIN_SHIFT)
+
 /* Bind/Unbind message extension register */
 #define IPC_DST_MOD_ID_SHIFT           0
 #define IPC_DST_MOD_ID(x)              (((x) & IPC_MOD_ID_MASK) \
@@ -190,6 +195,7 @@ enum skl_ipc_glb_type {
        IPC_GLB_GET_PPL_CONTEXT_SIZE = 21,
        IPC_GLB_SAVE_PPL = 22,
        IPC_GLB_RESTORE_PPL = 23,
+       IPC_GLB_LOAD_LIBRARY = 24,
        IPC_GLB_NOTIFY = 26,
        IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */
 };
@@ -338,7 +344,7 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
                        break;
 
                default:
-                       dev_err(ipc->dev, "ipc: Unhandled error msg=%x",
+                       dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n",
                                                header.primary);
                        break;
                }
@@ -379,13 +385,13 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
                break;
 
        default:
-               dev_err(ipc->dev, "Unknown ipc reply: 0x%x", reply);
+               dev_err(ipc->dev, "Unknown ipc reply: 0x%x\n", reply);
                msg->errno = -EINVAL;
                break;
        }
 
        if (reply != IPC_GLB_REPLY_SUCCESS) {
-               dev_err(ipc->dev, "ipc FW reply: reply=%d", reply);
+               dev_err(ipc->dev, "ipc FW reply: reply=%d\n", reply);
                dev_err(ipc->dev, "FW Error Code: %u\n",
                        ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp));
        }
@@ -434,9 +440,9 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context)
                hipcte = sst_dsp_shim_read_unlocked(dsp, SKL_ADSP_REG_HIPCTE);
                header.primary = hipct;
                header.extension = hipcte;
-               dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x",
+               dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x\n",
                                                header.primary);
-               dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x",
+               dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x\n",
                                                header.extension);
 
                if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) {
@@ -704,6 +710,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
        header.extension = IPC_CORE_ID(msg->core_id);
        header.extension |= IPC_PPL_INSTANCE_ID(msg->ppl_instance_id);
        header.extension |= IPC_PARAM_BLOCK_SIZE(param_block_size);
+       header.extension |= IPC_DOMAIN(msg->domain);
 
        dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
                         header.primary, header.extension);
@@ -742,7 +749,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc,
                         header.extension);
        ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
        if (ret < 0) {
-               dev_err(ipc->dev, "ipc: bind/unbind faileden");
+               dev_err(ipc->dev, "ipc: bind/unbind failed\n");
                return ret;
        }
 
@@ -902,3 +909,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
        return ret;
 }
 EXPORT_SYMBOL_GPL(skl_ipc_get_large_config);
+
+int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
+                               u8 dma_id, u8 table_id)
+{
+       struct skl_ipc_header header = {0};
+       u64 *ipc_header = (u64 *)(&header);
+       int ret = 0;
+
+       header.primary = IPC_MSG_TARGET(IPC_FW_GEN_MSG);
+       header.primary |= IPC_MSG_DIR(IPC_MSG_REQUEST);
+       header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_LIBRARY);
+       header.primary |= IPC_MOD_INSTANCE_ID(table_id);
+       header.primary |= IPC_MOD_ID(dma_id);
+
+       ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+
+       if (ret < 0)
+               dev_err(ipc->dev, "ipc: load lib failed\n");
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library);
index 2e3d4e8..0334ed4 100644 (file)
@@ -66,7 +66,7 @@ struct skl_sst {
 
        /* callback for miscbdge */
        void (*enable_miscbdcge)(struct device *dev, bool enable);
-       /*Is CGCTL.MISCBDCGE disabled*/
+       /* Is CGCTL.MISCBDCGE disabled */
        bool miscbdcg_disabled;
 
        /* Populate module information */
@@ -75,8 +75,14 @@ struct skl_sst {
        /* Is firmware loaded */
        bool fw_loaded;
 
+       /* first boot ? */
+       bool is_first_boot;
+
        /* multi-core */
        struct skl_dsp_cores cores;
+
+       /* tplg manifest */
+       struct skl_dfw_manifest manifest;
 };
 
 struct skl_ipc_init_instance_msg {
@@ -85,6 +91,7 @@ struct skl_ipc_init_instance_msg {
        u16 param_data_size;
        u8 ppl_instance_id;
        u8 core_id;
+       u8 domain;
 };
 
 struct skl_ipc_bind_unbind_msg {
@@ -145,6 +152,9 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
 int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
                struct skl_ipc_large_config_msg *msg, u32 *param);
 
+int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
+                       u8 dma_id, u8 table_id);
+
 void skl_ipc_int_enable(struct sst_dsp *dsp);
 void skl_ipc_op_int_enable(struct sst_dsp *ctx);
 void skl_ipc_op_int_disable(struct sst_dsp *ctx);
index ddcb52a..8dc0303 100644 (file)
 /* FW Extended Manifest Header id = $AE1 */
 #define SKL_EXT_MANIFEST_HEADER_MAGIC   0x31454124
 
-struct skl_dfw_module_mod {
-       char name[100];
-       struct skl_dfw_module skl_dfw_mod;
-};
-
 struct UUID {
        u8 id[16];
 };
@@ -99,10 +94,15 @@ struct adsp_fw_hdr {
        u32 load_offset;
 } __packed;
 
+#define MAX_INSTANCE_BUFF 2
+
 struct uuid_module {
        uuid_le uuid;
        int id;
        int is_loadable;
+       int max_instance;
+       u64 pvt_id[MAX_INSTANCE_BUFF];
+       int *instance_id;
 
        struct list_head list;
 };
@@ -115,13 +115,13 @@ struct skl_ext_manifest_hdr {
        u32 entries;
 };
 
-int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
-                       struct skl_dfw_module *dfw_config)
+int snd_skl_get_module_info(struct skl_sst *ctx,
+                               struct skl_module_cfg *mconfig)
 {
        struct uuid_module *module;
        uuid_le *uuid_mod;
 
-       uuid_mod = (uuid_le *)uuid;
+       uuid_mod = (uuid_le *)mconfig->guid;
 
        if (list_empty(&ctx->uuid_list)) {
                dev_err(ctx->dev, "Module list is empty\n");
@@ -130,8 +130,8 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
 
        list_for_each_entry(module, &ctx->uuid_list, list) {
                if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
-                       dfw_config->module_id = module->id;
-                       dfw_config->is_loadable = module->is_loadable;
+                       mconfig->id.module_id = module->id;
+                       mconfig->is_loadable = module->is_loadable;
 
                        return 0;
                }
@@ -141,15 +141,154 @@ int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
 }
 EXPORT_SYMBOL_GPL(snd_skl_get_module_info);
 
+static int skl_get_pvtid_map(struct uuid_module *module, int instance_id)
+{
+       int pvt_id;
+
+       for (pvt_id = 0; pvt_id < module->max_instance; pvt_id++) {
+               if (module->instance_id[pvt_id] == instance_id)
+                       return pvt_id;
+       }
+       return -EINVAL;
+}
+
+int skl_get_pvt_instance_id_map(struct skl_sst *ctx,
+                               int module_id, int instance_id)
+{
+       struct uuid_module *module;
+
+       list_for_each_entry(module, &ctx->uuid_list, list) {
+               if (module->id == module_id)
+                       return skl_get_pvtid_map(module, instance_id);
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(skl_get_pvt_instance_id_map);
+
+static inline int skl_getid_32(struct uuid_module *module, u64 *val,
+                               int word1_mask, int word2_mask)
+{
+       int index, max_inst, pvt_id;
+       u32 mask_val;
+
+       max_inst =  module->max_instance;
+       mask_val = (u32)(*val >> word1_mask);
+
+       if (mask_val != 0xffffffff) {
+               index = ffz(mask_val);
+               pvt_id = index + word1_mask + word2_mask;
+               if (pvt_id <= (max_inst - 1)) {
+                       *val |= 1 << (index + word1_mask);
+                       return pvt_id;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static inline int skl_pvtid_128(struct uuid_module *module)
+{
+       int j, i, word1_mask, word2_mask = 0, pvt_id;
+
+       for (j = 0; j < MAX_INSTANCE_BUFF; j++) {
+               word1_mask = 0;
+
+               for (i = 0; i < 2; i++) {
+                       pvt_id = skl_getid_32(module, &module->pvt_id[j],
+                                               word1_mask, word2_mask);
+                       if (pvt_id >= 0)
+                               return pvt_id;
+
+                       word1_mask += 32;
+                       if ((word1_mask + word2_mask) >= module->max_instance)
+                               return -EINVAL;
+               }
+
+               word2_mask += 64;
+               if (word2_mask >= module->max_instance)
+                       return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * skl_get_pvt_id: generate a private id for use as module id
+ *
+ * @ctx: driver context
+ * @mconfig: module configuration data
+ *
+ * This generates a 128 bit private unique id for a module TYPE so that
+ * module instance is unique
+ */
+int skl_get_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+{
+       struct uuid_module *module;
+       uuid_le *uuid_mod;
+       int pvt_id;
+
+       uuid_mod = (uuid_le *)mconfig->guid;
+
+       list_for_each_entry(module, &ctx->uuid_list, list) {
+               if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+
+                       pvt_id = skl_pvtid_128(module);
+                       if (pvt_id >= 0) {
+                               module->instance_id[pvt_id] =
+                                               mconfig->id.instance_id;
+                               return pvt_id;
+                       }
+               }
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(skl_get_pvt_id);
+
+/**
+ * skl_put_pvt_id: free up the private id allocated
+ *
+ * @ctx: driver context
+ * @mconfig: module configuration data
+ *
+ * This frees a 128 bit private unique id previously generated
+ */
+int skl_put_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+{
+       int i;
+       uuid_le *uuid_mod;
+       struct uuid_module *module;
+
+       uuid_mod = (uuid_le *)mconfig->guid;
+       list_for_each_entry(module, &ctx->uuid_list, list) {
+               if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+
+                       if (mconfig->id.pvt_id != 0)
+                               i = (mconfig->id.pvt_id) / 64;
+                       else
+                               i = 0;
+
+                       module->pvt_id[i] &= ~(1 << (mconfig->id.pvt_id));
+                       mconfig->id.pvt_id = -1;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(skl_put_pvt_id);
+
 /*
  * Parse the firmware binary to get the UUID, module id
  * and loadable flags
  */
-int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
+int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw,
+                       unsigned int offset, int index)
 {
        struct adsp_fw_hdr *adsp_hdr;
        struct adsp_module_entry *mod_entry;
-       int i, num_entry;
+       int i, num_entry, size;
        uuid_le *uuid_bin;
        const char *buf;
        struct skl_sst *skl = ctx->thread_context;
@@ -158,8 +297,8 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
        unsigned int safe_file;
 
        /* Get the FW pointer to derive ADSP header */
-       stripped_fw.data = ctx->fw->data;
-       stripped_fw.size = ctx->fw->size;
+       stripped_fw.data = fw->data;
+       stripped_fw.size = fw->size;
 
        skl_dsp_strip_extended_manifest(&stripped_fw);
 
@@ -210,8 +349,15 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
                uuid_bin = (uuid_le *)mod_entry->uuid.id;
                memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
 
-               module->id = i;
+               module->id = (i | (index << 12));
                module->is_loadable = mod_entry->type.load_type;
+               module->max_instance = mod_entry->instance_max_count;
+               size = sizeof(int) * mod_entry->instance_max_count;
+               module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
+               if (!module->instance_id) {
+                       kfree(module);
+                       return -ENOMEM;
+               }
 
                list_add_tail(&module->list, &skl->uuid_list);
 
index 588f899..8fc3178 100644 (file)
@@ -88,13 +88,15 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                }
        }
 
-       ret = snd_skl_parse_uuids(ctx, SKL_ADSP_FW_BIN_HDR_OFFSET);
-       if (ret < 0) {
-               dev_err(ctx->dev,
-                               "UUID parsing err: %d\n", ret);
-               release_firmware(ctx->fw);
-               skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
-               return ret;
+       /* prase uuids on first boot */
+       if (skl->is_first_boot) {
+               ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "UUID parsing err: %d\n", ret);
+                       release_firmware(ctx->fw);
+                       skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
+                       return ret;
+               }
        }
 
        /* check for extended manifest */
@@ -105,13 +107,13 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
 
        ret = skl_dsp_boot(ctx);
        if (ret < 0) {
-               dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
+               dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
                goto skl_load_base_firmware_failed;
        }
 
        ret = skl_cldma_prepare(ctx);
        if (ret < 0) {
-               dev_err(ctx->dev, "CL dma prepare failed : %d", ret);
+               dev_err(ctx->dev, "CL dma prepare failed : %d\n", ret);
                goto skl_load_base_firmware_failed;
        }
 
@@ -484,25 +486,32 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
                return ret;
 
        skl->cores.count = 2;
+       skl->is_first_boot = true;
+
+       if (dsp)
+               *dsp = skl;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+
+int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx)
+{
+       int ret;
+       struct sst_dsp *sst = ctx->dsp;
 
        ret = sst->fw_ops.load_fw(sst);
        if (ret < 0) {
-               dev_err(dev, "Load base fw failed : %d", ret);
-               goto cleanup;
+               dev_err(dev, "Load base fw failed : %d\n", ret);
+               return ret;
        }
 
        skl_dsp_init_core_state(sst);
+       ctx->is_first_boot = false;
 
-       if (dsp)
-               *dsp = skl;
-
-       return ret;
-
-cleanup:
-       skl_sst_dsp_cleanup(dev, skl);
-       return ret;
+       return 0;
 }
-EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+EXPORT_SYMBOL_GPL(skl_sst_init_fw);
 
 void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
 {
index cc0150f..b5b1934 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/firmware.h>
 #include <sound/soc.h>
 #include <sound/soc-topology.h>
+#include <uapi/sound/snd_sst_tokens.h>
 #include "skl-sst-dsp.h"
 #include "skl-sst-ipc.h"
 #include "skl-topology.h"
@@ -32,6 +33,8 @@
 #define SKL_CH_FIXUP_MASK              (1 << 0)
 #define SKL_RATE_FIXUP_MASK            (1 << 1)
 #define SKL_FMT_FIXUP_MASK             (1 << 2)
+#define SKL_IN_DIR_BIT_MASK            BIT(0)
+#define SKL_PIN_COUNT_MASK             GENMASK(7, 4)
 
 /*
  * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
@@ -473,6 +476,14 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
                w = w_module->w;
                mconfig = w->priv;
 
+               /* check if module ids are populated */
+               if (mconfig->id.module_id < 0) {
+                       dev_err(skl->skl_sst->dev,
+                                       "module %pUL id not populated\n",
+                                       (uuid_le *)mconfig->guid);
+                       return -EIO;
+               }
+
                /* check resource available */
                if (!skl_is_pipe_mcps_avail(skl, mconfig))
                        return -ENOMEM;
@@ -494,12 +505,15 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
                 * FE/BE params
                 */
                skl_tplg_update_module_params(w, ctx);
-
+               mconfig->id.pvt_id = skl_get_pvt_id(ctx, mconfig);
+               if (mconfig->id.pvt_id < 0)
+                       return ret;
                skl_tplg_set_module_init_data(w);
                ret = skl_init_module(ctx, mconfig);
-               if (ret < 0)
+               if (ret < 0) {
+                       skl_put_pvt_id(ctx, mconfig);
                        return ret;
-
+               }
                skl_tplg_alloc_pipe_mcps(skl, mconfig);
                ret = skl_tplg_set_module_params(w, ctx);
                if (ret < 0)
@@ -512,6 +526,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
 static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
         struct skl_pipe *pipe)
 {
+       int ret;
        struct skl_pipe_module *w_module = NULL;
        struct skl_module_cfg *mconfig = NULL;
 
@@ -519,9 +534,13 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
                mconfig  = w_module->w->priv;
 
                if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod &&
-                       mconfig->m_state > SKL_MODULE_UNINIT)
-                       return ctx->dsp->fw_ops.unload_mod(ctx->dsp,
+                       mconfig->m_state > SKL_MODULE_UNINIT) {
+                       ret = ctx->dsp->fw_ops.unload_mod(ctx->dsp,
                                                mconfig->id.module_id);
+                       if (ret < 0)
+                               return -EIO;
+               }
+               skl_put_pvt_id(ctx, mconfig);
        }
 
        /* no modules to unload in this path, so return */
@@ -588,6 +607,26 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int skl_fill_sink_instance_id(struct skl_sst *ctx,
+                               struct skl_algo_data *alg_data)
+{
+       struct skl_kpb_params *params = (struct skl_kpb_params *)alg_data->params;
+       struct skl_mod_inst_map *inst;
+       int i, pvt_id;
+
+       inst = params->map;
+
+       for (i = 0; i < params->num_modules; i++) {
+               pvt_id = skl_get_pvt_instance_id_map(ctx,
+                                       inst->mod_id, inst->inst_id);
+               if (pvt_id < 0)
+                       return -EINVAL;
+               inst->inst_id = pvt_id;
+               inst++;
+       }
+       return 0;
+}
+
 /*
  * Some modules require params to be set after the module is bound to
  * all pins connected.
@@ -636,6 +675,8 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
                        bc = (struct skl_algo_data *)sb->dobj.private;
 
                        if (bc->set_params == SKL_PARAM_BIND) {
+                               if (mconfig->m_type == SKL_MODULE_TYPE_KPB)
+                                       skl_fill_sink_instance_id(ctx, bc);
                                ret = skl_set_module_params(ctx,
                                                (u32 *)bc->params, bc->max,
                                                bc->param_id, mconfig);
@@ -1460,85 +1501,570 @@ static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = {
                                        skl_tplg_tlv_control_set},
 };
 
-/*
- * The topology binary passes the pin info for a module so initialize the pin
- * info passed into module instance
- */
-static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
-                                               struct skl_module_pin *m_pin,
-                                               bool is_dynamic, int max_pin)
+static int skl_tplg_fill_pipe_tkn(struct device *dev,
+                       struct skl_pipe *pipe, u32 tkn,
+                       u32 tkn_val)
 {
-       int i;
 
-       for (i = 0; i < max_pin; i++) {
-               m_pin[i].id.module_id = dfw_pin[i].module_id;
-               m_pin[i].id.instance_id = dfw_pin[i].instance_id;
-               m_pin[i].in_use = false;
-               m_pin[i].is_dynamic = is_dynamic;
-               m_pin[i].pin_state = SKL_PIN_UNBIND;
+       switch (tkn) {
+       case SKL_TKN_U32_PIPE_CONN_TYPE:
+               pipe->conn_type = tkn_val;
+               break;
+
+       case SKL_TKN_U32_PIPE_PRIORITY:
+               pipe->pipe_priority = tkn_val;
+               break;
+
+       case SKL_TKN_U32_PIPE_MEM_PGS:
+               pipe->memory_pages = tkn_val;
+               break;
+
+       default:
+               dev_err(dev, "Token not handled %d\n", tkn);
+               return -EINVAL;
        }
+
+       return 0;
 }
 
 /*
- * Add pipeline from topology binary into driver pipeline list
- *
- * If already added we return that instance
- * Otherwise we create a new instance and add into driver list
+ * Add pipeline by parsing the relevant tokens
+ * Return an existing pipe if the pipe already exists.
  */
-static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
-                       struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
+static int skl_tplg_add_pipe(struct device *dev,
+               struct skl_module_cfg *mconfig, struct skl *skl,
+               struct snd_soc_tplg_vendor_value_elem *tkn_elem)
 {
        struct skl_pipeline *ppl;
        struct skl_pipe *pipe;
        struct skl_pipe_params *params;
 
        list_for_each_entry(ppl, &skl->ppl_list, node) {
-               if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
-                       return ppl->pipe;
+               if (ppl->pipe->ppl_id == tkn_elem->value) {
+                       mconfig->pipe = ppl->pipe;
+                       return EEXIST;
+               }
        }
 
        ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
        if (!ppl)
-               return NULL;
+               return -ENOMEM;
 
        pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
        if (!pipe)
-               return NULL;
+               return -ENOMEM;
 
        params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
        if (!params)
-               return NULL;
+               return -ENOMEM;
 
-       pipe->ppl_id = dfw_pipe->pipe_id;
-       pipe->memory_pages = dfw_pipe->memory_pages;
-       pipe->pipe_priority = dfw_pipe->pipe_priority;
-       pipe->conn_type = dfw_pipe->conn_type;
-       pipe->state = SKL_PIPE_INVALID;
        pipe->p_params = params;
+       pipe->ppl_id = tkn_elem->value;
        INIT_LIST_HEAD(&pipe->w_list);
 
        ppl->pipe = pipe;
        list_add(&ppl->node, &skl->ppl_list);
 
-       return ppl->pipe;
+       mconfig->pipe = pipe;
+       mconfig->pipe->state = SKL_PIPE_INVALID;
+
+       return 0;
+}
+
+static int skl_tplg_fill_pin(struct device *dev, u32 tkn,
+                       struct skl_module_pin *m_pin,
+                       int pin_index, u32 value)
+{
+       switch (tkn) {
+       case SKL_TKN_U32_PIN_MOD_ID:
+               m_pin[pin_index].id.module_id = value;
+               break;
+
+       case SKL_TKN_U32_PIN_INST_ID:
+               m_pin[pin_index].id.instance_id = value;
+               break;
+
+       default:
+               dev_err(dev, "%d Not a pin token\n", value);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Parse for pin config specific tokens to fill up the
+ * module private data
+ */
+static int skl_tplg_fill_pins_info(struct device *dev,
+               struct skl_module_cfg *mconfig,
+               struct snd_soc_tplg_vendor_value_elem *tkn_elem,
+               int dir, int pin_count)
+{
+       int ret;
+       struct skl_module_pin *m_pin;
+
+       switch (dir) {
+       case SKL_DIR_IN:
+               m_pin = mconfig->m_in_pin;
+               break;
+
+       case SKL_DIR_OUT:
+               m_pin = mconfig->m_out_pin;
+               break;
+
+       default:
+               dev_err(dev, "Invalid direction value\n");
+               return -EINVAL;
+       }
+
+       ret = skl_tplg_fill_pin(dev, tkn_elem->token,
+                       m_pin, pin_count, tkn_elem->value);
+
+       if (ret < 0)
+               return ret;
+
+       m_pin[pin_count].in_use = false;
+       m_pin[pin_count].pin_state = SKL_PIN_UNBIND;
+
+       return 0;
+}
+
+/*
+ * Fill up input/output module config format based
+ * on the direction
+ */
+static int skl_tplg_fill_fmt(struct device *dev,
+               struct skl_module_cfg *mconfig, u32 tkn,
+               u32 value, u32 dir, u32 pin_count)
+{
+       struct skl_module_fmt *dst_fmt;
+
+       switch (dir) {
+       case SKL_DIR_IN:
+               dst_fmt = mconfig->in_fmt;
+               dst_fmt += pin_count;
+               break;
+
+       case SKL_DIR_OUT:
+               dst_fmt = mconfig->out_fmt;
+               dst_fmt += pin_count;
+               break;
+
+       default:
+               dev_err(dev, "Invalid direction value\n");
+               return -EINVAL;
+       }
+
+       switch (tkn) {
+       case SKL_TKN_U32_FMT_CH:
+               dst_fmt->channels  = value;
+               break;
+
+       case SKL_TKN_U32_FMT_FREQ:
+               dst_fmt->s_freq = value;
+               break;
+
+       case SKL_TKN_U32_FMT_BIT_DEPTH:
+               dst_fmt->bit_depth = value;
+               break;
+
+       case SKL_TKN_U32_FMT_SAMPLE_SIZE:
+               dst_fmt->valid_bit_depth = value;
+               break;
+
+       case SKL_TKN_U32_FMT_CH_CONFIG:
+               dst_fmt->ch_cfg = value;
+               break;
+
+       case SKL_TKN_U32_FMT_INTERLEAVE:
+               dst_fmt->interleaving_style = value;
+               break;
+
+       case SKL_TKN_U32_FMT_SAMPLE_TYPE:
+               dst_fmt->sample_type = value;
+               break;
+
+       case SKL_TKN_U32_FMT_CH_MAP:
+               dst_fmt->ch_map = value;
+               break;
+
+       default:
+               dev_err(dev, "Invalid token %d\n", tkn);
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
-static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
-                               struct skl_dfw_module_fmt *src_fmt,
-                               int pins)
+static int skl_tplg_get_uuid(struct device *dev, struct skl_module_cfg *mconfig,
+             struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn)
+{
+       if (uuid_tkn->token == SKL_TKN_UUID)
+               memcpy(&mconfig->guid, &uuid_tkn->uuid, 16);
+       else {
+               dev_err(dev, "Not an UUID token tkn %d\n", uuid_tkn->token);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void skl_tplg_fill_pin_dynamic_val(
+               struct skl_module_pin *mpin, u32 pin_count, u32 value)
 {
        int i;
 
-       for (i = 0; i < pins; i++) {
-               dst_fmt[i].channels  = src_fmt[i].channels;
-               dst_fmt[i].s_freq = src_fmt[i].freq;
-               dst_fmt[i].bit_depth = src_fmt[i].bit_depth;
-               dst_fmt[i].valid_bit_depth = src_fmt[i].valid_bit_depth;
-               dst_fmt[i].ch_cfg = src_fmt[i].ch_cfg;
-               dst_fmt[i].ch_map = src_fmt[i].ch_map;
-               dst_fmt[i].interleaving_style = src_fmt[i].interleaving_style;
-               dst_fmt[i].sample_type = src_fmt[i].sample_type;
+       for (i = 0; i < pin_count; i++)
+               mpin[i].is_dynamic = value;
+}
+
+/*
+ * Parse tokens to fill up the module private data
+ */
+static int skl_tplg_get_token(struct device *dev,
+               struct snd_soc_tplg_vendor_value_elem *tkn_elem,
+               struct skl *skl, struct skl_module_cfg *mconfig)
+{
+       int tkn_count = 0;
+       int ret;
+       static int is_pipe_exists;
+       static int pin_index, dir;
+
+       if (tkn_elem->token > SKL_TKN_MAX)
+               return -EINVAL;
+
+       switch (tkn_elem->token) {
+       case SKL_TKN_U8_IN_QUEUE_COUNT:
+               mconfig->max_in_queue = tkn_elem->value;
+               mconfig->m_in_pin = devm_kzalloc(dev, mconfig->max_in_queue *
+                                       sizeof(*mconfig->m_in_pin),
+                                       GFP_KERNEL);
+               if (!mconfig->m_in_pin)
+                       return -ENOMEM;
+
+               break;
+
+       case SKL_TKN_U8_OUT_QUEUE_COUNT:
+               mconfig->max_out_queue = tkn_elem->value;
+               mconfig->m_out_pin = devm_kzalloc(dev, mconfig->max_out_queue *
+                                       sizeof(*mconfig->m_out_pin),
+                                       GFP_KERNEL);
+
+               if (!mconfig->m_out_pin)
+                       return -ENOMEM;
+
+               break;
+
+       case SKL_TKN_U8_DYN_IN_PIN:
+               if (!mconfig->m_in_pin)
+                       return -ENOMEM;
+
+               skl_tplg_fill_pin_dynamic_val(mconfig->m_in_pin,
+                       mconfig->max_in_queue, tkn_elem->value);
+
+               break;
+
+       case SKL_TKN_U8_DYN_OUT_PIN:
+               if (!mconfig->m_out_pin)
+                       return -ENOMEM;
+
+               skl_tplg_fill_pin_dynamic_val(mconfig->m_out_pin,
+                       mconfig->max_out_queue, tkn_elem->value);
+
+               break;
+
+       case SKL_TKN_U8_TIME_SLOT:
+               mconfig->time_slot = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U8_CORE_ID:
+               mconfig->core_id = tkn_elem->value;
+
+       case SKL_TKN_U8_MOD_TYPE:
+               mconfig->m_type = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U8_DEV_TYPE:
+               mconfig->dev_type = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U8_HW_CONN_TYPE:
+               mconfig->hw_conn_type = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U16_MOD_INST_ID:
+               mconfig->id.instance_id =
+               tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_MEM_PAGES:
+               mconfig->mem_pages = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_MAX_MCPS:
+               mconfig->mcps = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_OBS:
+               mconfig->obs = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_IBS:
+               mconfig->ibs = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_VBUS_ID:
+               mconfig->vbus_id = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_PARAMS_FIXUP:
+               mconfig->params_fixup = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_CONVERTER:
+               mconfig->converter = tkn_elem->value;
+               break;
+
+       case SKL_TKN_U32_PIPE_ID:
+               ret = skl_tplg_add_pipe(dev,
+                               mconfig, skl, tkn_elem);
+
+               if (ret < 0)
+                       return is_pipe_exists;
+
+               if (ret == EEXIST)
+                       is_pipe_exists = 1;
+
+               break;
+
+       case SKL_TKN_U32_PIPE_CONN_TYPE:
+       case SKL_TKN_U32_PIPE_PRIORITY:
+       case SKL_TKN_U32_PIPE_MEM_PGS:
+               if (is_pipe_exists) {
+                       ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe,
+                                       tkn_elem->token, tkn_elem->value);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               break;
+
+       /*
+        * SKL_TKN_U32_DIR_PIN_COUNT token has the value for both
+        * direction and the pin count. The first four bits represent
+        * direction and next four the pin count.
+        */
+       case SKL_TKN_U32_DIR_PIN_COUNT:
+               dir = tkn_elem->value & SKL_IN_DIR_BIT_MASK;
+               pin_index = (tkn_elem->value &
+                       SKL_PIN_COUNT_MASK) >> 4;
+
+               break;
+
+       case SKL_TKN_U32_FMT_CH:
+       case SKL_TKN_U32_FMT_FREQ:
+       case SKL_TKN_U32_FMT_BIT_DEPTH:
+       case SKL_TKN_U32_FMT_SAMPLE_SIZE:
+       case SKL_TKN_U32_FMT_CH_CONFIG:
+       case SKL_TKN_U32_FMT_INTERLEAVE:
+       case SKL_TKN_U32_FMT_SAMPLE_TYPE:
+       case SKL_TKN_U32_FMT_CH_MAP:
+               ret = skl_tplg_fill_fmt(dev, mconfig, tkn_elem->token,
+                               tkn_elem->value, dir, pin_index);
+
+               if (ret < 0)
+                       return ret;
+
+               break;
+
+       case SKL_TKN_U32_PIN_MOD_ID:
+       case SKL_TKN_U32_PIN_INST_ID:
+               ret = skl_tplg_fill_pins_info(dev,
+                               mconfig, tkn_elem, dir,
+                               pin_index);
+               if (ret < 0)
+                       return ret;
+
+               break;
+
+       case SKL_TKN_U32_CAPS_SIZE:
+               mconfig->formats_config.caps_size =
+                       tkn_elem->value;
+
+               break;
+
+       case SKL_TKN_U32_PROC_DOMAIN:
+               mconfig->domain =
+                       tkn_elem->value;
+
+               break;
+
+       case SKL_TKN_U8_IN_PIN_TYPE:
+       case SKL_TKN_U8_OUT_PIN_TYPE:
+       case SKL_TKN_U8_CONN_TYPE:
+               break;
+
+       default:
+               dev_err(dev, "Token %d not handled\n",
+                               tkn_elem->token);
+               return -EINVAL;
+       }
+
+       tkn_count++;
+
+       return tkn_count;
+}
+
+/*
+ * Parse the vendor array for specific tokens to construct
+ * module private data
+ */
+static int skl_tplg_get_tokens(struct device *dev,
+               char *pvt_data, struct skl *skl,
+               struct skl_module_cfg *mconfig, int block_size)
+{
+       struct snd_soc_tplg_vendor_array *array;
+       struct snd_soc_tplg_vendor_value_elem *tkn_elem;
+       int tkn_count = 0, ret;
+       int off = 0, tuple_size = 0;
+
+       if (block_size <= 0)
+               return -EINVAL;
+
+       while (tuple_size < block_size) {
+               array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off);
+
+               off += array->size;
+
+               switch (array->type) {
+               case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+                       dev_warn(dev, "no string tokens expected for skl tplg\n");
+                       continue;
+
+               case SND_SOC_TPLG_TUPLE_TYPE_UUID:
+                       ret = skl_tplg_get_uuid(dev, mconfig, array->uuid);
+                       if (ret < 0)
+                               return ret;
+
+                       tuple_size += sizeof(*array->uuid);
+
+                       continue;
+
+               default:
+                       tkn_elem = array->value;
+                       tkn_count = 0;
+                       break;
+               }
+
+               while (tkn_count <= (array->num_elems - 1)) {
+                       ret = skl_tplg_get_token(dev, tkn_elem,
+                                       skl, mconfig);
+
+                       if (ret < 0)
+                               return ret;
+
+                       tkn_count = tkn_count + ret;
+                       tkn_elem++;
+               }
+
+               tuple_size += tkn_count * sizeof(*tkn_elem);
+       }
+
+       return 0;
+}
+
+/*
+ * Every data block is preceded by a descriptor to read the number
+ * of data blocks, they type of the block and it's size
+ */
+static int skl_tplg_get_desc_blocks(struct device *dev,
+               struct snd_soc_tplg_vendor_array *array)
+{
+       struct snd_soc_tplg_vendor_value_elem *tkn_elem;
+
+       tkn_elem = array->value;
+
+       switch (tkn_elem->token) {
+       case SKL_TKN_U8_NUM_BLOCKS:
+       case SKL_TKN_U8_BLOCK_TYPE:
+       case SKL_TKN_U16_BLOCK_SIZE:
+               return tkn_elem->value;
+
+       default:
+               dev_err(dev, "Invalid descriptor token %d\n", tkn_elem->token);
+               break;
+       }
+
+       return -EINVAL;
+}
+
+/*
+ * Parse the private data for the token and corresponding value.
+ * The private data can have multiple data blocks. So, a data block
+ * is preceded by a descriptor for number of blocks and a descriptor
+ * for the type and size of the suceeding data block.
+ */
+static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w,
+                               struct skl *skl, struct device *dev,
+                               struct skl_module_cfg *mconfig)
+{
+       struct snd_soc_tplg_vendor_array *array;
+       int num_blocks, block_size = 0, block_type, off = 0;
+       char *data;
+       int ret;
+
+       /* Read the NUM_DATA_BLOCKS descriptor */
+       array = (struct snd_soc_tplg_vendor_array *)tplg_w->priv.data;
+       ret = skl_tplg_get_desc_blocks(dev, array);
+       if (ret < 0)
+               return ret;
+       num_blocks = ret;
+
+       off += array->size;
+       array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data + off);
+
+       /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */
+       while (num_blocks > 0) {
+               ret = skl_tplg_get_desc_blocks(dev, array);
+
+               if (ret < 0)
+                       return ret;
+               block_type = ret;
+               off += array->size;
+
+               array = (struct snd_soc_tplg_vendor_array *)
+                       (tplg_w->priv.data + off);
+
+               ret = skl_tplg_get_desc_blocks(dev, array);
+
+               if (ret < 0)
+                       return ret;
+               block_size = ret;
+               off += array->size;
+
+               array = (struct snd_soc_tplg_vendor_array *)
+                       (tplg_w->priv.data + off);
+
+               data = (tplg_w->priv.data + off);
+
+               if (block_type == SKL_TYPE_TUPLE) {
+                       ret = skl_tplg_get_tokens(dev, data,
+                                       skl, mconfig, block_size);
+
+                       if (ret < 0)
+                               return ret;
+
+                       --num_blocks;
+               } else {
+                       if (mconfig->formats_config.caps_size > 0)
+                               memcpy(mconfig->formats_config.caps, data,
+                                       mconfig->formats_config.caps_size);
+                       --num_blocks;
+               }
        }
+
+       return 0;
 }
 
 static void skl_clear_pin_config(struct snd_soc_platform *platform,
@@ -1606,9 +2132,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
        struct skl *skl = ebus_to_skl(ebus);
        struct hdac_bus *bus = ebus_to_hbus(ebus);
        struct skl_module_cfg *mconfig;
-       struct skl_pipe *pipe;
-       struct skl_dfw_module *dfw_config =
-                               (struct skl_dfw_module *)tplg_w->priv.data;
 
        if (!tplg_w->priv.size)
                goto bind_event;
@@ -1619,76 +2142,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
                return -ENOMEM;
 
        w->priv = mconfig;
-       memcpy(&mconfig->guid, &dfw_config->uuid, 16);
 
-       ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config);
+       /*
+        * module binary can be loaded later, so set it to query when
+        * module is load for a use case
+        */
+       mconfig->id.module_id = -1;
+
+       /* Parse private data for tuples */
+       ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig);
        if (ret < 0)
                return ret;
-
-       mconfig->id.module_id = dfw_config->module_id;
-       mconfig->id.instance_id = dfw_config->instance_id;
-       mconfig->mcps = dfw_config->max_mcps;
-       mconfig->ibs = dfw_config->ibs;
-       mconfig->obs = dfw_config->obs;
-       mconfig->core_id = dfw_config->core_id;
-       mconfig->max_in_queue = dfw_config->max_in_queue;
-       mconfig->max_out_queue = dfw_config->max_out_queue;
-       mconfig->is_loadable = dfw_config->is_loadable;
-       skl_tplg_fill_fmt(mconfig->in_fmt, dfw_config->in_fmt,
-                                               MODULE_MAX_IN_PINS);
-       skl_tplg_fill_fmt(mconfig->out_fmt, dfw_config->out_fmt,
-                                               MODULE_MAX_OUT_PINS);
-
-       mconfig->params_fixup = dfw_config->params_fixup;
-       mconfig->converter = dfw_config->converter;
-       mconfig->m_type = dfw_config->module_type;
-       mconfig->vbus_id = dfw_config->vbus_id;
-       mconfig->mem_pages = dfw_config->mem_pages;
-
-       pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
-       if (pipe)
-               mconfig->pipe = pipe;
-
-       mconfig->dev_type = dfw_config->dev_type;
-       mconfig->hw_conn_type = dfw_config->hw_conn_type;
-       mconfig->time_slot = dfw_config->time_slot;
-       mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
-
-       mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
-                                               sizeof(*mconfig->m_in_pin),
-                                               GFP_KERNEL);
-       if (!mconfig->m_in_pin)
-               return -ENOMEM;
-
-       mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
-                                               sizeof(*mconfig->m_out_pin),
-                                               GFP_KERNEL);
-       if (!mconfig->m_out_pin)
-               return -ENOMEM;
-
-       skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
-                                               dfw_config->is_dynamic_in_pin,
-                                               mconfig->max_in_queue);
-
-       skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
-                                                dfw_config->is_dynamic_out_pin,
-                                                       mconfig->max_out_queue);
-
-
-       if (mconfig->formats_config.caps_size == 0)
-               goto bind_event;
-
-       mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
-                       mconfig->formats_config.caps_size, GFP_KERNEL);
-
-       if (mconfig->formats_config.caps == NULL)
-               return -ENOMEM;
-
-       memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
-                                                dfw_config->caps.caps_size);
-       mconfig->formats_config.param_id = dfw_config->caps.param_id;
-       mconfig->formats_config.set_params = dfw_config->caps.set_params;
-
 bind_event:
        if (tplg_w->event_type == 0) {
                dev_dbg(bus->dev, "ASoC: No event handler required\n");
@@ -1767,11 +2231,229 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
        return 0;
 }
 
+static int skl_tplg_fill_str_mfest_tkn(struct device *dev,
+               struct snd_soc_tplg_vendor_string_elem *str_elem,
+               struct skl_dfw_manifest *minfo)
+{
+       int tkn_count = 0;
+       static int ref_count;
+
+       switch (str_elem->token) {
+       case SKL_TKN_STR_LIB_NAME:
+               if (ref_count > minfo->lib_count - 1) {
+                       ref_count = 0;
+                       return -EINVAL;
+               }
+
+               strncpy(minfo->lib[ref_count].name, str_elem->string,
+                               ARRAY_SIZE(minfo->lib[ref_count].name));
+               ref_count++;
+               tkn_count++;
+               break;
+
+       default:
+               dev_err(dev, "Not a string token %d\n", str_elem->token);
+               break;
+       }
+
+       return tkn_count;
+}
+
+static int skl_tplg_get_str_tkn(struct device *dev,
+               struct snd_soc_tplg_vendor_array *array,
+               struct skl_dfw_manifest *minfo)
+{
+       int tkn_count = 0, ret;
+       struct snd_soc_tplg_vendor_string_elem *str_elem;
+
+       str_elem = (struct snd_soc_tplg_vendor_string_elem *)array->value;
+       while (tkn_count < array->num_elems) {
+               ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, minfo);
+               str_elem++;
+
+               if (ret < 0)
+                       return ret;
+
+               tkn_count = tkn_count + ret;
+       }
+
+       return tkn_count;
+}
+
+static int skl_tplg_get_int_tkn(struct device *dev,
+               struct snd_soc_tplg_vendor_value_elem *tkn_elem,
+               struct skl_dfw_manifest *minfo)
+{
+       int tkn_count = 0;
+
+       switch (tkn_elem->token) {
+       case SKL_TKN_U32_LIB_COUNT:
+               minfo->lib_count = tkn_elem->value;
+               tkn_count++;
+               break;
+
+       default:
+               dev_err(dev, "Not a manifest token %d\n", tkn_elem->token);
+               return -EINVAL;
+       }
+
+       return tkn_count;
+}
+
+/*
+ * Fill the manifest structure by parsing the tokens based on the
+ * type.
+ */
+static int skl_tplg_get_manifest_tkn(struct device *dev,
+               char *pvt_data, struct skl_dfw_manifest *minfo,
+               int block_size)
+{
+       int tkn_count = 0, ret;
+       int off = 0, tuple_size = 0;
+       struct snd_soc_tplg_vendor_array *array;
+       struct snd_soc_tplg_vendor_value_elem *tkn_elem;
+
+       if (block_size <= 0)
+               return -EINVAL;
+
+       while (tuple_size < block_size) {
+               array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off);
+               off += array->size;
+               switch (array->type) {
+               case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+                       ret = skl_tplg_get_str_tkn(dev, array, minfo);
+
+                       if (ret < 0)
+                               return ret;
+                       tkn_count += ret;
+
+                       tuple_size += tkn_count *
+                               sizeof(struct snd_soc_tplg_vendor_string_elem);
+                       continue;
+
+               case SND_SOC_TPLG_TUPLE_TYPE_UUID:
+                       dev_warn(dev, "no uuid tokens for skl tplf manifest\n");
+                       continue;
+
+               default:
+                       tkn_elem = array->value;
+                       tkn_count = 0;
+                       break;
+               }
+
+               while (tkn_count <= array->num_elems - 1) {
+                       ret = skl_tplg_get_int_tkn(dev,
+                                       tkn_elem, minfo);
+                       if (ret < 0)
+                               return ret;
+
+                       tkn_count = tkn_count + ret;
+                       tkn_elem++;
+                       tuple_size += tkn_count *
+                               sizeof(struct snd_soc_tplg_vendor_value_elem);
+                       break;
+               }
+               tkn_count = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * Parse manifest private data for tokens. The private data block is
+ * preceded by descriptors for type and size of data block.
+ */
+static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest,
+                       struct device *dev, struct skl_dfw_manifest *minfo)
+{
+       struct snd_soc_tplg_vendor_array *array;
+       int num_blocks, block_size = 0, block_type, off = 0;
+       char *data;
+       int ret;
+
+       /* Read the NUM_DATA_BLOCKS descriptor */
+       array = (struct snd_soc_tplg_vendor_array *)manifest->priv.data;
+       ret = skl_tplg_get_desc_blocks(dev, array);
+       if (ret < 0)
+               return ret;
+       num_blocks = ret;
+
+       off += array->size;
+       array = (struct snd_soc_tplg_vendor_array *)
+                       (manifest->priv.data + off);
+
+       /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */
+       while (num_blocks > 0) {
+               ret = skl_tplg_get_desc_blocks(dev, array);
+
+               if (ret < 0)
+                       return ret;
+               block_type = ret;
+               off += array->size;
+
+               array = (struct snd_soc_tplg_vendor_array *)
+                       (manifest->priv.data + off);
+
+               ret = skl_tplg_get_desc_blocks(dev, array);
+
+               if (ret < 0)
+                       return ret;
+               block_size = ret;
+               off += array->size;
+
+               array = (struct snd_soc_tplg_vendor_array *)
+                       (manifest->priv.data + off);
+
+               data = (manifest->priv.data + off);
+
+               if (block_type == SKL_TYPE_TUPLE) {
+                       ret = skl_tplg_get_manifest_tkn(dev, data, minfo,
+                                       block_size);
+
+                       if (ret < 0)
+                               return ret;
+
+                       --num_blocks;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int skl_manifest_load(struct snd_soc_component *cmpnt,
+                               struct snd_soc_tplg_manifest *manifest)
+{
+       struct skl_dfw_manifest *minfo;
+       struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct skl *skl = ebus_to_skl(ebus);
+       int ret = 0;
+
+       /* proceed only if we have private data defined */
+       if (manifest->priv.size == 0)
+               return 0;
+
+       minfo = &skl->skl_sst->manifest;
+
+       skl_tplg_get_manifest_data(manifest, bus->dev, minfo);
+
+       if (minfo->lib_count > HDA_MAX_LIB) {
+               dev_err(bus->dev, "Exceeding max Library count. Got:%d\n",
+                                       minfo->lib_count);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
 static struct snd_soc_tplg_ops skl_tplg_ops  = {
        .widget_load = skl_tplg_widget_load,
        .control_load = skl_tplg_control_load,
        .bytes_ext_ops = skl_tlv_ops,
        .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
+       .manifest = skl_manifest_load,
 };
 
 /*
index 22d3ef8..a519360 100644 (file)
@@ -133,7 +133,7 @@ struct skl_i2s_config_blob {
 struct skl_dma_control {
        u32 node_id;
        u32 config_length;
-       u32 config_data[1];
+       u32 config_data[0];
 } __packed;
 
 struct skl_cpr_cfg {
@@ -215,9 +215,20 @@ struct skl_module_fmt {
 
 struct skl_module_cfg;
 
+struct skl_mod_inst_map {
+       u16 mod_id;
+       u16 inst_id;
+};
+
+struct skl_kpb_params {
+       u32 num_modules;
+       struct skl_mod_inst_map map[0];
+};
+
 struct skl_module_inst_id {
-       u32 module_id;
+       int module_id;
        u32 instance_id;
+       int pvt_id;
 };
 
 enum skl_module_pin_state {
index a32e5e9..2f6281e 100644 (file)
@@ -80,7 +80,8 @@ enum skl_module_type {
        SKL_MODULE_TYPE_UPDWMIX,
        SKL_MODULE_TYPE_SRCINT,
        SKL_MODULE_TYPE_ALGO,
-       SKL_MODULE_TYPE_BASE_OUTFMT
+       SKL_MODULE_TYPE_BASE_OUTFMT,
+       SKL_MODULE_TYPE_KPB,
 };
 
 enum skl_core_affinity {
@@ -148,84 +149,34 @@ enum skl_module_param_type {
        SKL_PARAM_BIND
 };
 
-struct skl_dfw_module_pin {
-       u16 module_id;
-       u16 instance_id;
-} __packed;
-
-struct skl_dfw_module_fmt {
-       u32 channels;
-       u32 freq;
-       u32 bit_depth;
-       u32 valid_bit_depth;
-       u32 ch_cfg;
-       u32 interleaving_style;
-       u32 sample_type;
-       u32 ch_map;
-} __packed;
-
-struct skl_dfw_module_caps {
+struct skl_dfw_algo_data {
        u32 set_params:2;
        u32 rsvd:30;
        u32 param_id;
-       u32 caps_size;
-       u32 caps[HDA_SST_CFG_MAX];
-};
-
-struct skl_dfw_pipe {
-       u8 pipe_id;
-       u8 pipe_priority;
-       u16 conn_type:4;
-       u16 rsvd:4;
-       u16 memory_pages:8;
+       u32 max;
+       char params[0];
 } __packed;
 
-struct skl_dfw_module {
-       u8 uuid[16];
-
-       u16 module_id;
-       u16 instance_id;
-       u32 max_mcps;
-       u32 mem_pages;
-       u32 obs;
-       u32 ibs;
-       u32 vbus_id;
-
-       u32 max_in_queue:8;
-       u32 max_out_queue:8;
-       u32 time_slot:8;
-       u32 core_id:4;
-       u32 rsvd1:4;
-
-       u32 module_type:8;
-       u32 conn_type:4;
-       u32 dev_type:4;
-       u32 hw_conn_type:4;
-       u32 rsvd2:12;
-
-       u32 params_fixup:8;
-       u32 converter:8;
-       u32 input_pin_type:1;
-       u32 output_pin_type:1;
-       u32 is_dynamic_in_pin:1;
-       u32 is_dynamic_out_pin:1;
-       u32 is_loadable:1;
-       u32 rsvd3:11;
-
-       struct skl_dfw_pipe pipe;
-       struct skl_dfw_module_fmt in_fmt[MAX_IN_QUEUE];
-       struct skl_dfw_module_fmt out_fmt[MAX_OUT_QUEUE];
-       struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
-       struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
-       struct skl_dfw_module_caps caps;
+#define LIB_NAME_LENGTH        128
+#define HDA_MAX_LIB    16
+
+struct lib_info {
+       char name[LIB_NAME_LENGTH];
 } __packed;
 
-struct skl_dfw_algo_data {
-       u32 set_params:2;
-       u32 rsvd:30;
-       u32 param_id;
-       u32 max;
-       char params[0];
+struct skl_dfw_manifest {
+       u32 lib_count;
+       struct lib_info lib[HDA_MAX_LIB];
 } __packed;
 
+enum skl_tkn_dir {
+       SKL_DIR_IN,
+       SKL_DIR_OUT
+};
+
+enum skl_tuple_type {
+       SKL_TYPE_TUPLE,
+       SKL_TYPE_DATA
+};
+
 #endif
index e3e7641..2989c16 100644 (file)
@@ -587,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
                return -ENXIO;
        }
 
-       snd_hdac_ext_bus_parse_capabilities(ebus);
+       snd_hdac_bus_parse_capabilities(bus);
 
        if (skl_acquire_irq(ebus, 0) < 0)
                return -EBUSY;
@@ -684,7 +684,7 @@ static int skl_probe(struct pci_dev *pci,
        skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
 
        /* check if dsp is there */
-       if (ebus->ppcap) {
+       if (bus->ppcap) {
                err = skl_machine_device_register(skl,
                                  (void *)pci_id->driver_data);
                if (err < 0)
@@ -698,7 +698,7 @@ static int skl_probe(struct pci_dev *pci,
                skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
 
        }
-       if (ebus->mlcap)
+       if (bus->mlcap)
                snd_hdac_ext_bus_get_ml_capabilities(ebus);
 
        /* create device for soc dmic */
index 9064e5b..5d4fbb0 100644 (file)
@@ -105,6 +105,7 @@ struct skl_dsp_ops {
                        int irq, const char *fw_name,
                        struct skl_dsp_loader_ops loader_ops,
                        struct skl_sst **skl_sst);
+       int (*init_fw)(struct device *dev, struct skl_sst *ctx);
        void (*cleanup)(struct device *dev, struct skl_sst *ctx);
 };
 
@@ -123,4 +124,5 @@ int skl_free_dsp(struct skl *skl);
 int skl_suspend_dsp(struct skl *skl);
 int skl_resume_dsp(struct skl *skl);
 void skl_cleanup_resources(struct skl *skl);
+const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id);
 #endif /* __SOUND_SOC_SKL_H */