Merge remote-tracking branches 'asoc/topic/tlv320aic31xx', 'asoc/topic/topology'...
authorMark Brown <broonie@kernel.org>
Mon, 12 Dec 2016 15:53:25 +0000 (15:53 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 12 Dec 2016 15:53:25 +0000 (15:53 +0000)
14 files changed:
Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
include/sound/soc-dai.h
include/sound/soc-topology.h
include/sound/soc.h
include/uapi/sound/asoc.h
sound/soc/codecs/Kconfig
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic31xx.h
sound/soc/codecs/uda1380.c
sound/soc/codecs/uda1380.h
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm8523.c
sound/soc/soc-core.c
sound/soc/soc-topology.c

index 9340d2ddcc54b9f968b594f2857e0ac72dc5d57e..6fbba562eaa7b36a46593160a0512dcc79e58a74 100644 (file)
@@ -12,6 +12,7 @@ Required properties:
     "ti,tlv320aic3120" - TLV320AIC3120 (mono speaker amp, MiniDSP)
     "ti,tlv320aic3111" - TLV320AIC3111 (stereo speaker amp, MiniDSP)
     "ti,tlv320dac3100" - TLV320DAC3100 (no ADC, mono speaker amp, no MiniDSP)
+    "ti,tlv320dac3101" - TLV320DAC3101 (no ADC, stereo speaker amp, no MiniDSP)
 
 - reg - <int> -  I2C slave address
 - HPVDD-supply, SPRVDD-supply, SPLVDD-supply, AVDD-supply, IOVDD-supply,
index b4082454b46f12d43d9a690614ec3a0e5d9e80e2..200e1f04c16612f14be43acfe9569cab2af7e673 100644 (file)
@@ -15,6 +15,7 @@
 
 
 #include <linux/list.h>
+#include <sound/asoc.h>
 
 struct snd_pcm_substream;
 struct snd_soc_dapm_widget;
@@ -26,13 +27,13 @@ struct snd_compr_stream;
  * Describes the physical PCM data formating and clocking. Add new formats
  * to the end.
  */
-#define SND_SOC_DAIFMT_I2S             1 /* I2S mode */
-#define SND_SOC_DAIFMT_RIGHT_J         2 /* Right Justified mode */
-#define SND_SOC_DAIFMT_LEFT_J          3 /* Left Justified mode */
-#define SND_SOC_DAIFMT_DSP_A           4 /* L data MSB after FRM LRC */
-#define SND_SOC_DAIFMT_DSP_B           5 /* L data MSB during FRM LRC */
-#define SND_SOC_DAIFMT_AC97            6 /* AC97 */
-#define SND_SOC_DAIFMT_PDM             7 /* Pulse density modulation */
+#define SND_SOC_DAIFMT_I2S             SND_SOC_DAI_FORMAT_I2S
+#define SND_SOC_DAIFMT_RIGHT_J         SND_SOC_DAI_FORMAT_RIGHT_J
+#define SND_SOC_DAIFMT_LEFT_J          SND_SOC_DAI_FORMAT_LEFT_J
+#define SND_SOC_DAIFMT_DSP_A           SND_SOC_DAI_FORMAT_DSP_A
+#define SND_SOC_DAIFMT_DSP_B           SND_SOC_DAI_FORMAT_DSP_B
+#define SND_SOC_DAIFMT_AC97            SND_SOC_DAI_FORMAT_AC97
+#define SND_SOC_DAIFMT_PDM             SND_SOC_DAI_FORMAT_PDM
 
 /* left and right justified also known as MSB and LSB respectively */
 #define SND_SOC_DAIFMT_MSB             SND_SOC_DAIFMT_LEFT_J
index b897b9d6316119bae25dcf81aac8c21bee1970ab..f9cc7b9271ac438ccfb9ca61c739be10393cc663 100644 (file)
@@ -53,7 +53,7 @@ struct snd_soc_dobj_control {
 
 /* dynamic widget object */
 struct snd_soc_dobj_widget {
-       unsigned int kcontrol_enum:1;   /* this widget is an enum kcontrol */
+       unsigned int kcontrol_type;     /* kcontrol type: mixer, enum, bytes */
 };
 
 /* generic dynamic object - all dynamic objects belong to this struct */
index 42339c0f3532800be447e41e24f4aed089972f4d..2b502f6cc6d036be6e25c6834671be1cde2fd8da 100644 (file)
@@ -1691,6 +1691,9 @@ int snd_soc_add_dai_link(struct snd_soc_card *card,
                                struct snd_soc_dai_link *dai_link);
 void snd_soc_remove_dai_link(struct snd_soc_card *card,
                             struct snd_soc_dai_link *dai_link);
+struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
+                                              int id, const char *name,
+                                              const char *stream_name);
 
 int snd_soc_register_dai(struct snd_soc_component *component,
        struct snd_soc_dai_driver *dai_drv);
index 819d895edfdca7f9b146f187b0636410eb5bde7f..6702533c8bd86b8f65d81c0cb8fe0c73cbfd6e05 100644 (file)
  */
 #define SND_SOC_TPLG_STREAM_CONFIG_MAX  8
 
+/*
+ * Maximum number of physical link's hardware configs
+ */
+#define SND_SOC_TPLG_HW_CONFIG_MAX     8
+
 /* individual kcontrol info types - can be mixed with other types */
 #define SND_SOC_TPLG_CTL_VOLSW         1
 #define SND_SOC_TPLG_CTL_VOLSW_SX      2
@@ -77,7 +82,8 @@
 #define SND_SOC_TPLG_NUM_TEXTS         16
 
 /* ABI version */
-#define SND_SOC_TPLG_ABI_VERSION       0x5
+#define SND_SOC_TPLG_ABI_VERSION       0x5     /* current version */
+#define SND_SOC_TPLG_ABI_VERSION_MIN   0x4     /* oldest version supported */
 
 /* Max size of TLV data */
 #define SND_SOC_TPLG_TLV_SIZE          32
 #define SND_SOC_TPLG_TYPE_CODEC_LINK   9
 #define SND_SOC_TPLG_TYPE_BACKEND_LINK 10
 #define SND_SOC_TPLG_TYPE_PDATA                11
-#define SND_SOC_TPLG_TYPE_BE_DAI       12
-#define SND_SOC_TPLG_TYPE_MAX          SND_SOC_TPLG_TYPE_BE_DAI
+#define SND_SOC_TPLG_TYPE_DAI          12
+#define SND_SOC_TPLG_TYPE_MAX          SND_SOC_TPLG_TYPE_DAI
 
 /* vendor block IDs - please add new vendor types to end */
 #define SND_SOC_TPLG_TYPE_VENDOR_FW    1000
 #define SND_SOC_TPLG_TUPLE_TYPE_WORD   4
 #define SND_SOC_TPLG_TUPLE_TYPE_SHORT  5
 
-/* BE DAI flags */
+/* DAI flags */
 #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES         (1 << 0)
 #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS      (1 << 1)
 #define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS    (1 << 2)
 
+/* DAI physical PCM data formats.
+ * Add new formats to the end of the list.
+ */
+#define SND_SOC_DAI_FORMAT_I2S          1 /* I2S mode */
+#define SND_SOC_DAI_FORMAT_RIGHT_J      2 /* Right Justified mode */
+#define SND_SOC_DAI_FORMAT_LEFT_J       3 /* Left Justified mode */
+#define SND_SOC_DAI_FORMAT_DSP_A        4 /* L data MSB after FRM LRC */
+#define SND_SOC_DAI_FORMAT_DSP_B        5 /* L data MSB during FRM LRC */
+#define SND_SOC_DAI_FORMAT_AC97         6 /* AC97 */
+#define SND_SOC_DAI_FORMAT_PDM          7 /* Pulse density modulation */
+
+/* left and right justified also known as MSB and LSB respectively */
+#define SND_SOC_DAI_FORMAT_MSB          SND_SOC_DAI_FORMAT_LEFT_J
+#define SND_SOC_DAI_FORMAT_LSB          SND_SOC_DAI_FORMAT_RIGHT_J
+
+/* DAI link flags */
+#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES         (1 << 0)
+#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS      (1 << 1)
+#define SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS    (1 << 2)
+#define SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP            (1 << 3)
+
 /*
  * Block Header.
  * This header precedes all object and object arrays below.
@@ -267,6 +294,35 @@ struct snd_soc_tplg_stream {
        __le32 channels;        /* channels */
 } __attribute__((packed));
 
+
+/*
+ * Describes a physical link's runtime supported hardware config,
+ * i.e. hardware audio formats.
+ */
+struct snd_soc_tplg_hw_config {
+       __le32 size;            /* in bytes of this structure */
+       __le32 id;              /* unique ID - - used to match */
+       __le32 fmt;             /* SND_SOC_DAI_FORMAT_ format value */
+       __u8 clock_gated;       /* 1 if clock can be gated to save power */
+       __u8 invert_bclk;       /* 1 for inverted BCLK, 0 for normal */
+       __u8 invert_fsync;      /* 1 for inverted frame clock, 0 for normal */
+       __u8 bclk_master;       /* 1 for master of BCLK, 0 for slave */
+       __u8 fsync_master;      /* 1 for master of FSYNC, 0 for slave */
+       __u8 mclk_direction;    /* 0 for input, 1 for output */
+       __le16 reserved;        /* for 32bit alignment */
+       __le32 mclk_rate;       /* MCLK or SYSCLK freqency in Hz */
+       __le32 bclk_rate;       /* BCLK freqency in Hz */
+       __le32 fsync_rate;      /* frame clock in Hz */
+       __le32 tdm_slots;       /* number of TDM slots in use */
+       __le32 tdm_slot_width;  /* width in bits for each slot */
+       __le32 tx_slots;        /* bit mask for active Tx slots */
+       __le32 rx_slots;        /* bit mask for active Rx slots */
+       __le32 tx_channels;     /* number of Tx channels */
+       __le32 tx_chanmap[SND_SOC_TPLG_MAX_CHAN]; /* array of slot number */
+       __le32 rx_channels;     /* number of Rx channels */
+       __le32 rx_chanmap[SND_SOC_TPLG_MAX_CHAN]; /* array of slot number */
+} __attribute__((packed));
+
 /*
  * Manifest. List totals for each payload type. Not used in parsing, but will
  * be passed to the component driver before any other objects in order for any
@@ -286,7 +342,7 @@ struct snd_soc_tplg_manifest {
        __le32 graph_elems;     /* number of graph elements */
        __le32 pcm_elems;       /* number of PCM elements */
        __le32 dai_link_elems;  /* number of DAI link elements */
-       __le32 be_dai_elems;    /* number of BE DAI elements */
+       __le32 dai_elems;       /* number of physical DAI elements */
        __le32 reserved[20];    /* reserved for new ABI element types */
        struct snd_soc_tplg_private priv;
 } __attribute__((packed));
@@ -434,13 +490,16 @@ struct snd_soc_tplg_pcm {
        struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */
        __le32 num_streams;     /* number of streams */
        struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */
+       __le32 flag_mask;       /* bitmask of flags to configure */
+       __le32 flags;           /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */
+       struct snd_soc_tplg_private priv;
 } __attribute__((packed));
 
 
 /*
- * Describes the BE or CC link runtime supported configs or params
+ * Describes the physical link runtime supported configs or params
  *
- * File block representation for BE/CC link config :-
+ * File block representation for physical link config :-
  * +-----------------------------------+-----+
  * | struct snd_soc_tplg_hdr           |  1  |
  * +-----------------------------------+-----+
@@ -450,21 +509,30 @@ struct snd_soc_tplg_pcm {
 struct snd_soc_tplg_link_config {
        __le32 size;            /* in bytes of this structure */
        __le32 id;              /* unique ID - used to match */
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* name - used to match */
+       char stream_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* stream name - used to match */
        struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */
        __le32 num_streams;     /* number of streams */
+       struct snd_soc_tplg_hw_config hw_config[SND_SOC_TPLG_HW_CONFIG_MAX]; /* hw configs */
+       __le32 num_hw_configs;         /* number of hw configs */
+       __le32 default_hw_config_id;   /* default hw config ID for init */
+       __le32 flag_mask;       /* bitmask of flags to configure */
+       __le32 flags;           /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */
+       struct snd_soc_tplg_private priv;
 } __attribute__((packed));
 
 /*
- * Describes SW/FW specific features of BE DAI.
+ * Describes SW/FW specific features of physical DAI.
+ * It can be used to configure backend DAIs for DPCM.
  *
- * File block representation for BE DAI :-
+ * File block representation for physical DAI :-
  * +-----------------------------------+-----+
  * | struct snd_soc_tplg_hdr           |  1  |
  * +-----------------------------------+-----+
- * | struct snd_soc_tplg_be_dai        |  N  |
+ * | struct snd_soc_tplg_dai           |  N  |
  * +-----------------------------------+-----+
  */
-struct snd_soc_tplg_be_dai {
+struct snd_soc_tplg_dai {
        __le32 size;            /* in bytes of this structure */
        char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* name - used to match */
        __le32 dai_id;          /* unique ID - used to match */
index 23f31e536c119ca3761af02f857a18578910d246..b4f8dc80105c25bd383473dfb0915c6076102a3d 100644 (file)
@@ -897,6 +897,7 @@ config SND_SOC_UDA134X
 
 config SND_SOC_UDA1380
         tristate
+       depends on I2C
 
 config SND_SOC_WL1273
        tristate
index be1a64bfd3202eb1cada4f47b86beb6916305553..f8a90ba8cd718ca43541d097e94e797d5f1ed84a 100644 (file)
@@ -1253,6 +1253,8 @@ static const struct of_device_id tlv320aic31xx_of_match[] = {
        { .compatible = "ti,tlv320aic3110" },
        { .compatible = "ti,tlv320aic3120" },
        { .compatible = "ti,tlv320aic3111" },
+       { .compatible = "ti,tlv320dac3100" },
+       { .compatible = "ti,tlv320dac3101" },
        {},
 };
 MODULE_DEVICE_TABLE(of, tlv320aic31xx_of_match);
@@ -1379,6 +1381,7 @@ static const struct i2c_device_id aic31xx_i2c_id[] = {
        { "tlv320aic3120", AIC3120 },
        { "tlv320aic3111", AIC3111 },
        { "tlv320dac3100", DAC3100 },
+       { "tlv320dac3101", DAC3101 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
index 5acd5b69fb837a622ddd0c745c5d47f6d41564af..730fb2058869978b3f1b5281b123844a44458c42 100644 (file)
@@ -32,6 +32,7 @@ enum aic31xx_type {
        AIC3120 = AIC31XX_MINIDSP_BIT,
        AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
        DAC3100 = DAC31XX_BIT,
+       DAC3101 = DAC31XX_BIT | AIC31XX_STEREO_CLASS_D_BIT,
 };
 
 struct aic31xx_pdata {
index 533e3bb444e4d2ee05ee6772db636539cb726af0..2918fdb95e58581555fe7ef3ae3c6019040b86b3 100644 (file)
@@ -698,25 +698,10 @@ static int uda1380_probe(struct snd_soc_codec *codec)
        codec->hw_write = (hw_write_t)i2c_master_send;
        codec->control_data = uda1380->control_data;
 
-       if (!pdata)
-               return -EINVAL;
-
-       if (gpio_is_valid(pdata->gpio_reset)) {
-               ret = gpio_request_one(pdata->gpio_reset, GPIOF_OUT_INIT_LOW,
-                                      "uda1380 reset");
-               if (ret)
-                       goto err_out;
-       }
-
-       if (gpio_is_valid(pdata->gpio_power)) {
-               ret = gpio_request_one(pdata->gpio_power, GPIOF_OUT_INIT_LOW,
-                                  "uda1380 power");
-               if (ret)
-                       goto err_free_gpio;
-       } else {
+       if (!gpio_is_valid(pdata->gpio_power)) {
                ret = uda1380_reset(codec);
                if (ret)
-                       goto err_free_gpio;
+                       return ret;
        }
 
        INIT_WORK(&uda1380->work, uda1380_flush_work);
@@ -732,29 +717,11 @@ static int uda1380_probe(struct snd_soc_codec *codec)
                break;
        }
 
-       return 0;
-
-err_free_gpio:
-       if (gpio_is_valid(pdata->gpio_reset))
-               gpio_free(pdata->gpio_reset);
-err_out:
-       return ret;
-}
-
-/* power down chip */
-static int uda1380_remove(struct snd_soc_codec *codec)
-{
-       struct uda1380_platform_data *pdata =codec->dev->platform_data;
-
-       gpio_free(pdata->gpio_reset);
-       gpio_free(pdata->gpio_power);
-
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
        .probe =        uda1380_probe,
-       .remove =       uda1380_remove,
        .read =         uda1380_read_reg_cache,
        .write =        uda1380_write,
        .set_bias_level = uda1380_set_bias_level,
@@ -775,18 +742,35 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
        },
 };
 
-#if IS_ENABLED(CONFIG_I2C)
 static int uda1380_i2c_probe(struct i2c_client *i2c,
                             const struct i2c_device_id *id)
 {
+       struct uda1380_platform_data *pdata = i2c->dev.platform_data;
        struct uda1380_priv *uda1380;
        int ret;
 
+       if (!pdata)
+               return -EINVAL;
+
        uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv),
                               GFP_KERNEL);
        if (uda1380 == NULL)
                return -ENOMEM;
 
+       if (gpio_is_valid(pdata->gpio_reset)) {
+               ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_reset,
+                       GPIOF_OUT_INIT_LOW, "uda1380 reset");
+               if (ret)
+                       return ret;
+       }
+
+       if (gpio_is_valid(pdata->gpio_power)) {
+               ret = devm_gpio_request_one(&i2c->dev, pdata->gpio_power,
+                       GPIOF_OUT_INIT_LOW, "uda1380 power");
+               if (ret)
+                       return ret;
+       }
+
        i2c_set_clientdata(i2c, uda1380);
        uda1380->control_data = i2c;
 
@@ -815,27 +799,8 @@ static struct i2c_driver uda1380_i2c_driver = {
        .remove =   uda1380_i2c_remove,
        .id_table = uda1380_i2c_id,
 };
-#endif
 
-static int __init uda1380_modinit(void)
-{
-       int ret = 0;
-#if IS_ENABLED(CONFIG_I2C)
-       ret = i2c_add_driver(&uda1380_i2c_driver);
-       if (ret != 0)
-               pr_err("Failed to register UDA1380 I2C driver: %d\n", ret);
-#endif
-       return ret;
-}
-module_init(uda1380_modinit);
-
-static void __exit uda1380_exit(void)
-{
-#if IS_ENABLED(CONFIG_I2C)
-       i2c_del_driver(&uda1380_i2c_driver);
-#endif
-}
-module_exit(uda1380_exit);
+module_i2c_driver(uda1380_i2c_driver);
 
 MODULE_AUTHOR("Giorgio Padrin");
 MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
index 942e3927c72b11e033a98cda16dbf9b80b9b2dd1..69a326ac3c1a420b55a4d8291e9b5c4399baf0e5 100644 (file)
@@ -72,8 +72,4 @@
 #define R22_SKIP_DCFIL 0x0002
 #define R23_AGC_EN     0x0001
 
-#define UDA1380_DAI_DUPLEX     0 /* playback and capture on single DAI */
-#define UDA1380_DAI_PLAYBACK   1 /* playback DAI */
-#define UDA1380_DAI_CAPTURE    2 /* capture DAI */
-
 #endif /* _UDA1380_H */
index 606bf88abfc444a9036695eebb0e459419041997..d83dab57a1d1f5171afbc9bf6b9d1c2f9c6a40ba 100644 (file)
@@ -999,7 +999,7 @@ static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
 static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
 
-static const char *wm2200_mixer_texts[] = {
+static const char * const wm2200_mixer_texts[] = {
        "None",
        "Tone Generator",
        "AEC Loopback",
@@ -1033,7 +1033,7 @@ static const char *wm2200_mixer_texts[] = {
        "DSP2.6",
 };
 
-static int wm2200_mixer_values[] = {
+static unsigned int wm2200_mixer_values[] = {
        0x00,
        0x04,   /* Tone */
        0x08,   /* AEC */
index deb2e075428ec9e29acdbcb981dc34630b0df0ff..6d0a2723bfde7e06956934d1792f719263353699 100644 (file)
@@ -446,7 +446,6 @@ static const struct regmap_config wm8523_regmap = {
        .volatile_reg = wm8523_volatile_register,
 };
 
-#if IS_ENABLED(CONFIG_I2C)
 static int wm8523_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
@@ -543,29 +542,8 @@ static struct i2c_driver wm8523_i2c_driver = {
        .remove =   wm8523_i2c_remove,
        .id_table = wm8523_i2c_id,
 };
-#endif
 
-static int __init wm8523_modinit(void)
-{
-       int ret;
-#if IS_ENABLED(CONFIG_I2C)
-       ret = i2c_add_driver(&wm8523_i2c_driver);
-       if (ret != 0) {
-               printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
-                      ret);
-       }
-#endif
-       return 0;
-}
-module_init(wm8523_modinit);
-
-static void __exit wm8523_exit(void)
-{
-#if IS_ENABLED(CONFIG_I2C)
-       i2c_del_driver(&wm8523_i2c_driver);
-#endif
-}
-module_exit(wm8523_exit);
+module_i2c_driver(wm8523_i2c_driver);
 
 MODULE_DESCRIPTION("ASoC WM8523 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
index ce8e665a9c894f507882e008557543978316c39d..f1901bb1466ec67b12189713c66ad040f1ae75c7 100644 (file)
@@ -972,6 +972,48 @@ struct snd_soc_dai *snd_soc_find_dai(
 }
 EXPORT_SYMBOL_GPL(snd_soc_find_dai);
 
+
+/**
+ * snd_soc_find_dai_link - Find a DAI link
+ *
+ * @card: soc card
+ * @id: DAI link ID to match
+ * @name: DAI link name to match, optional
+ * @stream name: DAI link stream name to match, optional
+ *
+ * This function will search all existing DAI links of the soc card to
+ * find the link of the same ID. Since DAI links may not have their
+ * unique ID, so name and stream name should also match if being
+ * specified.
+ *
+ * Return: pointer of DAI link, or NULL if not found.
+ */
+struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
+                                              int id, const char *name,
+                                              const char *stream_name)
+{
+       struct snd_soc_dai_link *link, *_link;
+
+       lockdep_assert_held(&client_mutex);
+
+       list_for_each_entry_safe(link, _link, &card->dai_link_list, list) {
+               if (link->id != id)
+                       continue;
+
+               if (name && (!link->name || strcmp(name, link->name)))
+                       continue;
+
+               if (stream_name && (!link->stream_name
+                       || strcmp(stream_name, link->stream_name)))
+                       continue;
+
+               return link;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_find_dai_link);
+
 static bool soc_is_dai_link_bound(struct snd_soc_card *card,
                struct snd_soc_dai_link *dai_link)
 {
index 6b05047a4134da76ff9abe96da16e5f6123d12aa..65670b2b408cca0d4bffb2931cb7443cd4ba75b1 100644 (file)
 #define SOC_TPLG_PASS_GRAPH            5
 #define SOC_TPLG_PASS_PINS             6
 #define SOC_TPLG_PASS_BE_DAI           7
+#define SOC_TPLG_PASS_LINK             8
 
 #define SOC_TPLG_PASS_START    SOC_TPLG_PASS_MANIFEST
-#define SOC_TPLG_PASS_END      SOC_TPLG_PASS_BE_DAI
+#define SOC_TPLG_PASS_END      SOC_TPLG_PASS_LINK
 
+/*
+ * Old version of ABI structs, supported for backward compatibility.
+ */
+
+/* Manifest v4 */
+struct snd_soc_tplg_manifest_v4 {
+       __le32 size;            /* in bytes of this structure */
+       __le32 control_elems;   /* number of control elements */
+       __le32 widget_elems;    /* number of widget elements */
+       __le32 graph_elems;     /* number of graph elements */
+       __le32 pcm_elems;       /* number of PCM elements */
+       __le32 dai_link_elems;  /* number of DAI link elements */
+       struct snd_soc_tplg_private priv;
+} __packed;
+
+/* Stream Capabilities v4 */
+struct snd_soc_tplg_stream_caps_v4 {
+       __le32 size;            /* in bytes of this structure */
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */
+       __le32 rates;           /* supported rates SNDRV_PCM_RATE_* */
+       __le32 rate_min;        /* min rate */
+       __le32 rate_max;        /* max rate */
+       __le32 channels_min;    /* min channels */
+       __le32 channels_max;    /* max channels */
+       __le32 periods_min;     /* min number of periods */
+       __le32 periods_max;     /* max number of periods */
+       __le32 period_size_min; /* min period size bytes */
+       __le32 period_size_max; /* max period size bytes */
+       __le32 buffer_size_min; /* min buffer size bytes */
+       __le32 buffer_size_max; /* max buffer size bytes */
+} __packed;
+
+/* PCM v4 */
+struct snd_soc_tplg_pcm_v4 {
+       __le32 size;            /* in bytes of this structure */
+       char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       __le32 pcm_id;          /* unique ID - used to match with DAI link */
+       __le32 dai_id;          /* unique ID - used to match */
+       __le32 playback;        /* supports playback mode */
+       __le32 capture;         /* supports capture mode */
+       __le32 compress;        /* 1 = compressed; 0 = PCM */
+       struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */
+       __le32 num_streams;     /* number of streams */
+       struct snd_soc_tplg_stream_caps_v4 caps[2]; /* playback and capture for DAI */
+} __packed;
+
+/* Physical link config v4 */
+struct snd_soc_tplg_link_config_v4 {
+       __le32 size;            /* in bytes of this structure */
+       __le32 id;              /* unique ID - used to match */
+       struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */
+       __le32 num_streams;     /* number of streams */
+} __packed;
+
+/* topology context */
 struct soc_tplg {
        const struct firmware *fw;
 
@@ -428,33 +486,41 @@ static void remove_widget(struct snd_soc_component *comp,
                dobj->ops->widget_unload(comp, dobj);
 
        /*
-        * Dynamic Widgets either have 1 enum kcontrol or 1..N mixers.
+        * Dynamic Widgets either have 1..N enum kcontrols or mixers.
         * The enum may either have an array of values or strings.
         */
-       if (dobj->widget.kcontrol_enum) {
+       if (dobj->widget.kcontrol_type == SND_SOC_TPLG_TYPE_ENUM) {
                /* enumerated widget mixer */
-               struct soc_enum *se =
-                       (struct soc_enum *)w->kcontrols[0]->private_value;
+               for (i = 0; i < w->num_kcontrols; i++) {
+                       struct snd_kcontrol *kcontrol = w->kcontrols[i];
+                       struct soc_enum *se =
+                               (struct soc_enum *)kcontrol->private_value;
 
-               snd_ctl_remove(card, w->kcontrols[0]);
+                       snd_ctl_remove(card, kcontrol);
 
-               kfree(se->dobj.control.dvalues);
-               for (i = 0; i < se->items; i++)
-                       kfree(se->dobj.control.dtexts[i]);
+                       kfree(se->dobj.control.dvalues);
+                       for (i = 0; i < se->items; i++)
+                               kfree(se->dobj.control.dtexts[i]);
 
-               kfree(se);
+                       kfree(se);
+               }
                kfree(w->kcontrol_news);
        } else {
-               /* non enumerated widget mixer */
+               /* volume mixer or bytes controls */
                for (i = 0; i < w->num_kcontrols; i++) {
                        struct snd_kcontrol *kcontrol = w->kcontrols[i];
-                       struct soc_mixer_control *sm =
-                       (struct soc_mixer_control *) kcontrol->private_value;
 
-                       kfree(w->kcontrols[i]->tlv.p);
+                       if (dobj->widget.kcontrol_type
+                           == SND_SOC_TPLG_TYPE_MIXER)
+                               kfree(kcontrol->tlv.p);
 
-                       snd_ctl_remove(card, w->kcontrols[i]);
-                       kfree(sm);
+                       snd_ctl_remove(card, kcontrol);
+
+                       /* Private value is used as struct soc_mixer_control
+                        * for volume mixers or soc_bytes_ext for bytes
+                        * controls.
+                        */
+                       kfree((void *)kcontrol->private_value);
                }
                kfree(w->kcontrol_news);
        }
@@ -474,6 +540,7 @@ static void remove_dai(struct snd_soc_component *comp,
        if (dobj->ops && dobj->ops->dai_unload)
                dobj->ops->dai_unload(comp, dobj);
 
+       kfree(dai_drv->name);
        list_del(&dobj->list);
        kfree(dai_drv);
 }
@@ -491,6 +558,10 @@ static void remove_link(struct snd_soc_component *comp,
        if (dobj->ops && dobj->ops->link_unload)
                dobj->ops->link_unload(comp, dobj);
 
+       kfree(link->name);
+       kfree(link->stream_name);
+       kfree(link->cpu_dai_name);
+
        list_del(&dobj->list);
        snd_soc_remove_dai_link(comp->card, link);
        kfree(link);
@@ -1193,98 +1264,105 @@ err:
 }
 
 static struct snd_kcontrol_new *soc_tplg_dapm_widget_denum_create(
-       struct soc_tplg *tplg)
+       struct soc_tplg *tplg, int num_kcontrols)
 {
        struct snd_kcontrol_new *kc;
        struct snd_soc_tplg_enum_control *ec;
        struct soc_enum *se;
-       int i, err;
-
-       ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
-       tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
-               ec->priv.size);
+       int i, j, err;
 
-       /* validate kcontrol */
-       if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
-               SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
-               return NULL;
-
-       kc = kzalloc(sizeof(*kc), GFP_KERNEL);
+       kc = kcalloc(num_kcontrols, sizeof(*kc), GFP_KERNEL);
        if (kc == NULL)
                return NULL;
 
-       se = kzalloc(sizeof(*se), GFP_KERNEL);
-       if (se == NULL)
-               goto err;
+       for (i = 0; i < num_kcontrols; i++) {
+               ec = (struct snd_soc_tplg_enum_control *)tplg->pos;
+               /* validate kcontrol */
+               if (strnlen(ec->hdr.name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
+                           SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+                       return NULL;
 
-       dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
-               ec->hdr.name);
+               se = kzalloc(sizeof(*se), GFP_KERNEL);
+               if (se == NULL)
+                       goto err;
 
-       kc->name = ec->hdr.name;
-       kc->private_value = (long)se;
-       kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       kc->access = ec->hdr.access;
+               dev_dbg(tplg->dev, " adding DAPM widget enum control %s\n",
+                       ec->hdr.name);
+
+               kc[i].name = ec->hdr.name;
+               kc[i].private_value = (long)se;
+               kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               kc[i].access = ec->hdr.access;
 
-       /* we only support FL/FR channel mapping atm */
-       se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
-       se->shift_l = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FL);
-       se->shift_r = tplc_chan_get_shift(tplg, ec->channel, SNDRV_CHMAP_FR);
+               /* we only support FL/FR channel mapping atm */
+               se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+               se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+                                                 SNDRV_CHMAP_FL);
+               se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+                                                 SNDRV_CHMAP_FR);
 
-       se->items = ec->items;
-       se->mask = ec->mask;
-       se->dobj.index = tplg->index;
+               se->items = ec->items;
+               se->mask = ec->mask;
+               se->dobj.index = tplg->index;
 
-       switch (ec->hdr.ops.info) {
-       case SND_SOC_TPLG_CTL_ENUM_VALUE:
-       case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
-               err = soc_tplg_denum_create_values(se, ec);
-               if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: could not create values for %s\n",
-                               ec->hdr.name);
+               switch (ec->hdr.ops.info) {
+               case SND_SOC_TPLG_CTL_ENUM_VALUE:
+               case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
+                       err = soc_tplg_denum_create_values(se, ec);
+                       if (err < 0) {
+                               dev_err(tplg->dev, "ASoC: could not create values for %s\n",
+                                       ec->hdr.name);
+                               goto err_se;
+                       }
+                       /* fall through to create texts */
+               case SND_SOC_TPLG_CTL_ENUM:
+               case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
+               case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
+                       err = soc_tplg_denum_create_texts(se, ec);
+                       if (err < 0) {
+                               dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
+                                       ec->hdr.name);
+                               goto err_se;
+                       }
+                       break;
+               default:
+                       dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
+                               ec->hdr.ops.info, ec->hdr.name);
                        goto err_se;
                }
-               /* fall through to create texts */
-       case SND_SOC_TPLG_CTL_ENUM:
-       case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
-       case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
-               err = soc_tplg_denum_create_texts(se, ec);
+
+               /* map io handlers */
+               err = soc_tplg_kcontrol_bind_io(&ec->hdr, &kc[i], tplg);
+               if (err) {
+                       soc_control_err(tplg, &ec->hdr, ec->hdr.name);
+                       goto err_se;
+               }
+
+               /* pass control to driver for optional further init */
+               err = soc_tplg_init_kcontrol(tplg, &kc[i],
+                       (struct snd_soc_tplg_ctl_hdr *)ec);
                if (err < 0) {
-                       dev_err(tplg->dev, "ASoC: could not create texts for %s\n",
+                       dev_err(tplg->dev, "ASoC: failed to init %s\n",
                                ec->hdr.name);
                        goto err_se;
                }
-               break;
-       default:
-               dev_err(tplg->dev, "ASoC: invalid enum control type %d for %s\n",
-                       ec->hdr.ops.info, ec->hdr.name);
-               goto err_se;
-       }
 
-       /* map io handlers */
-       err = soc_tplg_kcontrol_bind_io(&ec->hdr, kc, tplg);
-       if (err) {
-               soc_control_err(tplg, &ec->hdr, ec->hdr.name);
-               goto err_se;
-       }
-
-       /* pass control to driver for optional further init */
-       err = soc_tplg_init_kcontrol(tplg, kc,
-               (struct snd_soc_tplg_ctl_hdr *)ec);
-       if (err < 0) {
-               dev_err(tplg->dev, "ASoC: failed to init %s\n",
-                       ec->hdr.name);
-               goto err_se;
+               tplg->pos += (sizeof(struct snd_soc_tplg_enum_control) +
+                               ec->priv.size);
        }
 
        return kc;
 
 err_se:
-       /* free values and texts */
-       kfree(se->dobj.control.dvalues);
-       for (i = 0; i < ec->items; i++)
-               kfree(se->dobj.control.dtexts[i]);
+       for (; i >= 0; i--) {
+               /* free values and texts */
+               se = (struct soc_enum *)kc[i].private_value;
+               kfree(se->dobj.control.dvalues);
+               for (j = 0; j < ec->items; j++)
+                       kfree(se->dobj.control.dtexts[j]);
 
-       kfree(se);
+               kfree(se);
+       }
 err:
        kfree(kc);
 
@@ -1366,6 +1444,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
        struct snd_soc_dapm_widget template, *widget;
        struct snd_soc_tplg_ctl_hdr *control_hdr;
        struct snd_soc_card *card = tplg->comp->card;
+       unsigned int kcontrol_type;
        int ret = 0;
 
        if (strnlen(w->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) ==
@@ -1406,6 +1485,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
        tplg->pos +=
                (sizeof(struct snd_soc_tplg_dapm_widget) + w->priv.size);
        if (w->num_kcontrols == 0) {
+               kcontrol_type = 0;
                template.num_kcontrols = 0;
                goto widget;
        }
@@ -1421,6 +1501,7 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
        case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
        case SND_SOC_TPLG_CTL_RANGE:
        case SND_SOC_TPLG_DAPM_CTL_VOLSW:
+               kcontrol_type = SND_SOC_TPLG_TYPE_MIXER;  /* volume mixer */
                template.num_kcontrols = w->num_kcontrols;
                template.kcontrol_news =
                        soc_tplg_dapm_widget_dmixer_create(tplg,
@@ -1435,16 +1516,18 @@ static int soc_tplg_dapm_widget_create(struct soc_tplg *tplg,
        case SND_SOC_TPLG_DAPM_CTL_ENUM_DOUBLE:
        case SND_SOC_TPLG_DAPM_CTL_ENUM_VIRT:
        case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
-               template.dobj.widget.kcontrol_enum = 1;
-               template.num_kcontrols = 1;
+               kcontrol_type = SND_SOC_TPLG_TYPE_ENUM; /* enumerated mixer */
+               template.num_kcontrols = w->num_kcontrols;
                template.kcontrol_news =
-                       soc_tplg_dapm_widget_denum_create(tplg);
+                       soc_tplg_dapm_widget_denum_create(tplg,
+                       template.num_kcontrols);
                if (!template.kcontrol_news) {
                        ret = -ENOMEM;
                        goto hdr_err;
                }
                break;
        case SND_SOC_TPLG_CTL_BYTES:
+               kcontrol_type = SND_SOC_TPLG_TYPE_BYTES; /* bytes control */
                template.num_kcontrols = w->num_kcontrols;
                template.kcontrol_news =
                        soc_tplg_dapm_widget_dbytes_create(tplg,
@@ -1481,6 +1564,7 @@ widget:
        }
 
        widget->dobj.type = SND_SOC_DOBJ_WIDGET;
+       widget->dobj.widget.kcontrol_type = kcontrol_type;
        widget->dobj.ops = tplg->ops;
        widget->dobj.index = tplg->index;
        kfree(template.sname);
@@ -1589,7 +1673,8 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
        if (dai_drv == NULL)
                return -ENOMEM;
 
-       dai_drv->name = pcm->dai_name;
+       if (strlen(pcm->dai_name))
+               dai_drv->name = kstrdup(pcm->dai_name, GFP_KERNEL);
        dai_drv->id = pcm->dai_id;
 
        if (pcm->playback) {
@@ -1621,8 +1706,31 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
        return snd_soc_register_dai(tplg->comp, dai_drv);
 }
 
+static void set_link_flags(struct snd_soc_dai_link *link,
+               unsigned int flag_mask, unsigned int flags)
+{
+       if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES)
+               link->symmetric_rates =
+                       flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES ? 1 : 0;
+
+       if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS)
+               link->symmetric_channels =
+                       flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS ?
+                       1 : 0;
+
+       if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS)
+               link->symmetric_samplebits =
+                       flags & SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS ?
+                       1 : 0;
+
+       if (flag_mask & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP)
+               link->ignore_suspend =
+               flags & SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP ?
+               1 : 0;
+}
+
 /* create the FE DAI link */
-static int soc_tplg_link_create(struct soc_tplg *tplg,
+static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
        struct snd_soc_tplg_pcm *pcm)
 {
        struct snd_soc_dai_link *link;
@@ -1632,11 +1740,15 @@ static int soc_tplg_link_create(struct soc_tplg *tplg,
        if (link == NULL)
                return -ENOMEM;
 
-       link->name = pcm->pcm_name;
-       link->stream_name = pcm->pcm_name;
+       if (strlen(pcm->pcm_name)) {
+               link->name = kstrdup(pcm->pcm_name, GFP_KERNEL);
+               link->stream_name = kstrdup(pcm->pcm_name, GFP_KERNEL);
+       }
        link->id = pcm->pcm_id;
 
-       link->cpu_dai_name = pcm->dai_name;
+       if (strlen(pcm->dai_name))
+               link->cpu_dai_name = kstrdup(pcm->dai_name, GFP_KERNEL);
+
        link->codec_name = "snd-soc-dummy";
        link->codec_dai_name = "snd-soc-dummy-dai";
 
@@ -1644,6 +1756,8 @@ static int soc_tplg_link_create(struct soc_tplg *tplg,
        link->dynamic = 1;
        link->dpcm_playback = pcm->playback;
        link->dpcm_capture = pcm->capture;
+       if (pcm->flag_mask)
+               set_link_flags(link, pcm->flag_mask, pcm->flags);
 
        /* pass control to component driver for optional further init */
        ret = soc_tplg_dai_link_load(tplg, link);
@@ -1672,55 +1786,351 @@ static int soc_tplg_pcm_create(struct soc_tplg *tplg,
        if (ret < 0)
                return ret;
 
-       return  soc_tplg_link_create(tplg, pcm);
+       return  soc_tplg_fe_link_create(tplg, pcm);
+}
+
+/* copy stream caps from the old version 4 of source */
+static void stream_caps_new_ver(struct snd_soc_tplg_stream_caps *dest,
+                               struct snd_soc_tplg_stream_caps_v4 *src)
+{
+       dest->size = sizeof(*dest);
+       memcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+       dest->formats = src->formats;
+       dest->rates = src->rates;
+       dest->rate_min = src->rate_min;
+       dest->rate_max = src->rate_max;
+       dest->channels_min = src->channels_min;
+       dest->channels_max = src->channels_max;
+       dest->periods_min = src->periods_min;
+       dest->periods_max = src->periods_max;
+       dest->period_size_min = src->period_size_min;
+       dest->period_size_max = src->period_size_max;
+       dest->buffer_size_min = src->buffer_size_min;
+       dest->buffer_size_max = src->buffer_size_max;
+}
+
+/**
+ * pcm_new_ver - Create the new version of PCM from the old version.
+ * @tplg: topology context
+ * @src: older version of pcm as a source
+ * @pcm: latest version of pcm created from the source
+ *
+ * Support from vesion 4. User should free the returned pcm manually.
+ */
+static int pcm_new_ver(struct soc_tplg *tplg,
+                      struct snd_soc_tplg_pcm *src,
+                      struct snd_soc_tplg_pcm **pcm)
+{
+       struct snd_soc_tplg_pcm *dest;
+       struct snd_soc_tplg_pcm_v4 *src_v4;
+       int i;
+
+       *pcm = NULL;
+
+       if (src->size != sizeof(*src_v4)) {
+               dev_err(tplg->dev, "ASoC: invalid PCM size\n");
+               return -EINVAL;
+       }
+
+       dev_warn(tplg->dev, "ASoC: old version of PCM\n");
+       src_v4 = (struct snd_soc_tplg_pcm_v4 *)src;
+       dest = kzalloc(sizeof(*dest), GFP_KERNEL);
+       if (!dest)
+               return -ENOMEM;
+
+       dest->size = sizeof(*dest);     /* size of latest abi version */
+       memcpy(dest->pcm_name, src_v4->pcm_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+       memcpy(dest->dai_name, src_v4->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+       dest->pcm_id = src_v4->pcm_id;
+       dest->dai_id = src_v4->dai_id;
+       dest->playback = src_v4->playback;
+       dest->capture = src_v4->capture;
+       dest->compress = src_v4->compress;
+       dest->num_streams = src_v4->num_streams;
+       for (i = 0; i < dest->num_streams; i++)
+               memcpy(&dest->stream[i], &src_v4->stream[i],
+                      sizeof(struct snd_soc_tplg_stream));
+
+       for (i = 0; i < 2; i++)
+               stream_caps_new_ver(&dest->caps[i], &src_v4->caps[i]);
+
+       *pcm = dest;
+       return 0;
 }
 
 static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
        struct snd_soc_tplg_hdr *hdr)
 {
-       struct snd_soc_tplg_pcm *pcm;
+       struct snd_soc_tplg_pcm *pcm, *_pcm;
        int count = hdr->count;
-       int i;
+       int i, err;
+       bool abi_match;
 
        if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
                return 0;
 
+       /* check the element size and count */
+       pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
+       if (pcm->size > sizeof(struct snd_soc_tplg_pcm)
+               || pcm->size < sizeof(struct snd_soc_tplg_pcm_v4)) {
+               dev_err(tplg->dev, "ASoC: invalid size %d for PCM elems\n",
+                       pcm->size);
+               return -EINVAL;
+       }
+
        if (soc_tplg_check_elem_count(tplg,
-               sizeof(struct snd_soc_tplg_pcm), count,
+               pcm->size, count,
                hdr->payload_size, "PCM DAI")) {
                dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n",
                        count);
                return -EINVAL;
        }
 
-       /* create the FE DAIs and DAI links */
-       pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
        for (i = 0; i < count; i++) {
-               if (pcm->size != sizeof(*pcm)) {
-                       dev_err(tplg->dev, "ASoC: invalid pcm size\n");
-                       return -EINVAL;
+               pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
+
+               /* check ABI version by size, create a new version of pcm
+                * if abi not match.
+                */
+               if (pcm->size == sizeof(*pcm)) {
+                       abi_match = true;
+                       _pcm = pcm;
+               } else {
+                       abi_match = false;
+                       err = pcm_new_ver(tplg, pcm, &_pcm);
                }
 
-               soc_tplg_pcm_create(tplg, pcm);
-               pcm++;
+               /* create the FE DAIs and DAI links */
+               soc_tplg_pcm_create(tplg, _pcm);
+
+               /* offset by version-specific struct size and
+                * real priv data size
+                */
+               tplg->pos += pcm->size + _pcm->priv.size;
+
+               if (!abi_match)
+                       kfree(_pcm); /* free the duplicated one */
        }
 
        dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
-       tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count;
 
        return 0;
 }
 
-/* *
- * soc_tplg_be_dai_config - Find and configure an existing BE DAI.
+/**
+ * set_link_hw_format - Set the HW audio format of the physical DAI link.
+ * @tplg: topology context
+ * @cfg: physical link configs.
+ *
+ * Topology context contains a list of supported HW formats (configs) and
+ * a default format ID for the physical link. This function will use this
+ * default ID to choose the HW format to set the link's DAI format for init.
+ */
+static void set_link_hw_format(struct snd_soc_dai_link *link,
+                       struct snd_soc_tplg_link_config *cfg)
+{
+       struct snd_soc_tplg_hw_config *hw_config;
+       unsigned char bclk_master, fsync_master;
+       unsigned char invert_bclk, invert_fsync;
+       int i;
+
+       for (i = 0; i < cfg->num_hw_configs; i++) {
+               hw_config = &cfg->hw_config[i];
+               if (hw_config->id != cfg->default_hw_config_id)
+                       continue;
+
+               link->dai_fmt = hw_config->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+               /* clock signal polarity */
+               invert_bclk = hw_config->invert_bclk;
+               invert_fsync = hw_config->invert_fsync;
+               if (!invert_bclk && !invert_fsync)
+                       link->dai_fmt |= SND_SOC_DAIFMT_NB_NF;
+               else if (!invert_bclk && invert_fsync)
+                       link->dai_fmt |= SND_SOC_DAIFMT_NB_IF;
+               else if (invert_bclk && !invert_fsync)
+                       link->dai_fmt |= SND_SOC_DAIFMT_IB_NF;
+               else
+                       link->dai_fmt |= SND_SOC_DAIFMT_IB_IF;
+
+               /* clock masters */
+               bclk_master = hw_config->bclk_master;
+               fsync_master = hw_config->fsync_master;
+               if (!bclk_master && !fsync_master)
+                       link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+               else if (bclk_master && !fsync_master)
+                       link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
+               else if (!bclk_master && fsync_master)
+                       link->dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
+               else
+                       link->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+       }
+}
+
+/**
+ * link_new_ver - Create a new physical link config from the old
+ * version of source.
+ * @toplogy: topology context
+ * @src: old version of phyical link config as a source
+ * @link: latest version of physical link config created from the source
+ *
+ * Support from vesion 4. User need free the returned link config manually.
+ */
+static int link_new_ver(struct soc_tplg *tplg,
+                       struct snd_soc_tplg_link_config *src,
+                       struct snd_soc_tplg_link_config **link)
+{
+       struct snd_soc_tplg_link_config *dest;
+       struct snd_soc_tplg_link_config_v4 *src_v4;
+       int i;
+
+       *link = NULL;
+
+       if (src->size != sizeof(struct snd_soc_tplg_link_config_v4)) {
+               dev_err(tplg->dev, "ASoC: invalid physical link config size\n");
+               return -EINVAL;
+       }
+
+       dev_warn(tplg->dev, "ASoC: old version of physical link config\n");
+
+       src_v4 = (struct snd_soc_tplg_link_config_v4 *)src;
+       dest = kzalloc(sizeof(*dest), GFP_KERNEL);
+       if (!dest)
+               return -ENOMEM;
+
+       dest->size = sizeof(*dest);
+       dest->id = src_v4->id;
+       dest->num_streams = src_v4->num_streams;
+       for (i = 0; i < dest->num_streams; i++)
+               memcpy(&dest->stream[i], &src_v4->stream[i],
+                      sizeof(struct snd_soc_tplg_stream));
+
+       *link = dest;
+       return 0;
+}
+
+/* Find and configure an existing physical DAI link */
+static int soc_tplg_link_config(struct soc_tplg *tplg,
+       struct snd_soc_tplg_link_config *cfg)
+{
+       struct snd_soc_dai_link *link;
+       const char *name, *stream_name;
+       size_t len;
+       int ret;
+
+       len = strnlen(cfg->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+       if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+               return -EINVAL;
+       else if (len)
+               name = cfg->name;
+       else
+               name = NULL;
+
+       len = strnlen(cfg->stream_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN);
+       if (len == SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+               return -EINVAL;
+       else if (len)
+               stream_name = cfg->stream_name;
+       else
+               stream_name = NULL;
+
+       link = snd_soc_find_dai_link(tplg->comp->card, cfg->id,
+                                    name, stream_name);
+       if (!link) {
+               dev_err(tplg->dev, "ASoC: physical link %s (id %d) not exist\n",
+                       name, cfg->id);
+               return -EINVAL;
+       }
+
+       /* hw format */
+       if (cfg->num_hw_configs)
+               set_link_hw_format(link, cfg);
+
+       /* flags */
+       if (cfg->flag_mask)
+               set_link_flags(link, cfg->flag_mask, cfg->flags);
+
+       /* pass control to component driver for optional further init */
+       ret = soc_tplg_dai_link_load(tplg, link);
+       if (ret < 0) {
+               dev_err(tplg->dev, "ASoC: physical link loading failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+
+/* Load physical link config elements from the topology context */
+static int soc_tplg_link_elems_load(struct soc_tplg *tplg,
+       struct snd_soc_tplg_hdr *hdr)
+{
+       struct snd_soc_tplg_link_config *link, *_link;
+       int count = hdr->count;
+       int i, ret;
+       bool abi_match;
+
+       if (tplg->pass != SOC_TPLG_PASS_LINK) {
+               tplg->pos += hdr->size + hdr->payload_size;
+               return 0;
+       };
+
+       /* check the element size and count */
+       link = (struct snd_soc_tplg_link_config *)tplg->pos;
+       if (link->size > sizeof(struct snd_soc_tplg_link_config)
+               || link->size < sizeof(struct snd_soc_tplg_link_config_v4)) {
+               dev_err(tplg->dev, "ASoC: invalid size %d for physical link elems\n",
+                       link->size);
+               return -EINVAL;
+       }
+
+       if (soc_tplg_check_elem_count(tplg,
+               link->size, count,
+               hdr->payload_size, "physical link config")) {
+               dev_err(tplg->dev, "ASoC: invalid count %d for physical link elems\n",
+                       count);
+               return -EINVAL;
+       }
+
+       /* config physical DAI links */
+       for (i = 0; i < count; i++) {
+               link = (struct snd_soc_tplg_link_config *)tplg->pos;
+               if (link->size == sizeof(*link)) {
+                       abi_match = true;
+                       _link = link;
+               } else {
+                       abi_match = false;
+                       ret = link_new_ver(tplg, link, &_link);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               ret = soc_tplg_link_config(tplg, _link);
+               if (ret < 0)
+                       return ret;
+
+               /* offset by version-specific struct size and
+                * real priv data size
+                */
+               tplg->pos += link->size + _link->priv.size;
+
+               if (!abi_match)
+                       kfree(_link); /* free the duplicated one */
+       }
+
+       return 0;
+}
+
+/**
+ * soc_tplg_dai_config - Find and configure an existing physical DAI.
  * @tplg: topology context
- * @be: topology BE DAI configs.
+ * @d: physical DAI configs.
  *
- * The BE dai should already be registered by the platform driver. The
- * platform driver should specify the BE DAI name and ID for matching.
+ * The physical dai should already be registered by the platform driver.
+ * The platform driver should specify the DAI name and ID for matching.
  */
-static int soc_tplg_be_dai_config(struct soc_tplg *tplg,
-                                 struct snd_soc_tplg_be_dai *be)
+static int soc_tplg_dai_config(struct soc_tplg *tplg,
+                              struct snd_soc_tplg_dai *d)
 {
        struct snd_soc_dai_link_component dai_component = {0};
        struct snd_soc_dai *dai;
@@ -1729,17 +2139,17 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg,
        struct snd_soc_tplg_stream_caps *caps;
        int ret;
 
-       dai_component.dai_name = be->dai_name;
+       dai_component.dai_name = d->dai_name;
        dai = snd_soc_find_dai(&dai_component);
        if (!dai) {
-               dev_err(tplg->dev, "ASoC: BE DAI %s not registered\n",
-                       be->dai_name);
+               dev_err(tplg->dev, "ASoC: physical DAI %s not registered\n",
+                       d->dai_name);
                return -EINVAL;
        }
 
-       if (be->dai_id != dai->id) {
-               dev_err(tplg->dev, "ASoC: BE DAI %s id mismatch\n",
-                       be->dai_name);
+       if (d->dai_id != dai->id) {
+               dev_err(tplg->dev, "ASoC: physical DAI %s id mismatch\n",
+                       d->dai_name);
                return -EINVAL;
        }
 
@@ -1747,20 +2157,20 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg,
        if (!dai_drv)
                return -EINVAL;
 
-       if (be->playback) {
+       if (d->playback) {
                stream = &dai_drv->playback;
-               caps = &be->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
+               caps = &d->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
                set_stream_info(stream, caps);
        }
 
-       if (be->capture) {
+       if (d->capture) {
                stream = &dai_drv->capture;
-               caps = &be->caps[SND_SOC_TPLG_STREAM_CAPTURE];
+               caps = &d->caps[SND_SOC_TPLG_STREAM_CAPTURE];
                set_stream_info(stream, caps);
        }
 
-       if (be->flag_mask)
-               set_dai_flags(dai_drv, be->flag_mask, be->flags);
+       if (d->flag_mask)
+               set_dai_flags(dai_drv, d->flag_mask, d->flags);
 
        /* pass control to component driver for optional further init */
        ret = soc_tplg_dai_load(tplg, dai_drv);
@@ -1772,10 +2182,11 @@ static int soc_tplg_be_dai_config(struct soc_tplg *tplg,
        return 0;
 }
 
-static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg,
-                                     struct snd_soc_tplg_hdr *hdr)
+/* load physical DAI elements */
+static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
+                                  struct snd_soc_tplg_hdr *hdr)
 {
-       struct snd_soc_tplg_be_dai *be;
+       struct snd_soc_tplg_dai *dai;
        int count = hdr->count;
        int i;
 
@@ -1784,41 +2195,95 @@ static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg,
 
        /* config the existing BE DAIs */
        for (i = 0; i < count; i++) {
-               be = (struct snd_soc_tplg_be_dai *)tplg->pos;
-               if (be->size != sizeof(*be)) {
-                       dev_err(tplg->dev, "ASoC: invalid BE DAI size\n");
+               dai = (struct snd_soc_tplg_dai *)tplg->pos;
+               if (dai->size != sizeof(*dai)) {
+                       dev_err(tplg->dev, "ASoC: invalid physical DAI size\n");
                        return -EINVAL;
                }
 
-               soc_tplg_be_dai_config(tplg, be);
-               tplg->pos += (sizeof(*be) + be->priv.size);
+               soc_tplg_dai_config(tplg, dai);
+               tplg->pos += (sizeof(*dai) + dai->priv.size);
        }
 
        dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count);
        return 0;
 }
 
+/**
+ * manifest_new_ver - Create a new version of manifest from the old version
+ * of source.
+ * @toplogy: topology context
+ * @src: old version of manifest as a source
+ * @manifest: latest version of manifest created from the source
+ *
+ * Support from vesion 4. Users need free the returned manifest manually.
+ */
+static int manifest_new_ver(struct soc_tplg *tplg,
+                           struct snd_soc_tplg_manifest *src,
+                           struct snd_soc_tplg_manifest **manifest)
+{
+       struct snd_soc_tplg_manifest *dest;
+       struct snd_soc_tplg_manifest_v4 *src_v4;
+
+       *manifest = NULL;
+
+       if (src->size != sizeof(*src_v4)) {
+               dev_err(tplg->dev, "ASoC: invalid manifest size\n");
+               return -EINVAL;
+       }
+
+       dev_warn(tplg->dev, "ASoC: old version of manifest\n");
+
+       src_v4 = (struct snd_soc_tplg_manifest_v4 *)src;
+       dest = kzalloc(sizeof(*dest) + src_v4->priv.size, GFP_KERNEL);
+       if (!dest)
+               return -ENOMEM;
+
+       dest->size = sizeof(*dest);     /* size of latest abi version */
+       dest->control_elems = src_v4->control_elems;
+       dest->widget_elems = src_v4->widget_elems;
+       dest->graph_elems = src_v4->graph_elems;
+       dest->pcm_elems = src_v4->pcm_elems;
+       dest->dai_link_elems = src_v4->dai_link_elems;
+       dest->priv.size = src_v4->priv.size;
+       if (dest->priv.size)
+               memcpy(dest->priv.data, src_v4->priv.data,
+                      src_v4->priv.size);
+
+       *manifest = dest;
+       return 0;
+}
 
 static int soc_tplg_manifest_load(struct soc_tplg *tplg,
                                  struct snd_soc_tplg_hdr *hdr)
 {
-       struct snd_soc_tplg_manifest *manifest;
+       struct snd_soc_tplg_manifest *manifest, *_manifest;
+       bool abi_match;
+       int err;
 
        if (tplg->pass != SOC_TPLG_PASS_MANIFEST)
                return 0;
 
        manifest = (struct snd_soc_tplg_manifest *)tplg->pos;
-       if (manifest->size != sizeof(*manifest)) {
-               dev_err(tplg->dev, "ASoC: invalid manifest size\n");
-               return -EINVAL;
-       }
 
-       tplg->pos += sizeof(struct snd_soc_tplg_manifest);
+       /* check ABI version by size, create a new manifest if abi not match */
+       if (manifest->size == sizeof(*manifest)) {
+               abi_match = true;
+               _manifest = manifest;
+       } else {
+               abi_match = false;
+               err = manifest_new_ver(tplg, manifest, &_manifest);
+               if (err < 0)
+                       return err;
+       }
 
+       /* pass control to component driver for optional further init */
        if (tplg->comp && tplg->ops && tplg->ops->manifest)
-               return tplg->ops->manifest(tplg->comp, manifest);
+               return tplg->ops->manifest(tplg->comp, _manifest);
+
+       if (!abi_match) /* free the duplicated one */
+               kfree(_manifest);
 
-       dev_err(tplg->dev, "ASoC: Firmware manifest not supported\n");
        return 0;
 }
 
@@ -1854,7 +2319,9 @@ static int soc_valid_header(struct soc_tplg *tplg,
                return -EINVAL;
        }
 
-       if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) {
+       /* Support ABI from version 4 */
+       if (hdr->abi > SND_SOC_TPLG_ABI_VERSION
+               || hdr->abi < SND_SOC_TPLG_ABI_VERSION_MIN) {
                dev_err(tplg->dev,
                        "ASoC: pass %d invalid ABI version got 0x%x need 0x%x at offset 0x%lx size 0x%zx.\n",
                        tplg->pass, hdr->abi,
@@ -1902,8 +2369,12 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
                return soc_tplg_dapm_widget_elems_load(tplg, hdr);
        case SND_SOC_TPLG_TYPE_PCM:
                return soc_tplg_pcm_elems_load(tplg, hdr);
-       case SND_SOC_TPLG_TYPE_BE_DAI:
-               return soc_tplg_be_dai_elems_load(tplg, hdr);
+       case SND_SOC_TPLG_TYPE_DAI:
+               return soc_tplg_dai_elems_load(tplg, hdr);
+       case SND_SOC_TPLG_TYPE_DAI_LINK:
+       case SND_SOC_TPLG_TYPE_BACKEND_LINK:
+               /* physical link configurations */
+               return soc_tplg_link_elems_load(tplg, hdr);
        case SND_SOC_TPLG_TYPE_MANIFEST:
                return soc_tplg_manifest_load(tplg, hdr);
        default: