Merge remote-tracking branches 'asoc/topic/max9867', 'asoc/topic/mtk', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Sun, 19 Feb 2017 16:36:35 +0000 (16:36 +0000)
committerMark Brown <broonie@kernel.org>
Sun, 19 Feb 2017 16:36:35 +0000 (16:36 +0000)
99 files changed:
Documentation/devicetree/bindings/misc/atmel-ssc.txt
Documentation/devicetree/bindings/sound/axentia,tse850-pcm5142.txt
Documentation/devicetree/bindings/sound/es8328.txt
Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
Documentation/devicetree/bindings/sound/nau8540.txt [new file with mode: 0644]
drivers/misc/atmel-ssc.c
include/drm/drm_edid.h
include/linux/atmel-ssc.h
include/sound/dmaengine_pcm.h
include/sound/soc-dai.h
include/sound/soc.h
sound/hda/ext/hdac_ext_stream.c
sound/soc/amd/acp-pcm-dma.c
sound/soc/atmel/tse850-pcm5142.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/adau17x1.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cs47l24.c
sound/soc/codecs/da7218.c
sound/soc/codecs/es8328-i2c.c
sound/soc/codecs/es8328.c
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/hdac_hdmi.h
sound/soc/codecs/hdmi-codec.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max9867.c
sound/soc/codecs/nau8540.c [new file with mode: 0644]
sound/soc/codecs/nau8540.h [new file with mode: 0644]
sound/soc/codecs/pcm3168a.c
sound/soc/codecs/rt298.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5670.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8997.c
sound/soc/codecs/wm8998.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/davinci/davinci-evm.c
sound/soc/dwc/designware_i2s.c
sound/soc/dwc/designware_pcm.c
sound/soc/dwc/local.h
sound/soc/fsl/efika-audio-fabric.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/fsl/mpc5200_psc_ac97.h [deleted file]
sound/soc/img/img-parallel-out.c
sound/soc/intel/Kconfig
sound/soc/intel/Makefile
sound/soc/intel/atom/Makefile
sound/soc/intel/atom/sst-atom-controls.c
sound/soc/intel/atom/sst-mfld-platform-pcm.c
sound/soc/intel/atom/sst/sst_acpi.c
sound/soc/intel/atom/sst/sst_ipc.c
sound/soc/intel/atom/sst/sst_stream.c
sound/soc/intel/boards/broadwell.c
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/boards/bytcr_rt5651.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/skl_nau88l25_max98357a.c
sound/soc/intel/boards/skl_nau88l25_ssm4567.c
sound/soc/intel/boards/skl_rt286.c
sound/soc/intel/common/sst-dsp.c
sound/soc/intel/skylake/bxt-sst.c
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-nhlt.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-sst-dsp.h
sound/soc/intel/skylake/skl-sst-ipc.h
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
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
sound/soc/mediatek/mt8173/mt8173-max98090.c
sound/soc/mxs/mxs-saif.c
sound/soc/omap/mcbsp.h
sound/soc/omap/omap-mcbsp.c
sound/soc/samsung/dmaengine.c
sound/soc/samsung/i2s.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/soc-ac97.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-generic-dmaengine-pcm.c
sound/soc/soc-ops.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/sunxi/sun4i-i2s.c

index efc98ea1f23d28e18f64b0f0331a8b2e3da0c4ff..f8629bb7394578d8f1ae22123ee0da0179137270 100644 (file)
@@ -24,6 +24,8 @@ Optional properties:
        this parameter to choose where the clock from.
      - By default the clock is from TK pin, if the clock from RK pin, this
        property is needed.
+  - #sound-dai-cells: Should contain <0>.
+     - This property makes the SSC into an automatically registered DAI.
 
 Examples:
 - PDC transfer:
index 5b9b38f578bb6d8cf8e64ac8f58b166202686135..fdb25b49251430f23d8efeb0ca43f62493e8b679 100644 (file)
@@ -2,8 +2,7 @@ Devicetree bindings for the Axentia TSE-850 audio complex
 
 Required properties:
   - compatible: "axentia,tse850-pcm5142"
-  - axentia,ssc-controller: The phandle of the atmel SSC controller used as
-    cpu dai.
+  - axentia,cpu-dai: The phandle of the cpu dai.
   - axentia,audio-codec: The phandle of the PCM5142 codec.
   - axentia,add-gpios: gpio specifier that controls the mixer.
   - axentia,loop1-gpios: gpio specifier that controls loop relays on channel 1.
@@ -43,6 +42,12 @@ the PCM5142 codec.
 
 Example:
 
+       &ssc0 {
+               #sound-dai-cells = <0>;
+
+               status = "okay";
+       };
+
        &i2c {
                codec: pcm5142@4c {
                        compatible = "ti,pcm5142";
@@ -77,7 +82,7 @@ Example:
        sound {
                compatible = "axentia,tse850-pcm5142";
 
-               axentia,ssc-controller = <&ssc0>;
+               axentia,cpu-dai = <&ssc0>;
                axentia,audio-codec = <&codec>;
 
                axentia,add-gpios = <&pioA 8 GPIO_ACTIVE_LOW>;
index 30ea8a318ae98070624f754f563e56ef2c2be5cd..33fbf058c997c65f5afa899df614e7900f5bfe69 100644 (file)
@@ -4,7 +4,7 @@ This device supports both I2C and SPI.
 
 Required properties:
 
-  - compatible : "everest,es8328"
+  - compatible  : Should be "everest,es8328" or "everest,es8388"
   - DVDD-supply : Regulator providing digital core supply voltage 1.8 - 3.6V
   - AVDD-supply : Regulator providing analog supply voltage 3.3V
   - PVDD-supply : Regulator providing digital IO supply voltage 1.8 - 3.6V
index 3e623a724e557e895dc5f8efe394fc9c8dbf8c20..9800a560e0c20586e4c7ba4ccbcddab44575b437 100644 (file)
@@ -4,6 +4,7 @@ Required properties:
 - compatible = "mediatek,mt2701-audio";
 - reg: register location and size
 - interrupts: Should contain AFE interrupt
+- power-domains: should define the power domain
 - clock-names: should have these clock names:
                "infra_sys_audio_clk",
                "top_audio_mux1_sel",
@@ -58,6 +59,7 @@ Example:
                      <0 0x112A0000 0 0x20000>;
                interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
                             <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+               power-domains = <&scpsys MT2701_POWER_DOMAIN_IFR_MSC>;
                clocks = <&infracfg CLK_INFRA_AUDIO>,
                         <&topckgen CLK_TOP_AUD_MUX1_SEL>,
                         <&topckgen CLK_TOP_AUD_MUX2_SEL>,
diff --git a/Documentation/devicetree/bindings/sound/nau8540.txt b/Documentation/devicetree/bindings/sound/nau8540.txt
new file mode 100644 (file)
index 0000000..307a765
--- /dev/null
@@ -0,0 +1,16 @@
+NAU85L40 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "nuvoton,nau8540"
+
+  - reg : the I2C address of the device.
+
+Example:
+
+codec: nau8540@1c {
+       compatible = "nuvoton,nau8540";
+       reg = <0x1c>;
+};
index 0516ecda54d35fc6a590750a3f27e76083e80003..b2a0340f277e268739c288e6bb73f62321475c30 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <linux/of.h>
 
+#include "../../sound/soc/atmel/atmel_ssc_dai.h"
+
 /* Serialize access to ssc_list and user count */
 static DEFINE_SPINLOCK(user_lock);
 static LIST_HEAD(ssc_list);
@@ -145,6 +147,49 @@ static inline const struct atmel_ssc_platform_data * __init
                platform_get_device_id(pdev)->driver_data;
 }
 
+#ifdef CONFIG_SND_ATMEL_SOC_SSC
+static int ssc_sound_dai_probe(struct ssc_device *ssc)
+{
+       struct device_node *np = ssc->pdev->dev.of_node;
+       int ret;
+       int id;
+
+       ssc->sound_dai = false;
+
+       if (!of_property_read_bool(np, "#sound-dai-cells"))
+               return 0;
+
+       id = of_alias_get_id(np, "ssc");
+       if (id < 0)
+               return id;
+
+       ret = atmel_ssc_set_audio(id);
+       ssc->sound_dai = !ret;
+
+       return ret;
+}
+
+static void ssc_sound_dai_remove(struct ssc_device *ssc)
+{
+       if (!ssc->sound_dai)
+               return;
+
+       atmel_ssc_put_audio(of_alias_get_id(ssc->pdev->dev.of_node, "ssc"));
+}
+#else
+static inline int ssc_sound_dai_probe(struct ssc_device *ssc)
+{
+       if (of_property_read_bool(ssc->pdev->dev.of_node, "#sound-dai-cells"))
+               return -ENOTSUPP;
+
+       return 0;
+}
+
+static inline void ssc_sound_dai_remove(struct ssc_device *ssc)
+{
+}
+#endif
+
 static int ssc_probe(struct platform_device *pdev)
 {
        struct resource *regs;
@@ -204,6 +249,9 @@ static int ssc_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",
                        ssc->regs, ssc->irq);
 
+       if (ssc_sound_dai_probe(ssc))
+               dev_err(&pdev->dev, "failed to auto-setup ssc for audio\n");
+
        return 0;
 }
 
@@ -211,6 +259,8 @@ static int ssc_remove(struct platform_device *pdev)
 {
        struct ssc_device *ssc = platform_get_drvdata(pdev);
 
+       ssc_sound_dai_remove(ssc);
+
        spin_lock(&user_lock);
        list_del(&ssc->list);
        spin_unlock(&user_lock);
index 38eabf65f19df05a005ad12984f3c628ae40628a..2705a66b770bc907082a5d26998be28c17bfcfaf 100644 (file)
@@ -248,6 +248,7 @@ struct detailed_timing {
 # define DRM_ELD_AUD_SYNCH_DELAY_MAX   0xfa    /* 500 ms */
 
 #define DRM_ELD_SPEAKER                        7
+# define DRM_ELD_SPEAKER_MASK          0x7f
 # define DRM_ELD_SPEAKER_RLRC          (1 << 6)
 # define DRM_ELD_SPEAKER_FLRC          (1 << 5)
 # define DRM_ELD_SPEAKER_RC            (1 << 4)
@@ -413,6 +414,18 @@ static inline int drm_eld_size(const uint8_t *eld)
        return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
 }
 
+/**
+ * drm_eld_get_spk_alloc - Get speaker allocation
+ * @eld: pointer to an ELD memory structure
+ *
+ * The returned value is the speakers mask. User has to use %DRM_ELD_SPEAKER
+ * field definitions to identify speakers.
+ */
+static inline u8 drm_eld_get_spk_alloc(const uint8_t *eld)
+{
+       return eld[DRM_ELD_SPEAKER] & DRM_ELD_SPEAKER_MASK;
+}
+
 /**
  * drm_eld_get_conn_type - Get device type hdmi/dp connected
  * @eld: pointer to an ELD memory structure
index 7c0f6549898bd29cacebaadd08596f6f4d2ab778..fdb545101ede3c0518361ebb6bc1f755f6a3779b 100644 (file)
@@ -20,6 +20,7 @@ struct ssc_device {
        int                     user;
        int                     irq;
        bool                    clk_from_rk_pin;
+       bool                    sound_dai;
 };
 
 struct ssc_device * __must_check ssc_request(unsigned int ssc_num);
index 1c8f9e1ef2a5a511a2df3bdbb8e8c6444b58f309..67be2445941a613bcc74918d8964bfe3aef907ff 100644 (file)
@@ -71,6 +71,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
  * @slave_id: Slave requester id for the DMA channel.
  * @filter_data: Custom DMA channel filter data, this will usually be used when
  * requesting the DMA channel.
+ * @chan_name: Custom channel name to use when requesting DMA channel.
  * @fifo_size: FIFO size of the DAI controller in bytes
  * @flags: PCM_DAI flags, only SND_DMAENGINE_PCM_DAI_FLAG_PACK for now
  */
@@ -80,6 +81,7 @@ struct snd_dmaengine_dai_dma_data {
        u32 maxburst;
        unsigned int slave_id;
        void *filter_data;
+       const char *chan_name;
        unsigned int fifo_size;
        unsigned int flags;
 };
@@ -105,6 +107,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
  * playback.
  */
 #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)
+/*
+ * The PCM streams have custom channel names specified.
+ */
+#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4)
 
 /**
  * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM
index 200e1f04c16612f14be43acfe9569cab2af7e673..58acd00cae1967bc6ebb7851aab9b12aea49f782 100644 (file)
@@ -256,6 +256,9 @@ struct snd_soc_dai_driver {
        int (*resume)(struct snd_soc_dai *dai);
        /* compress dai */
        int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
+       /* Optional Callback used at pcm creation*/
+       int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
+                      struct snd_soc_dai *dai);
        /* DAI is also used for the control bus */
        bool bus_control;
 
index b86168a21d56c7fff1d59dfacbef93049bde888e..cdfb55f7aedeac2b0ee6a7f5f7b4bf81da123ddf 100644 (file)
@@ -497,6 +497,8 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream);
 int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
        unsigned int dai_fmt);
 
+int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour);
+
 /* Utility functions to get clock rates from various things */
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
 int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params);
@@ -507,9 +509,6 @@ int snd_soc_params_to_bclk(struct snd_pcm_hw_params *parms);
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
        const struct snd_pcm_hardware *hw);
 
-int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
-               int cmd, struct snd_soc_platform *platform);
-
 int soc_dai_hw_params(struct snd_pcm_substream *substream,
                      struct snd_pcm_hw_params *params,
                      struct snd_soc_dai *dai);
@@ -785,6 +784,10 @@ struct snd_soc_component_driver {
        int (*suspend)(struct snd_soc_component *);
        int (*resume)(struct snd_soc_component *);
 
+       /* pcm creation and destruction */
+       int (*pcm_new)(struct snd_soc_pcm_runtime *);
+       void (*pcm_free)(struct snd_pcm *);
+
        /* DT */
        int (*of_xlate_dai_name)(struct snd_soc_component *component,
                                 struct of_phandle_args *args,
@@ -859,6 +862,8 @@ struct snd_soc_component {
        void (*remove)(struct snd_soc_component *);
        int (*suspend)(struct snd_soc_component *);
        int (*resume)(struct snd_soc_component *);
+       int (*pcm_new)(struct snd_soc_pcm_runtime *);
+       void (*pcm_free)(struct snd_pcm *);
 
        /* machine specific init */
        int (*init)(struct snd_soc_component *component);
@@ -941,20 +946,11 @@ struct snd_soc_platform_driver {
        int (*pcm_new)(struct snd_soc_pcm_runtime *);
        void (*pcm_free)(struct snd_pcm *);
 
-       /*
-        * For platform caused delay reporting.
-        * Optional.
-        */
-       snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
-               struct snd_soc_dai *);
-
        /* platform stream pcm ops */
        const struct snd_pcm_ops *ops;
 
        /* platform stream compress ops */
        const struct snd_compr_ops *compr_ops;
-
-       int (*bespoke_trigger)(struct snd_pcm_substream *, int);
 };
 
 struct snd_soc_dai_link_component {
@@ -1099,6 +1095,8 @@ struct snd_soc_card {
        const char *name;
        const char *long_name;
        const char *driver_name;
+       char dmi_longname[80];
+
        struct device *dev;
        struct snd_card *snd_card;
        struct module *owner;
@@ -1647,37 +1645,21 @@ static inline struct snd_soc_platform *snd_soc_kcontrol_platform(
 int snd_soc_util_init(void);
 void snd_soc_util_exit(void);
 
-#define snd_soc_of_parse_card_name(card, propname) \
-       snd_soc_of_parse_card_name_from_node(card, NULL, propname)
-int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card,
-                                        struct device_node *np,
-                                        const char *propname);
-#define snd_soc_of_parse_audio_simple_widgets(card, propname)\
-       snd_soc_of_parse_audio_simple_widgets_from_node(card, NULL, propname)
-int snd_soc_of_parse_audio_simple_widgets_from_node(struct snd_soc_card *card,
-                                                   struct device_node *np,
-                                                   const char *propname);
-
+int snd_soc_of_parse_card_name(struct snd_soc_card *card,
+                              const char *propname);
+int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
+                                         const char *propname);
 int snd_soc_of_parse_tdm_slot(struct device_node *np,
                              unsigned int *tx_mask,
                              unsigned int *rx_mask,
                              unsigned int *slots,
                              unsigned int *slot_width);
-#define snd_soc_of_parse_audio_prefix(card, codec_conf, of_node, propname) \
-       snd_soc_of_parse_audio_prefix_from_node(card, NULL, codec_conf, \
-                                               of_node, propname)
-void snd_soc_of_parse_audio_prefix_from_node(struct snd_soc_card *card,
-                                  struct device_node *np,
+void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,
                                   struct snd_soc_codec_conf *codec_conf,
                                   struct device_node *of_node,
                                   const char *propname);
-
-#define snd_soc_of_parse_audio_routing(card, propname) \
-       snd_soc_of_parse_audio_routing_from_node(card, NULL, propname)
-int snd_soc_of_parse_audio_routing_from_node(struct snd_soc_card *card,
-                                            struct device_node *np,
-                                            const char *propname);
-
+int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
+                                  const char *propname);
 unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
                                     const char *prefix,
                                     struct device_node **bitclkmaster,
index 3be051ab55335746d57d46aa1445fb42f5eafce2..c96d7a7a36af033b0c8241e20538e7722828bc0e 100644 (file)
@@ -128,14 +128,17 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
 {
        struct hdac_stream *hstream = &stream->hstream;
        struct hdac_bus *bus = &ebus->bus;
+       u32 val;
+       int mask = AZX_PPCTL_PROCEN(hstream->index);
 
        spin_lock_irq(&bus->reg_lock);
-       if (decouple)
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0,
-                               AZX_PPCTL_PROCEN(hstream->index));
-       else
-               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
-                                       AZX_PPCTL_PROCEN(hstream->index), 0);
+       val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
+
+       if (decouple && !val)
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask);
+       else if (!decouple && val)
+               snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
+
        stream->decoupled = decouple;
        spin_unlock_irq(&bus->reg_lock);
 }
index 504c7cd7f58a335b4c5c35c1cccb36f5d4536c22..818b052377f3f2e656d77ad12e835df299663872 100644 (file)
@@ -670,13 +670,10 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream,
 {
        int status;
        uint64_t size;
-       struct snd_dma_buffer *dma_buffer;
        struct page *pg;
        struct snd_pcm_runtime *runtime;
        struct audio_substream_data *rtd;
 
-       dma_buffer = &substream->dma_buffer;
-
        runtime = substream->runtime;
        rtd = runtime->private_data;
 
index ac6a814c8ecf3658ffb20e1aa5bc7dce63b039c9..a72c7d642026e131a27b8082f5a4b1d27d61e72f 100644 (file)
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 
-#include "atmel_ssc_dai.h"
-
 struct tse850_priv {
-       int ssc_id;
-
        struct gpio_desc *add;
        struct gpio_desc *loop1;
        struct gpio_desc *loop2;
@@ -329,23 +325,20 @@ static int tse850_dt_init(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
        struct device_node *codec_np, *cpu_np;
-       struct snd_soc_card *card = &tse850_card;
        struct snd_soc_dai_link *dailink = &tse850_dailink;
-       struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
 
        if (!np) {
                dev_err(&pdev->dev, "only device tree supported\n");
                return -EINVAL;
        }
 
-       cpu_np = of_parse_phandle(np, "axentia,ssc-controller", 0);
+       cpu_np = of_parse_phandle(np, "axentia,cpu-dai", 0);
        if (!cpu_np) {
-               dev_err(&pdev->dev, "failed to get dai and pcm info\n");
+               dev_err(&pdev->dev, "failed to get cpu dai\n");
                return -EINVAL;
        }
        dailink->cpu_of_node = cpu_np;
        dailink->platform_of_node = cpu_np;
-       tse850->ssc_id = of_alias_get_id(cpu_np, "ssc");
        of_node_put(cpu_np);
 
        codec_np = of_parse_phandle(np, "axentia,audio-codec", 0);
@@ -415,23 +408,14 @@ static int tse850_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = atmel_ssc_set_audio(tse850->ssc_id);
-       if (ret != 0) {
-               dev_err(dev,
-                       "failed to set SSC %d for audio\n", tse850->ssc_id);
-               goto err_disable_ana;
-       }
-
        ret = snd_soc_register_card(card);
        if (ret) {
                dev_err(dev, "snd_soc_register_card failed\n");
-               goto err_put_audio;
+               goto err_disable_ana;
        }
 
        return 0;
 
-err_put_audio:
-       atmel_ssc_put_audio(tse850->ssc_id);
 err_disable_ana:
        regulator_disable(tse850->ana);
        return ret;
@@ -443,7 +427,6 @@ static int tse850_remove(struct platform_device *pdev)
        struct tse850_priv *tse850 = snd_soc_card_get_drvdata(card);
 
        snd_soc_unregister_card(card);
-       atmel_ssc_put_audio(tse850->ssc_id);
        regulator_disable(tse850->ana);
 
        return 0;
index 9e1718a8cb1ce20a0fd68e2cbe797653b39aadeb..b456d31f7fc1bf2adf82028a68ed6bad0c585fe9 100644 (file)
@@ -45,7 +45,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_ALC5623 if I2C
        select SND_SOC_ALC5632 if I2C
        select SND_SOC_BT_SCO
-       select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
+       select SND_SOC_CQ0093VC
        select SND_SOC_CS35L32 if I2C
        select SND_SOC_CS35L33 if I2C
        select SND_SOC_CS35L34 if I2C
@@ -95,6 +95,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_MC13783 if MFD_MC13XXX
        select SND_SOC_ML26124 if I2C
+       select SND_SOC_NAU8540 if I2C
        select SND_SOC_NAU8810 if I2C
        select SND_SOC_NAU8825 if I2C
        select SND_SOC_HDMI_CODEC
@@ -525,14 +526,16 @@ config SND_SOC_HDMI_CODEC
        select HDMI
 
 config SND_SOC_ES8328
-       tristate "Everest Semi ES8328 CODEC"
+       tristate
 
 config SND_SOC_ES8328_I2C
-       tristate
+       tristate "Everest Semi ES8328 CODEC (I2C)"
+       depends on I2C
        select SND_SOC_ES8328
 
 config SND_SOC_ES8328_SPI
-       tristate
+       tristate "Everest Semi ES8328 CODEC (SPI)"
+       depends on SPI_MASTER
        select SND_SOC_ES8328
 
 config SND_SOC_GTM601
@@ -1105,6 +1108,10 @@ config SND_SOC_MC13783
 config SND_SOC_ML26124
        tristate
 
+config SND_SOC_NAU8540
+       tristate "Nuvoton Technology Corporation NAU85L40 CODEC"
+       depends on I2C
+
 config SND_SOC_NAU8810
        tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
        depends on I2C
index 7e1dad79610b39a6d5b83865c7022f09d53d3705..0a7f4ffe0d785cef79d5310785d6aaa08172f30d 100644 (file)
@@ -90,6 +90,7 @@ snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
 snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o
 snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
+snd-soc-nau8540-objs := nau8540.o
 snd-soc-nau8810-objs := nau8810.o
 snd-soc-nau8825-objs := nau8825.o
 snd-soc-hdmi-codec-objs := hdmi-codec.o
@@ -318,6 +319,7 @@ obj-$(CONFIG_SND_SOC_MC13783)       += snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)  += snd-soc-ml26124.o
 obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
 obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
+obj-$(CONFIG_SND_SOC_NAU8540)   += snd-soc-nau8540.o
 obj-$(CONFIG_SND_SOC_NAU8810)   += snd-soc-nau8810.o
 obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
 obj-$(CONFIG_SND_SOC_HDMI_CODEC)       += snd-soc-hdmi-codec.o
index b36511d965c8202c547a9aaef2827273acc83c28..2c1bd27638648796dd89e117613578655429232d 100644 (file)
@@ -65,7 +65,6 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct adau *adau = snd_soc_codec_get_drvdata(codec);
-       int ret;
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
                adau->pll_regs[5] = 1;
@@ -78,7 +77,7 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
        }
 
        /* The PLL register is 6 bytes long and can only be written at once. */
-       ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+       regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
                        adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
index 2609f95b7d197946c5449610c28287920f6b6c10..23ab9646c3514c1c21adbd0166e93eb4eab4b360 100644 (file)
@@ -189,7 +189,7 @@ static int ak4642_lout_event(struct snd_soc_dapm_widget *w,
        case SND_SOC_DAPM_POST_PMU:
        case SND_SOC_DAPM_POST_PMD:
                /* Power save mode OFF */
-               mdelay(300);
+               msleep(300);
                snd_soc_update_bits(codec, SG_SL2, LOPS, 0);
                break;
        }
index 56707860657cca129df30ed0d64d4081efdedbbf..1822e3b3de805986001f8a037fb0b91f1f83d8c0 100644 (file)
@@ -192,6 +192,7 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 #define ARIZONA_DSP_ROUTES(name) \
        { name, NULL, name " Preloader"}, \
        { name " Preloader", NULL, "SYSCLK" }, \
+       { name " Preload", NULL, name " Preloader"}, \
        { name, NULL, name " Aux 1" }, \
        { name, NULL, name " Aux 2" }, \
        { name, NULL, name " Aux 3" }, \
index 73559ae864b656aa224564a089bd336fd9b39dc4..47e6fddef92b8e7af04e88de54ba04f0409736f6 100644 (file)
@@ -173,6 +173,9 @@ SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
 SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
 SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
 
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+
 ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE),
@@ -1121,7 +1124,10 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
 
        priv->core.arizona->dapm = dapm;
 
-       arizona_init_spk(codec);
+       ret = arizona_init_spk(codec);
+       if (ret < 0)
+               return ret;
+
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
        arizona_init_notifiers(codec);
index c69e97654fc63e7b9f9a06776daeaba199a8b952..d256ebf9e309de299b0711688366c5c5cdb74274 100644 (file)
@@ -1634,7 +1634,8 @@ static const struct snd_soc_dapm_widget da7218_dapm_widgets[] = {
                            SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
 
        /* DAI */
-       SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7218_DAI_TDM_CTRL,
+                            DA7218_DAI_OE_SHIFT, DA7218_NO_INVERT),
        SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
 
        /* Output Mixers */
index 2d05b5d3a6ce7fdc3531f5c322853dbb1a0ca63e..318ab28c5351fcd198603b71b427e6972bbc43c5 100644 (file)
 
 static const struct i2c_device_id es8328_id[] = {
        { "es8328", 0 },
+       { "es8388", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, es8328_id);
 
 static const struct of_device_id es8328_of_match[] = {
        { .compatible = "everest,es8328", },
+       { .compatible = "everest,es8388", },
        { }
 };
 MODULE_DEVICE_TABLE(of, es8328_of_match);
index 37722194b107e8a733bb124be480ff46b63749ef..3f84fbd071e275e52d8c004ae10d7474035c1fc7 100644 (file)
@@ -589,9 +589,21 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
        u8 dac_mode = 0;
        u8 adc_mode = 0;
 
-       /* set master/slave audio interface */
-       if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* Master serial port mode, with BCLK generated automatically */
+               snd_soc_update_bits(codec, ES8328_MASTERMODE,
+                                   ES8328_MASTERMODE_MSC,
+                                   ES8328_MASTERMODE_MSC);
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* Slave serial port mode */
+               snd_soc_update_bits(codec, ES8328_MASTERMODE,
+                                   ES8328_MASTERMODE_MSC, 0);
+               break;
+       default:
                return -EINVAL;
+       }
 
        /* interface format */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -620,10 +632,6 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
        snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
                        ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
 
-       /* Master serial port mode, with BCLK generated automatically */
-       snd_soc_update_bits(codec, ES8328_MASTERMODE,
-                       ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
-
        return 0;
 }
 
index 0c6228a0bf950fcce779491baca38381e7d24e69..78fca8acd3ec0a2209876bf5b20a1363a03c6336 100644 (file)
 #define HDA_MAX_CONNECTIONS     32
 
 #define HDA_MAX_CVTS           3
+#define HDA_MAX_PORTS          3
 
 #define ELD_MAX_SIZE    256
 #define ELD_FIXED_BYTES        20
 
+#define ELD_VER_CEA_861D 2
+#define ELD_VER_PARTIAL 31
+#define ELD_MAX_MNL     16
+
 struct hdac_hdmi_cvt_params {
        unsigned int channels_min;
        unsigned int channels_max;
@@ -77,43 +82,180 @@ struct hdac_hdmi_eld {
 struct hdac_hdmi_pin {
        struct list_head head;
        hda_nid_t nid;
+       bool mst_capable;
+       struct hdac_hdmi_port *ports;
+       int num_ports;
+       struct hdac_ext_device *edev;
+};
+
+struct hdac_hdmi_port {
+       struct list_head head;
+       int id;
+       struct hdac_hdmi_pin *pin;
        int num_mux_nids;
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
        struct hdac_hdmi_eld eld;
-       struct hdac_ext_device *edev;
-       int repoll_count;
-       struct delayed_work work;
-       struct mutex lock;
-       bool chmap_set;
-       unsigned char chmap[8]; /* ALSA API channel-map */
-       int channels; /* current number of channels */
+       const char *jack_pin;
+       struct snd_soc_dapm_context *dapm;
+       const char *output_pin;
 };
 
 struct hdac_hdmi_pcm {
        struct list_head head;
        int pcm_id;
-       struct hdac_hdmi_pin *pin;
+       struct list_head port_list;
        struct hdac_hdmi_cvt *cvt;
-       struct snd_jack *jack;
+       struct snd_soc_jack *jack;
+       int stream_tag;
+       int channels;
+       int format;
+       bool chmap_set;
+       unsigned char chmap[8]; /* ALSA API channel-map */
+       struct mutex lock;
+       int jack_event;
 };
 
-struct hdac_hdmi_dai_pin_map {
+struct hdac_hdmi_dai_port_map {
        int dai_id;
-       struct hdac_hdmi_pin *pin;
+       struct hdac_hdmi_port *port;
        struct hdac_hdmi_cvt *cvt;
 };
 
 struct hdac_hdmi_priv {
-       struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS];
+       struct hdac_hdmi_dai_port_map dai_map[HDA_MAX_CVTS];
        struct list_head pin_list;
        struct list_head cvt_list;
        struct list_head pcm_list;
        int num_pin;
        int num_cvt;
+       int num_ports;
        struct mutex pin_mutex;
        struct hdac_chmap chmap;
 };
 
+static struct hdac_hdmi_pcm *
+hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
+                          struct hdac_hdmi_cvt *cvt)
+{
+       struct hdac_hdmi_pcm *pcm = NULL;
+
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->cvt == cvt)
+                       break;
+       }
+
+       return pcm;
+}
+
+static void hdac_hdmi_jack_report(struct hdac_hdmi_pcm *pcm,
+               struct hdac_hdmi_port *port, bool is_connect)
+{
+       struct hdac_ext_device *edev = port->pin->edev;
+
+       if (is_connect)
+               snd_soc_dapm_enable_pin(port->dapm, port->jack_pin);
+       else
+               snd_soc_dapm_disable_pin(port->dapm, port->jack_pin);
+
+       if (is_connect) {
+               /*
+                * Report Jack connect event when a device is connected
+                * for the first time where same PCM is attached to multiple
+                * ports.
+                */
+               if (pcm->jack_event == 0) {
+                       dev_dbg(&edev->hdac.dev,
+                                       "jack report for pcm=%d\n",
+                                       pcm->pcm_id);
+                       snd_soc_jack_report(pcm->jack, SND_JACK_AVOUT,
+                                               SND_JACK_AVOUT);
+               }
+               pcm->jack_event++;
+       } else {
+               /*
+                * Report Jack disconnect event when a device is disconnected
+                * is the only last connected device when same PCM is attached
+                * to multiple ports.
+                */
+               if (pcm->jack_event == 1)
+                       snd_soc_jack_report(pcm->jack, 0, SND_JACK_AVOUT);
+               if (pcm->jack_event > 0)
+                       pcm->jack_event--;
+       }
+
+       snd_soc_dapm_sync(port->dapm);
+}
+
+/* MST supported verbs */
+/*
+ * Get the no devices that can be connected to a port on the Pin widget.
+ */
+static int hdac_hdmi_get_port_len(struct hdac_ext_device *hdac, hda_nid_t nid)
+{
+       unsigned int caps;
+       unsigned int type, param;
+
+       caps = get_wcaps(&hdac->hdac, nid);
+       type = get_wcaps_type(caps);
+
+       if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN))
+               return 0;
+
+       param = snd_hdac_read_parm_uncached(&hdac->hdac, nid,
+                                       AC_PAR_DEVLIST_LEN);
+       if (param == -1)
+               return param;
+
+       return param & AC_DEV_LIST_LEN_MASK;
+}
+
+/*
+ * Get the port entry select on the pin. Return the port entry
+ * id selected on the pin. Return 0 means the first port entry
+ * is selected or MST is not supported.
+ */
+static int hdac_hdmi_port_select_get(struct hdac_ext_device *hdac,
+                                       struct hdac_hdmi_port *port)
+{
+       return snd_hdac_codec_read(&hdac->hdac, port->pin->nid,
+                               0, AC_VERB_GET_DEVICE_SEL, 0);
+}
+
+/*
+ * Sets the selected port entry for the configuring Pin widget verb.
+ * returns error if port set is not equal to port get otherwise success
+ */
+static int hdac_hdmi_port_select_set(struct hdac_ext_device *hdac,
+                                       struct hdac_hdmi_port *port)
+{
+       int num_ports;
+
+       if (!port->pin->mst_capable)
+               return 0;
+
+       /* AC_PAR_DEVLIST_LEN is 0 based. */
+       num_ports = hdac_hdmi_get_port_len(hdac, port->pin->nid);
+
+       if (num_ports < 0)
+               return -EIO;
+       /*
+        * Device List Length is a 0 based integer value indicating the
+        * number of sink device that a MST Pin Widget can support.
+        */
+       if (num_ports + 1  < port->id)
+               return 0;
+
+       snd_hdac_codec_write(&hdac->hdac, port->pin->nid, 0,
+                       AC_VERB_SET_DEVICE_SEL, port->id);
+
+       if (port->id != hdac_hdmi_port_select_get(hdac, port))
+               return -EIO;
+
+       dev_dbg(&hdac->hdac.dev, "Selected the port=%d\n", port->id);
+
+       return 0;
+}
+
 static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
                                                int pcm_idx)
 {
@@ -173,99 +315,6 @@ format_constraint:
 
 }
 
- /* HDMI ELD routines */
-static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
-                               hda_nid_t nid, int byte_index)
-{
-       unsigned int val;
-
-       val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
-                                                       byte_index);
-
-       dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
-                                       byte_index, val);
-
-       return val;
-}
-
-static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
-{
-       return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
-                                                AC_DIPSIZE_ELD_BUF);
-}
-
-/*
- * This function queries the ELD size and ELD data and fills in the buffer
- * passed by user
- */
-static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
-                            unsigned char *buf, int *eld_size)
-{
-       int i, size, ret = 0;
-
-       /*
-        * ELD size is initialized to zero in caller function. If no errors and
-        * ELD is valid, actual eld_size is assigned.
-        */
-
-       size = hdac_hdmi_get_eld_size(codec, nid);
-       if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
-               dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
-               return -ERANGE;
-       }
-
-       /* set ELD buffer */
-       for (i = 0; i < size; i++) {
-               unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
-               /*
-                * Graphics driver might be writing to ELD buffer right now.
-                * Just abort. The caller will repoll after a while.
-                */
-               if (!(val & AC_ELDD_ELD_VALID)) {
-                       dev_err(&codec->dev,
-                               "HDMI: invalid ELD data byte %d\n", i);
-                       ret = -EINVAL;
-                       goto error;
-               }
-               val &= AC_ELDD_ELD_DATA;
-               /*
-                * The first byte cannot be zero. This can happen on some DVI
-                * connections. Some Intel chips may also need some 250ms delay
-                * to return non-zero ELD data, even when the graphics driver
-                * correctly writes ELD content before setting ELD_valid bit.
-                */
-               if (!val && !i) {
-                       dev_err(&codec->dev, "HDMI: 0 ELD data\n");
-                       ret = -EINVAL;
-                       goto error;
-               }
-               buf[i] = val;
-       }
-
-       *eld_size = size;
-error:
-       return ret;
-}
-
-static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
-                               hda_nid_t cvt_nid, hda_nid_t pin_nid,
-                               u32 stream_tag, int format)
-{
-       unsigned int val;
-
-       dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
-                       cvt_nid, pin_nid, stream_tag, format);
-
-       val = (stream_tag << 4);
-
-       snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-                               AC_VERB_SET_CHANNEL_STREAMID, val);
-       snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-                               AC_VERB_SET_STREAM_FORMAT, format);
-
-       return 0;
-}
-
 static void
 hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
                                int packet_index, int byte_index)
@@ -291,13 +340,14 @@ struct dp_audio_infoframe {
 };
 
 static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
-                               hda_nid_t cvt_nid, hda_nid_t pin_nid)
+                  struct hdac_hdmi_pcm *pcm, struct hdac_hdmi_port *port)
 {
        uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
        struct hdmi_audio_infoframe frame;
+       struct hdac_hdmi_pin *pin = port->pin;
        struct dp_audio_infoframe dp_ai;
        struct hdac_hdmi_priv *hdmi = hdac->private_data;
-       struct hdac_hdmi_pin *pin;
+       struct hdac_hdmi_cvt *cvt = pcm->cvt;
        u8 *dip;
        int ret;
        int i;
@@ -305,21 +355,16 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
        u8 conn_type;
        int channels, ca;
 
-       list_for_each_entry(pin, &hdmi->pin_list, head) {
-               if (pin->nid == pin_nid)
-                       break;
-       }
-
-       ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc,
-                       pin->channels, pin->chmap_set, true, pin->chmap);
+       ca = snd_hdac_channel_allocation(&hdac->hdac, port->eld.info.spk_alloc,
+                       pcm->channels, pcm->chmap_set, true, pcm->chmap);
 
        channels = snd_hdac_get_active_channels(ca);
-       hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels);
+       hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt->nid, channels);
 
        snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
-                               pin->channels, pin->chmap, pin->chmap_set);
+                               pcm->channels, pcm->chmap, pcm->chmap_set);
 
-       eld_buf = pin->eld.eld_buffer;
+       eld_buf = port->eld.eld_buffer;
        conn_type = drm_eld_get_conn_type(eld_buf);
 
        switch (conn_type) {
@@ -353,75 +398,50 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
        }
 
        /* stop infoframe transmission */
-       hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
-       snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+       hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0);
+       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
                        AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_DISABLE);
 
 
        /*  Fill infoframe. Index auto-incremented */
-       hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
+       hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0);
        if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
                for (i = 0; i < sizeof(buffer); i++)
-                       snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+                       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
                                AC_VERB_SET_HDMI_DIP_DATA, buffer[i]);
        } else {
                for (i = 0; i < sizeof(dp_ai); i++)
-                       snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+                       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
                                AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
        }
 
        /* Start infoframe */
-       hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
-       snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+       hdac_hdmi_set_dip_index(hdac, pin->nid, 0x0, 0x0);
+       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
                        AC_VERB_SET_HDMI_DIP_XMIT, AC_DIPXMIT_BEST);
 
        return 0;
 }
 
-static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
-               struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
+static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai,
+               unsigned int tx_mask, unsigned int rx_mask,
+               int slots, int slot_width)
 {
-       /* Power up pin widget */
-       if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid,
-                                               pwr_state))
-               snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
-                       AC_VERB_SET_POWER_STATE, pwr_state);
-
-       /* Power up converter */
-       if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
-                                               pwr_state))
-               snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                       AC_VERB_SET_POWER_STATE, pwr_state);
-}
+       struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_dai_port_map *dai_map;
+       struct hdac_hdmi_pcm *pcm;
 
-static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
-{
-       struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
-       struct hdac_hdmi_priv *hdmi = hdac->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map;
-       struct hdac_hdmi_pin *pin;
-       struct hdac_ext_dma_params *dd;
-       int ret;
+       dev_dbg(&edev->hdac.dev, "%s: strm_tag: %d\n", __func__, tx_mask);
 
        dai_map = &hdmi->dai_map[dai->id];
-       pin = dai_map->pin;
-
-       dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
-       dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
-                       dd->stream_tag, dd->format);
 
-       mutex_lock(&pin->lock);
-       pin->channels = substream->runtime->channels;
+       pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
 
-       ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
-                                               dai_map->pin->nid);
-       mutex_unlock(&pin->lock);
-       if (ret < 0)
-               return ret;
+       if (pcm)
+               pcm->stream_tag = (tx_mask << 4);
 
-       return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
-                       dai_map->pin->nid, dd->stream_tag, dd->format);
+       return 0;
 }
 
 static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
@@ -429,101 +449,41 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
 {
        struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
        struct hdac_hdmi_priv *hdmi = hdac->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map;
-       struct hdac_hdmi_pin *pin;
-       struct hdac_ext_dma_params *dd;
+       struct hdac_hdmi_dai_port_map *dai_map;
+       struct hdac_hdmi_port *port;
+       struct hdac_hdmi_pcm *pcm;
+       int format;
 
        dai_map = &hdmi->dai_map[dai->id];
-       pin = dai_map->pin;
+       port = dai_map->port;
 
-       if (!pin)
+       if (!port)
                return -ENODEV;
 
-       if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) {
-               dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n",
-                                                               pin->nid);
+       if ((!port->eld.monitor_present) || (!port->eld.eld_valid)) {
+               dev_err(&hdac->hdac.dev,
+                       "device is not configured for this pin:port%d:%d\n",
+                                       port->pin->nid, port->id);
                return -ENODEV;
        }
 
-       dd = snd_soc_dai_get_dma_data(dai, substream);
-       if (!dd) {
-               dd = kzalloc(sizeof(*dd), GFP_KERNEL);
-               if (!dd)
-                       return -ENOMEM;
-       }
-
-       dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
+       format = snd_hdac_calc_stream_format(params_rate(hparams),
                        params_channels(hparams), params_format(hparams),
                        24, 0);
 
-       snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
-
-       return 0;
-}
-
-static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
-               struct snd_soc_dai *dai)
-{
-       struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
-       struct hdac_ext_dma_params *dd;
-       struct hdac_hdmi_priv *hdmi = edev->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map;
-
-       dai_map = &hdmi->dai_map[dai->id];
-
-       dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
-
-       if (dd) {
-               snd_soc_dai_set_dma_data(dai, substream, NULL);
-               kfree(dd);
-       }
-
-       return 0;
-}
-
-static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
-               struct hdac_hdmi_dai_pin_map *dai_map)
-{
-       /* Enable transmission */
-       snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                       AC_VERB_SET_DIGI_CONVERT_1, 1);
-
-       /* Category Code (CC) to zero */
-       snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                       AC_VERB_SET_DIGI_CONVERT_2, 0);
-}
-
-static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
-               struct hdac_hdmi_dai_pin_map *dai_map)
-{
-       int mux_idx;
-       struct hdac_hdmi_pin *pin = dai_map->pin;
-
-       for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
-               if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
-                       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
-                                       AC_VERB_SET_CONNECT_SEL, mux_idx);
-                       break;
-               }
-       }
-
-       if (mux_idx == pin->num_mux_nids)
+       pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
+       if (!pcm)
                return -EIO;
 
-       /* Enable out path for this pin widget */
-       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-
-       hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
-
-       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
-                       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+       pcm->format = format;
+       pcm->channels = params_channels(hparams);
 
        return 0;
 }
 
-static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
-                                       struct hdac_hdmi_pin *pin)
+static int hdac_hdmi_query_port_connlist(struct hdac_ext_device *hdac,
+                                       struct hdac_hdmi_pin *pin,
+                                       struct hdac_hdmi_port *port)
 {
        if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
                dev_warn(&hdac->hdac.dev,
@@ -532,51 +492,60 @@ static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
                return -EINVAL;
        }
 
-       pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
-                       pin->mux_nids, HDA_MAX_CONNECTIONS);
-       if (pin->num_mux_nids == 0)
-               dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n",
-                                                               pin->nid);
+       if (hdac_hdmi_port_select_set(hdac, port) < 0)
+               return -EIO;
 
-       dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n",
-                       pin->num_mux_nids, pin->nid);
+       port->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
+                       port->mux_nids, HDA_MAX_CONNECTIONS);
+       if (port->num_mux_nids == 0)
+               dev_warn(&hdac->hdac.dev,
+                       "No connections found for pin:port %d:%d\n",
+                                               pin->nid, port->id);
+
+       dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin:port %d:%d\n",
+                       port->num_mux_nids, pin->nid, port->id);
 
-       return pin->num_mux_nids;
+       return port->num_mux_nids;
 }
 
 /*
- * Query pcm list and return pin widget to which stream is routed.
+ * Query pcm list and return port to which stream is routed.
  *
- * Also query connection list of the pin, to validate the cvt to pin map.
+ * Also query connection list of the pin, to validate the cvt to port map.
  *
- * Same stream rendering to multiple pins simultaneously can be done
- * possibly, but not supported for now in driver. So return the first pin
+ * Same stream rendering to multiple ports simultaneously can be done
+ * possibly, but not supported for now in driver. So return the first port
  * connected.
  */
-static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
+static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
                        struct hdac_ext_device *edev,
                        struct hdac_hdmi_priv *hdmi,
                        struct hdac_hdmi_cvt *cvt)
 {
        struct hdac_hdmi_pcm *pcm;
-       struct hdac_hdmi_pin *pin = NULL;
+       struct hdac_hdmi_port *port = NULL;
        int ret, i;
 
        list_for_each_entry(pcm, &hdmi->pcm_list, head) {
                if (pcm->cvt == cvt) {
-                       pin = pcm->pin;
-                       break;
-               }
-       }
-
-       if (pin) {
-               ret = hdac_hdmi_query_pin_connlist(edev, pin);
-               if (ret < 0)
-                       return NULL;
-
-               for (i = 0; i < pin->num_mux_nids; i++) {
-                       if (pin->mux_nids[i] == cvt->nid)
-                               return pin;
+                       if (list_empty(&pcm->port_list))
+                               continue;
+
+                       list_for_each_entry(port, &pcm->port_list, head) {
+                               mutex_lock(&pcm->lock);
+                               ret = hdac_hdmi_query_port_connlist(edev,
+                                                       port->pin, port);
+                               mutex_unlock(&pcm->lock);
+                               if (ret < 0)
+                                       continue;
+
+                               for (i = 0; i < port->num_mux_nids; i++) {
+                                       if (port->mux_nids[i] == cvt->nid &&
+                                               port->eld.monitor_present &&
+                                               port->eld.eld_valid)
+                                               return port;
+                               }
+                       }
                }
        }
 
@@ -593,67 +562,42 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
 {
        struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
        struct hdac_hdmi_priv *hdmi = hdac->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map;
+       struct hdac_hdmi_dai_port_map *dai_map;
        struct hdac_hdmi_cvt *cvt;
-       struct hdac_hdmi_pin *pin;
+       struct hdac_hdmi_port *port;
        int ret;
 
        dai_map = &hdmi->dai_map[dai->id];
 
        cvt = dai_map->cvt;
-       pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
+       port = hdac_hdmi_get_port_from_cvt(hdac, hdmi, cvt);
 
        /*
         * To make PA and other userland happy.
         * userland scans devices so returning error does not help.
         */
-       if (!pin)
+       if (!port)
                return 0;
-
-       if ((!pin->eld.monitor_present) ||
-                       (!pin->eld.eld_valid)) {
+       if ((!port->eld.monitor_present) ||
+                       (!port->eld.eld_valid)) {
 
                dev_warn(&hdac->hdac.dev,
-                       "Failed: monitor present? %d ELD valid?: %d for pin: %d\n",
-                       pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
+                       "Failed: present?:%d ELD valid?:%d pin:port: %d:%d\n",
+                       port->eld.monitor_present, port->eld.eld_valid,
+                       port->pin->nid, port->id);
 
                return 0;
        }
 
-       dai_map->pin = pin;
-
-       hdac_hdmi_enable_cvt(hdac, dai_map);
-       ret = hdac_hdmi_enable_pin(hdac, dai_map);
-       if (ret < 0)
-               return ret;
+       dai_map->port = port;
 
        ret = hdac_hdmi_eld_limit_formats(substream->runtime,
-                               pin->eld.eld_buffer);
+                               port->eld.eld_buffer);
        if (ret < 0)
                return ret;
 
        return snd_pcm_hw_constraint_eld(substream->runtime,
-                               pin->eld.eld_buffer);
-}
-
-static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
-               struct snd_soc_dai *dai)
-{
-       struct hdac_hdmi_dai_pin_map *dai_map;
-       struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
-       struct hdac_hdmi_priv *hdmi = hdac->private_data;
-       int ret;
-
-       dai_map = &hdmi->dai_map[dai->id];
-       if (cmd == SNDRV_PCM_TRIGGER_RESUME) {
-               ret = hdac_hdmi_enable_pin(hdac, dai_map);
-               if (ret < 0)
-                       return ret;
-
-               return hdac_hdmi_playback_prepare(substream, dai);
-       }
-
-       return 0;
+                               port->eld.eld_buffer);
 }
 
 static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
@@ -661,29 +605,23 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
 {
        struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
        struct hdac_hdmi_priv *hdmi = hdac->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map;
+       struct hdac_hdmi_dai_port_map *dai_map;
+       struct hdac_hdmi_pcm *pcm;
 
        dai_map = &hdmi->dai_map[dai->id];
 
-       if (dai_map->pin) {
-               snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
-                               AC_VERB_SET_CHANNEL_STREAMID, 0);
-               snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
-                               AC_VERB_SET_STREAM_FORMAT, 0);
-
-               hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
-
-               snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
-                       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
-
-               mutex_lock(&dai_map->pin->lock);
-               dai_map->pin->chmap_set = false;
-               memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap));
-               dai_map->pin->channels = 0;
-               mutex_unlock(&dai_map->pin->lock);
+       pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
 
-               dai_map->pin = NULL;
+       if (pcm) {
+               mutex_lock(&pcm->lock);
+               pcm->chmap_set = false;
+               memset(pcm->chmap, 0, sizeof(pcm->chmap));
+               pcm->channels = 0;
+               mutex_unlock(&pcm->lock);
        }
+
+       if (dai_map->port)
+               dai_map->port = NULL;
 }
 
 static int
@@ -716,10 +654,11 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
 }
 
 static int hdac_hdmi_fill_widget_info(struct device *dev,
-                               struct snd_soc_dapm_widget *w,
-                               enum snd_soc_dapm_type id, void *priv,
-                               const char *wname, const char *stream,
-                               struct snd_kcontrol_new *wc, int numkc)
+               struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
+               void *priv, const char *wname, const char *stream,
+               struct snd_kcontrol_new *wc, int numkc,
+               int (*event)(struct snd_soc_dapm_widget *,
+               struct snd_kcontrol *, int), unsigned short event_flags)
 {
        w->id = id;
        w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
@@ -732,6 +671,8 @@ static int hdac_hdmi_fill_widget_info(struct device *dev,
        w->kcontrol_news = wc;
        w->num_kcontrols = numkc;
        w->priv = priv;
+       w->event = event;
+       w->event_flags = event_flags;
 
        return 0;
 }
@@ -748,30 +689,175 @@ static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
 }
 
 static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
-                                       struct hdac_hdmi_pin *pin)
+                                       struct hdac_hdmi_port *port)
 {
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm = NULL;
+       struct hdac_hdmi_port *p;
 
        list_for_each_entry(pcm, &hdmi->pcm_list, head) {
-               if (pcm->pin == pin)
-                       return pcm;
+               if (list_empty(&pcm->port_list))
+                       continue;
+
+               list_for_each_entry(p, &pcm->port_list, head) {
+                       if (p->id == port->id && port->pin == p->pin)
+                               return pcm;
+               }
        }
 
        return NULL;
 }
 
+static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
+                            hda_nid_t nid, unsigned int pwr_state)
+{
+       if (get_wcaps(&edev->hdac, nid) & AC_WCAP_POWER) {
+               if (!snd_hdac_check_power_state(&edev->hdac, nid, pwr_state))
+                       snd_hdac_codec_write(&edev->hdac, nid, 0,
+                               AC_VERB_SET_POWER_STATE, pwr_state);
+       }
+}
+
+static void hdac_hdmi_set_amp(struct hdac_ext_device *edev,
+                                  hda_nid_t nid, int val)
+{
+       if (get_wcaps(&edev->hdac, nid) & AC_WCAP_OUT_AMP)
+               snd_hdac_codec_write(&edev->hdac, nid, 0,
+                                       AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+
+static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kc, int event)
+{
+       struct hdac_hdmi_port *port = w->priv;
+       struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+       struct hdac_hdmi_pcm *pcm;
+
+       dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
+                       __func__, w->name, event);
+
+       pcm = hdac_hdmi_get_pcm(edev, port);
+       if (!pcm)
+               return -EIO;
+
+       /* set the device if pin is mst_capable */
+       if (hdac_hdmi_port_select_set(edev, port) < 0)
+               return -EIO;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D0);
+
+               /* Enable out path for this pin widget */
+               snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+               hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_UNMUTE);
+
+               return hdac_hdmi_setup_audio_infoframe(edev, pcm, port);
+
+       case SND_SOC_DAPM_POST_PMD:
+               hdac_hdmi_set_amp(edev, port->pin->nid, AMP_OUT_MUTE);
+
+               /* Disable out path for this pin widget */
+               snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+
+               hdac_hdmi_set_power_state(edev, port->pin->nid, AC_PWRST_D3);
+               break;
+
+       }
+
+       return 0;
+}
+
+static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kc, int event)
+{
+       struct hdac_hdmi_cvt *cvt = w->priv;
+       struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm;
+
+       dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
+                       __func__, w->name, event);
+
+       pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt);
+       if (!pcm)
+               return -EIO;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0);
+
+               /* Enable transmission */
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                       AC_VERB_SET_DIGI_CONVERT_1, 1);
+
+               /* Category Code (CC) to zero */
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                       AC_VERB_SET_DIGI_CONVERT_2, 0);
+
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_STREAM_FORMAT, pcm->format);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_CHANNEL_STREAMID, 0);
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_STREAM_FORMAT, 0);
+
+               hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3);
+               break;
+
+       }
+
+       return 0;
+}
+
+static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kc, int event)
+{
+       struct hdac_hdmi_port *port = w->priv;
+       struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+       int mux_idx;
+
+       dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
+                       __func__, w->name, event);
+
+       if (!kc)
+               kc  = w->kcontrols[0];
+
+       mux_idx = dapm_kcontrol_get_value(kc);
+
+       /* set the device if pin is mst_capable */
+       if (hdac_hdmi_port_select_set(edev, port) < 0)
+               return -EIO;
+
+       if (mux_idx > 0) {
+               snd_hdac_codec_write(&edev->hdac, port->pin->nid, 0,
+                       AC_VERB_SET_CONNECT_SEL, (mux_idx - 1));
+       }
+
+       return 0;
+}
+
 /*
  * Based on user selection, map the PINs with the PCMs.
  */
-static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
+static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
        int ret;
+       struct hdac_hdmi_port *p, *p_next;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
        struct snd_soc_dapm_context *dapm = w->dapm;
-       struct hdac_hdmi_pin *pin = w->priv;
+       struct hdac_hdmi_port *port = w->priv;
        struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm = NULL;
@@ -781,26 +867,35 @@ static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
        if (ret < 0)
                return ret;
 
+       if (port == NULL)
+               return -EINVAL;
+
        mutex_lock(&hdmi->pin_mutex);
        list_for_each_entry(pcm, &hdmi->pcm_list, head) {
-               if (pcm->pin == pin)
-                       pcm->pin = NULL;
+               if (list_empty(&pcm->port_list))
+                       continue;
 
-               /*
-                * Jack status is not reported during device probe as the
-                * PCMs are not registered by then. So report it here.
-                */
-               if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) {
-                       pcm->pin = pin;
-                       if (pin->eld.monitor_present && pin->eld.eld_valid) {
-                               dev_dbg(&edev->hdac.dev,
-                                       "jack report for pcm=%d\n",
-                                       pcm->pcm_id);
+               list_for_each_entry_safe(p, p_next, &pcm->port_list, head) {
+                       if (p == port && p->id == port->id &&
+                                       p->pin == port->pin) {
+                               hdac_hdmi_jack_report(pcm, port, false);
+                               list_del(&p->head);
+                       }
+               }
+       }
 
-                               snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+       /*
+        * Jack status is not reported during device probe as the
+        * PCMs are not registered by then. So report it here.
+        */
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (!strcmp(cvt_name, pcm->cvt->name)) {
+                       list_add_tail(&port->head, &pcm->port_list);
+                       if (port->eld.monitor_present && port->eld.eld_valid) {
+                               hdac_hdmi_jack_report(pcm, port, true);
+                               mutex_unlock(&hdmi->pin_mutex);
+                               return ret;
                        }
-                       mutex_unlock(&hdmi->pin_mutex);
-                       return ret;
                }
        }
        mutex_unlock(&hdmi->pin_mutex);
@@ -817,12 +912,13 @@ static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
  * care of selecting the right one and leaving all other inputs selected to
  * "NONE"
  */
-static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
-                               struct hdac_hdmi_pin *pin,
+static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev,
+                               struct hdac_hdmi_port *port,
                                struct snd_soc_dapm_widget *widget,
                                const char *widget_name)
 {
        struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pin *pin = port->pin;
        struct snd_kcontrol_new *kc;
        struct hdac_hdmi_cvt *cvt;
        struct soc_enum *se;
@@ -841,7 +937,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
        if (!se)
                return -ENOMEM;
 
-       sprintf(kc_name, "Pin %d Input", pin->nid);
+       sprintf(kc_name, "Pin %d port %d Input", pin->nid, port->id);
        kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);
        if (!kc->name)
                return -ENOMEM;
@@ -850,7 +946,7 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
        kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
        kc->access = 0;
        kc->info = snd_soc_info_enum_double;
-       kc->put = hdac_hdmi_set_pin_mux;
+       kc->put = hdac_hdmi_set_pin_port_mux;
        kc->get = snd_soc_dapm_get_enum_double;
 
        se->reg = SND_SOC_NOPM;
@@ -878,7 +974,9 @@ static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
                return -ENOMEM;
 
        return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
-                       snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
+                       snd_soc_dapm_mux, port, widget_name, NULL, kc, 1,
+                       hdac_hdmi_pin_mux_widget_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG);
 }
 
 /* Add cvt <- input <- mux route map */
@@ -889,10 +987,10 @@ static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        const struct snd_kcontrol_new *kc;
        struct soc_enum *se;
-       int mux_index = hdmi->num_cvt + hdmi->num_pin;
+       int mux_index = hdmi->num_cvt + hdmi->num_ports;
        int i, j;
 
-       for (i = 0; i < hdmi->num_pin; i++) {
+       for (i = 0; i < hdmi->num_ports; i++) {
                kc = widgets[mux_index].kcontrol_news;
                se = (struct soc_enum *)kc->private_value;
                for (j = 0; j < hdmi->num_cvt; j++) {
@@ -911,17 +1009,18 @@ static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
 /*
  * Widgets are added in the below sequence
  *     Converter widgets for num converters enumerated
- *     Pin widgets for num pins enumerated
- *     Pin mux widgets to represent connenction list of pin widget
+ *     Pin-port widgets for num ports for Pins enumerated
+ *     Pin-port mux widgets to represent connenction list of pin widget
  *
- * Total widgets elements = num_cvt + num_pin + num_pin;
+ * For each port, one Mux and One output widget is added
+ * Total widgets elements = num_cvt + (num_ports * 2);
  *
  * Routes are added as below:
- *     pin mux -> pin (based on num_pins)
- *     cvt -> "Input sel control" -> pin_mux
+ *     pin-port mux -> pin (based on num_ports)
+ *     cvt -> "Input sel control" -> pin-port_mux
  *
  * Total route elements:
- *     num_pins + (pin_muxes * num_cvt)
+ *     num_ports + (pin_muxes * num_cvt)
  */
 static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
 {
@@ -933,14 +1032,14 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
        char widget_name[NAME_SIZE];
        struct hdac_hdmi_cvt *cvt;
        struct hdac_hdmi_pin *pin;
-       int ret, i = 0, num_routes = 0;
+       int ret, i = 0, num_routes = 0, j;
 
        if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
                return -EINVAL;
 
-       widgets = devm_kzalloc(dapm->dev,
-               (sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)),
-               GFP_KERNEL);
+       widgets = devm_kzalloc(dapm->dev, (sizeof(*widgets) *
+                               ((2 * hdmi->num_ports) + hdmi->num_cvt)),
+                               GFP_KERNEL);
 
        if (!widgets)
                return -ENOMEM;
@@ -949,37 +1048,50 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
        list_for_each_entry(cvt, &hdmi->cvt_list, head) {
                sprintf(widget_name, "Converter %d", cvt->nid);
                ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
-                       snd_soc_dapm_aif_in, &cvt->nid,
-                       widget_name, dai_drv[i].playback.stream_name, NULL, 0);
+                       snd_soc_dapm_aif_in, cvt,
+                       widget_name, dai_drv[i].playback.stream_name, NULL, 0,
+                       hdac_hdmi_cvt_output_widget_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
                if (ret < 0)
                        return ret;
                i++;
        }
 
        list_for_each_entry(pin, &hdmi->pin_list, head) {
-               sprintf(widget_name, "hif%d Output", pin->nid);
-               ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
-                               snd_soc_dapm_output, &pin->nid,
-                               widget_name, NULL, NULL, 0);
-               if (ret < 0)
-                       return ret;
-               i++;
+               for (j = 0; j < pin->num_ports; j++) {
+                       sprintf(widget_name, "hif%d-%d Output",
+                               pin->nid, pin->ports[j].id);
+                       ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+                                       snd_soc_dapm_output, &pin->ports[j],
+                                       widget_name, NULL, NULL, 0,
+                                       hdac_hdmi_pin_output_widget_event,
+                                       SND_SOC_DAPM_PRE_PMU |
+                                       SND_SOC_DAPM_POST_PMD);
+                       if (ret < 0)
+                               return ret;
+                       pin->ports[j].output_pin = widgets[i].name;
+                       i++;
+               }
        }
 
        /* DAPM widgets to represent the connection list to pin widget */
        list_for_each_entry(pin, &hdmi->pin_list, head) {
-               sprintf(widget_name, "Pin %d Mux", pin->nid);
-               ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i],
-                                                       widget_name);
-               if (ret < 0)
-                       return ret;
-               i++;
+               for (j = 0; j < pin->num_ports; j++) {
+                       sprintf(widget_name, "Pin%d-Port%d Mux",
+                               pin->nid, pin->ports[j].id);
+                       ret = hdac_hdmi_create_pin_port_muxs(edev,
+                                               &pin->ports[j], &widgets[i],
+                                               widget_name);
+                       if (ret < 0)
+                               return ret;
+                       i++;
 
-               /* For cvt to pin_mux mapping */
-               num_routes += hdmi->num_cvt;
+                       /* For cvt to pin_mux mapping */
+                       num_routes += hdmi->num_cvt;
 
-               /* For pin_mux to pin mapping */
-               num_routes++;
+                       /* For pin_mux to pin mapping */
+                       num_routes++;
+               }
        }
 
        route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
@@ -990,20 +1102,22 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
        i = 0;
        /* Add pin <- NULL <- mux route map */
        list_for_each_entry(pin, &hdmi->pin_list, head) {
-               int sink_index = i + hdmi->num_cvt;
-               int src_index = sink_index + hdmi->num_pin;
+               for (j = 0; j < pin->num_ports; j++) {
+                       int sink_index = i + hdmi->num_cvt;
+                       int src_index = sink_index + pin->num_ports *
+                                               hdmi->num_pin;
 
-               hdac_hdmi_fill_route(&route[i],
+                       hdac_hdmi_fill_route(&route[i],
                                widgets[sink_index].name, NULL,
                                widgets[src_index].name, NULL);
-               i++;
-
+                       i++;
+               }
        }
 
        hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);
 
        snd_soc_dapm_new_controls(dapm, widgets,
-               ((2 * hdmi->num_pin) + hdmi->num_cvt));
+               ((2 * hdmi->num_ports) + hdmi->num_cvt));
 
        snd_soc_dapm_add_routes(dapm, route, num_routes);
        snd_soc_dapm_new_widgets(dapm->card);
@@ -1015,7 +1129,7 @@ static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
 static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
 {
        struct hdac_hdmi_priv *hdmi = edev->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map;
+       struct hdac_hdmi_dai_port_map *dai_map;
        struct hdac_hdmi_cvt *cvt;
        int dai_id = 0;
 
@@ -1059,132 +1173,149 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
        return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
 }
 
-static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
-                       struct hdac_hdmi_pin *pin)
+static int hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
+                       struct hdac_hdmi_port *port)
 {
-       pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER];
+       unsigned int ver, mnl;
+
+       ver = (port->eld.eld_buffer[DRM_ELD_VER] & DRM_ELD_VER_MASK)
+                                               >> DRM_ELD_VER_SHIFT;
+
+       if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) {
+               dev_err(&edev->hdac.dev, "HDMI: Unknown ELD version %d\n", ver);
+               return -EINVAL;
+       }
+
+       mnl = (port->eld.eld_buffer[DRM_ELD_CEA_EDID_VER_MNL] &
+               DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT;
+
+       if (mnl > ELD_MAX_MNL) {
+               dev_err(&edev->hdac.dev, "HDMI: MNL Invalid %d\n", mnl);
+               return -EINVAL;
+       }
+
+       port->eld.info.spk_alloc = port->eld.eld_buffer[DRM_ELD_SPEAKER];
+
+       return 0;
 }
 
-static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
+                                   struct hdac_hdmi_port *port)
 {
        struct hdac_ext_device *edev = pin->edev;
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm;
-       int val;
+       int size = 0;
+       int port_id = -1;
 
-       pin->repoll_count = repoll;
+       if (!hdmi)
+               return;
 
-       pm_runtime_get_sync(&edev->hdac.dev);
-       val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
-                                       AC_VERB_GET_PIN_SENSE, 0);
+       /*
+        * In case of non MST pin, get_eld info API expectes port
+        * to be -1.
+        */
+       mutex_lock(&hdmi->pin_mutex);
+       port->eld.monitor_present = false;
 
-       dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
-                                               val, pin->nid);
+       if (pin->mst_capable)
+               port_id = port->id;
 
+       size = snd_hdac_acomp_get_eld(&edev->hdac, pin->nid, port_id,
+                               &port->eld.monitor_present,
+                               port->eld.eld_buffer,
+                               ELD_MAX_SIZE);
 
-       mutex_lock(&hdmi->pin_mutex);
-       pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
-       pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
+       if (size > 0) {
+               size = min(size, ELD_MAX_SIZE);
+               if (hdac_hdmi_parse_eld(edev, port) < 0)
+                       size = -EINVAL;
+       }
 
-       pcm = hdac_hdmi_get_pcm(edev, pin);
+       if (size > 0) {
+               port->eld.eld_valid = true;
+               port->eld.eld_size = size;
+       } else {
+               port->eld.eld_valid = false;
+               port->eld.eld_size = 0;
+       }
 
-       if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
+       pcm = hdac_hdmi_get_pcm(edev, port);
 
-               dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
-                                               __func__, pin->nid);
+       if (!port->eld.monitor_present || !port->eld.eld_valid) {
+
+               dev_err(&edev->hdac.dev, "%s: disconnect for pin:port %d:%d\n",
+                                               __func__, pin->nid, port->id);
 
                /*
                 * PCMs are not registered during device probe, so don't
                 * report jack here. It will be done in usermode mux
                 * control select.
                 */
-               if (pcm) {
-                       dev_dbg(&edev->hdac.dev,
-                               "jack report for pcm=%d\n", pcm->pcm_id);
-
-                       snd_jack_report(pcm->jack, 0);
-               }
+               if (pcm)
+                       hdac_hdmi_jack_report(pcm, port, false);
 
                mutex_unlock(&hdmi->pin_mutex);
-               goto put_hdac_device;
+               return;
        }
 
-       if (pin->eld.monitor_present && pin->eld.eld_valid) {
-               /* TODO: use i915 component for reading ELD later */
-               if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
-                               pin->eld.eld_buffer,
-                               &pin->eld.eld_size) == 0) {
+       if (port->eld.monitor_present && port->eld.eld_valid) {
+               if (pcm)
+                       hdac_hdmi_jack_report(pcm, port, true);
 
-                       if (pcm) {
-                               dev_dbg(&edev->hdac.dev,
-                                       "jack report for pcm=%d\n",
-                                       pcm->pcm_id);
-
-                               snd_jack_report(pcm->jack, SND_JACK_AVOUT);
-                       }
-                       hdac_hdmi_parse_eld(edev, pin);
+               print_hex_dump_debug("ELD: ", DUMP_PREFIX_OFFSET, 16, 1,
+                         port->eld.eld_buffer, port->eld.eld_size, false);
 
-                       print_hex_dump_debug("ELD: ",
-                                       DUMP_PREFIX_OFFSET, 16, 1,
-                                       pin->eld.eld_buffer, pin->eld.eld_size,
-                                       true);
-               } else {
-                       pin->eld.monitor_present = false;
-                       pin->eld.eld_valid = false;
-
-                       if (pcm) {
-                               dev_dbg(&edev->hdac.dev,
-                                       "jack report for pcm=%d\n",
-                                       pcm->pcm_id);
-
-                               snd_jack_report(pcm->jack, 0);
-                       }
-               }
        }
-
        mutex_unlock(&hdmi->pin_mutex);
-
-       /*
-        * Sometimes the pin_sense may present invalid monitor
-        * present and eld_valid. If ELD data is not valid, loop few
-        * more times to get correct pin sense and valid ELD.
-        */
-       if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
-               schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
-
-put_hdac_device:
-       pm_runtime_put_sync(&edev->hdac.dev);
 }
 
-static void hdac_hdmi_repoll_eld(struct work_struct *work)
+static int hdac_hdmi_add_ports(struct hdac_hdmi_priv *hdmi,
+                               struct hdac_hdmi_pin *pin)
 {
-       struct hdac_hdmi_pin *pin =
-               container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
+       struct hdac_hdmi_port *ports;
+       int max_ports = HDA_MAX_PORTS;
+       int i;
+
+       /*
+        * FIXME: max_port may vary for each platform, so pass this as
+        * as driver data or query from i915 interface when this API is
+        * implemented.
+        */
 
-       /* picked from legacy HDA driver */
-       if (pin->repoll_count++ > 6)
-               pin->repoll_count = 0;
+       ports = kcalloc(max_ports, sizeof(*ports), GFP_KERNEL);
+       if (!ports)
+               return -ENOMEM;
 
-       hdac_hdmi_present_sense(pin, pin->repoll_count);
+       for (i = 0; i < max_ports; i++) {
+               ports[i].id = i;
+               ports[i].pin = pin;
+       }
+       pin->ports = ports;
+       pin->num_ports = max_ports;
+       return 0;
 }
 
 static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 {
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pin *pin;
+       int ret;
 
        pin = kzalloc(sizeof(*pin), GFP_KERNEL);
        if (!pin)
                return -ENOMEM;
 
        pin->nid = nid;
+       pin->mst_capable = false;
+       pin->edev = edev;
+       ret = hdac_hdmi_add_ports(hdmi, pin);
+       if (ret < 0)
+               return ret;
 
        list_add_tail(&pin->head, &hdmi->pin_list);
        hdmi->num_pin++;
-
-       pin->edev = edev;
-       mutex_init(&pin->lock);
-       INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
+       hdmi->num_ports += pin->num_ports;
 
        return 0;
 }
@@ -1233,9 +1364,7 @@ static struct snd_soc_dai_ops hdmi_dai_ops = {
        .startup = hdac_hdmi_pcm_open,
        .shutdown = hdac_hdmi_pcm_close,
        .hw_params = hdac_hdmi_set_hw_params,
-       .prepare = hdac_hdmi_playback_prepare,
-       .trigger = hdac_hdmi_trigger,
-       .hw_free = hdac_hdmi_playback_cleanup,
+       .set_tdm_slot = hdac_hdmi_set_tdm_slot,
 };
 
 /*
@@ -1372,13 +1501,16 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
 {
        struct hdac_ext_device *edev = aptr;
        struct hdac_hdmi_priv *hdmi = edev->private_data;
-       struct hdac_hdmi_pin *pin;
+       struct hdac_hdmi_pin *pin = NULL;
+       struct hdac_hdmi_port *hport = NULL;
        struct snd_soc_codec *codec = edev->scodec;
+       int i;
 
        /* Don't know how this mapping is derived */
        hda_nid_t pin_nid = port + 0x04;
 
-       dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
+       dev_dbg(&edev->hdac.dev, "%s: for pin:%d port=%d\n", __func__,
+                                                       pin_nid, pipe);
 
        /*
         * skip notification during system suspend (but not in runtime PM);
@@ -1394,9 +1526,29 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe)
                return;
 
        list_for_each_entry(pin, &hdmi->pin_list, head) {
-               if (pin->nid == pin_nid)
-                       hdac_hdmi_present_sense(pin, 1);
+               if (pin->nid != pin_nid)
+                       continue;
+
+               /* In case of non MST pin, pipe is -1 */
+               if (pipe == -1) {
+                       pin->mst_capable = false;
+                       /* if not MST, default is port[0] */
+                       hport = &pin->ports[0];
+                       goto out;
+               } else {
+                       for (i = 0; i < pin->num_ports; i++) {
+                               pin->mst_capable = true;
+                               if (pin->ports[i].id == pipe) {
+                                       hport = &pin->ports[i];
+                                       goto out;
+                               }
+                       }
+               }
        }
+
+out:
+       if (pin && hport)
+               hdac_hdmi_present_sense(pin, hport);
 }
 
 static struct i915_audio_component_audio_ops aops = {
@@ -1416,13 +1568,130 @@ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
        return NULL;
 }
 
-int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
+/* create jack pin kcontrols */
+static int create_fill_jack_kcontrols(struct snd_soc_card *card,
+                                   struct hdac_ext_device *edev)
+{
+       struct hdac_hdmi_pin *pin;
+       struct snd_kcontrol_new *kc;
+       char kc_name[NAME_SIZE], xname[NAME_SIZE];
+       char *name;
+       int i = 0, j;
+       struct snd_soc_codec *codec = edev->scodec;
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+
+       kc = devm_kcalloc(codec->dev, hdmi->num_ports,
+                               sizeof(*kc), GFP_KERNEL);
+
+       if (!kc)
+               return -ENOMEM;
+
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               for (j = 0; j < pin->num_ports; j++) {
+                       snprintf(xname, sizeof(xname), "hif%d-%d Jack",
+                                               pin->nid, pin->ports[j].id);
+                       name = devm_kstrdup(codec->dev, xname, GFP_KERNEL);
+                       if (!name)
+                               return -ENOMEM;
+                       snprintf(kc_name, sizeof(kc_name), "%s Switch", xname);
+                       kc[i].name = devm_kstrdup(codec->dev, kc_name,
+                                                       GFP_KERNEL);
+                       if (!kc[i].name)
+                               return -ENOMEM;
+
+                       kc[i].private_value = (unsigned long)name;
+                       kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+                       kc[i].access = 0;
+                       kc[i].info = snd_soc_dapm_info_pin_switch;
+                       kc[i].put = snd_soc_dapm_put_pin_switch;
+                       kc[i].get = snd_soc_dapm_get_pin_switch;
+                       i++;
+               }
+       }
+
+       return snd_soc_add_card_controls(card, kc, i);
+}
+
+int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec,
+                       struct snd_soc_dapm_context *dapm)
+{
+       struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pin *pin;
+       struct snd_soc_dapm_widget *widgets;
+       struct snd_soc_dapm_route *route;
+       char w_name[NAME_SIZE];
+       int i = 0, j, ret;
+
+       widgets = devm_kcalloc(dapm->dev, hdmi->num_ports,
+                               sizeof(*widgets), GFP_KERNEL);
+
+       if (!widgets)
+               return -ENOMEM;
+
+       route = devm_kcalloc(dapm->dev, hdmi->num_ports,
+                               sizeof(*route), GFP_KERNEL);
+       if (!route)
+               return -ENOMEM;
+
+       /* create Jack DAPM widget */
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               for (j = 0; j < pin->num_ports; j++) {
+                       snprintf(w_name, sizeof(w_name), "hif%d-%d Jack",
+                                               pin->nid, pin->ports[j].id);
+
+                       ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+                                       snd_soc_dapm_spk, NULL,
+                                       w_name, NULL, NULL, 0, NULL, 0);
+                       if (ret < 0)
+                               return ret;
+
+                       pin->ports[j].jack_pin = widgets[i].name;
+                       pin->ports[j].dapm = dapm;
+
+                       /* add to route from Jack widget to output */
+                       hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin,
+                                       NULL, pin->ports[j].output_pin, NULL);
+
+                       i++;
+               }
+       }
+
+       /* Add Route from Jack widget to the output widget */
+       ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dapm_new_widgets(dapm->card);
+       if (ret < 0)
+               return ret;
+
+       /* Add Jack Pin switch Kcontrol */
+       ret = create_fill_jack_kcontrols(dapm->card, edev);
+
+       if (ret < 0)
+               return ret;
+
+       /* default set the Jack Pin switch to OFF */
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               for (j = 0; j < pin->num_ports; j++)
+                       snd_soc_dapm_disable_pin(pin->ports[j].dapm,
+                                               pin->ports[j].jack_pin);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init);
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
+                               struct snd_soc_jack *jack)
 {
-       char jack_name[NAME_SIZE];
        struct snd_soc_codec *codec = dai->codec;
        struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
-       struct snd_soc_dapm_context *dapm =
-               snd_soc_component_get_dapm(&codec->component);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm;
        struct snd_pcm *snd_pcm;
@@ -1437,7 +1706,10 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
                return -ENOMEM;
        pcm->pcm_id = device;
        pcm->cvt = hdmi->dai_map[dai->id].cvt;
-
+       pcm->jack_event = 0;
+       pcm->jack = jack;
+       mutex_init(&pcm->lock);
+       INIT_LIST_HEAD(&pcm->port_list);
        snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
        if (snd_pcm) {
                err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
@@ -1452,20 +1724,40 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
 
        list_add_tail(&pcm->head, &hdmi->pcm_list);
 
-       sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
-
-       return snd_jack_new(dapm->card->snd_card, jack_name,
-               SND_JACK_AVOUT, &pcm->jack, true, false);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
 
+static void hdac_hdmi_present_sense_all_pins(struct hdac_ext_device *edev,
+                       struct hdac_hdmi_priv *hdmi, bool detect_pin_caps)
+{
+       int i;
+       struct hdac_hdmi_pin *pin;
+
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               if (detect_pin_caps) {
+
+                       if (hdac_hdmi_get_port_len(edev, pin->nid)  == 0)
+                               pin->mst_capable = false;
+                       else
+                               pin->mst_capable = true;
+               }
+
+               for (i = 0; i < pin->num_ports; i++) {
+                       if (!pin->mst_capable && i > 0)
+                               continue;
+
+                       hdac_hdmi_present_sense(pin, &pin->ports[i]);
+               }
+       }
+}
+
 static int hdmi_codec_probe(struct snd_soc_codec *codec)
 {
        struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct snd_soc_dapm_context *dapm =
                snd_soc_component_get_dapm(&codec->component);
-       struct hdac_hdmi_pin *pin;
        struct hdac_ext_link *hlink = NULL;
        int ret;
 
@@ -1495,9 +1787,7 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
-       list_for_each_entry(pin, &hdmi->pin_list, head)
-               hdac_hdmi_present_sense(pin, 1);
-
+       hdac_hdmi_present_sense_all_pins(edev, hdmi, true);
        /* Imp: Store the card pointer in hda_codec */
        edev->card = dapm->card->snd_card;
 
@@ -1545,7 +1835,6 @@ static void hdmi_codec_complete(struct device *dev)
 {
        struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
-       struct hdac_hdmi_pin *pin;
        struct hdac_device *hdac = &edev->hdac;
 
        /* Power up afg */
@@ -1558,10 +1847,10 @@ static void hdmi_codec_complete(struct device *dev)
        /*
         * As the ELD notify callback request is not entertained while the
         * device is in suspend state. Need to manually check detection of
-        * all pins here.
+        * all pins here. pin capablity change is not support, so use the
+        * already set pin caps.
         */
-       list_for_each_entry(pin, &hdmi->pin_list, head)
-               hdac_hdmi_present_sense(pin, 1);
+       hdac_hdmi_present_sense_all_pins(edev, hdmi, false);
 
        pm_runtime_put_sync(&edev->hdac.dev);
 }
@@ -1582,13 +1871,8 @@ static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
        struct hdac_ext_device *edev = to_ehdac_device(hdac);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
-       struct hdac_hdmi_pin *pin = pcm->pin;
-
-       /* chmap is already set to 0 in caller */
-       if (!pin)
-               return;
 
-       memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap));
+       memcpy(chmap, pcm->chmap, ARRAY_SIZE(pcm->chmap));
 }
 
 static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
@@ -1597,14 +1881,18 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
        struct hdac_ext_device *edev = to_ehdac_device(hdac);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
-       struct hdac_hdmi_pin *pin = pcm->pin;
-
-       mutex_lock(&pin->lock);
-       pin->chmap_set = true;
-       memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap));
-       if (prepared)
-               hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid);
-       mutex_unlock(&pin->lock);
+       struct hdac_hdmi_port *port;
+
+       if (list_empty(&pcm->port_list))
+               return;
+
+       mutex_lock(&pcm->lock);
+       pcm->chmap_set = true;
+       memcpy(pcm->chmap, chmap, ARRAY_SIZE(pcm->chmap));
+       list_for_each_entry(port, &pcm->port_list, head)
+               if (prepared)
+                       hdac_hdmi_setup_audio_infoframe(edev, pcm, port);
+       mutex_unlock(&pcm->lock);
 }
 
 static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
@@ -1612,9 +1900,11 @@ static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
        struct hdac_ext_device *edev = to_ehdac_device(hdac);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
-       struct hdac_hdmi_pin *pin = pcm->pin;
 
-       return pin ? true:false;
+       if (list_empty(&pcm->port_list))
+               return false;
+
+       return true;
 }
 
 static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
@@ -1622,12 +1912,20 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
        struct hdac_ext_device *edev = to_ehdac_device(hdac);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
-       struct hdac_hdmi_pin *pin = pcm->pin;
+       struct hdac_hdmi_port *port;
 
-       if (!pin || !pin->eld.eld_valid)
+       if (list_empty(&pcm->port_list))
                return 0;
 
-       return pin->eld.info.spk_alloc;
+       port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head);
+
+       if (!port)
+               return 0;
+
+       if (!port || !port->eld.eld_valid)
+               return 0;
+
+       return port->eld.info.spk_alloc;
 }
 
 static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
@@ -1700,12 +1998,19 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
        struct hdac_hdmi_pin *pin, *pin_next;
        struct hdac_hdmi_cvt *cvt, *cvt_next;
        struct hdac_hdmi_pcm *pcm, *pcm_next;
+       struct hdac_hdmi_port *port;
+       int i;
 
        snd_soc_unregister_codec(&edev->hdac.dev);
 
        list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
                pcm->cvt = NULL;
-               pcm->pin = NULL;
+               if (list_empty(&pcm->port_list))
+                       continue;
+
+               list_for_each_entry(port, &pcm->port_list, head)
+                       port = NULL;
+
                list_del(&pcm->head);
                kfree(pcm);
        }
@@ -1717,6 +2022,9 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
        }
 
        list_for_each_entry_safe(pin, pin_next, &hdmi->pin_list, head) {
+               for (i = 0; i < pin->num_ports; i++)
+                       pin->ports[i].pin = NULL;
+               kfree(pin->ports);
                list_del(&pin->head);
                kfree(pin);
        }
@@ -1819,6 +2127,7 @@ static const struct hda_device_id hdmi_list[] = {
        HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
        HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
        HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
+       HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", 0),
        {}
 };
 
index 8dfd1e0b57b34cade5df30fc66fad6ae73a95800..dfc3a9cf719963a3fdb3bbda7293895609638016 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef __HDAC_HDMI_H__
 #define __HDAC_HDMI_H__
 
-int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm);
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm,
+                               struct snd_soc_jack *jack);
 
+int hdac_hdmi_jack_port_init(struct snd_soc_codec *codec,
+                       struct snd_soc_dapm_context *dapm);
 #endif /* __HDAC_HDMI_H__ */
index 90b5948e0ff363a91538a28513e35e32e35a0ee1..8c5ae1fc23a9b9edd0c5d088ca27bec41f4be7ea 100644 (file)
@@ -18,6 +18,7 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/tlv.h>
 #include <sound/pcm_drm_eld.h>
 #include <sound/hdmi-codec.h>
 #include <sound/pcm_iec958.h>
@@ -31,8 +32,261 @@ struct hdmi_device {
 };
 #define pos_to_hdmi_device(pos)        container_of((pos), struct hdmi_device, list)
 LIST_HEAD(hdmi_device_list);
+static DEFINE_MUTEX(hdmi_mutex);
 
 #define DAI_NAME_SIZE 16
+
+#define HDMI_CODEC_CHMAP_IDX_UNKNOWN  -1
+
+struct hdmi_codec_channel_map_table {
+       unsigned char map;      /* ALSA API channel map position */
+       unsigned long spk_mask;         /* speaker position bit mask */
+};
+
+/*
+ * CEA speaker placement for HDMI 1.4:
+ *
+ *  FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *
+ *  RL  RLC   RC   RRC   RR
+ *
+ *  Speaker placement has to be extended to support HDMI 2.0
+ */
+enum hdmi_codec_cea_spk_placement {
+       FL  = BIT(0),   /* Front Left           */
+       FC  = BIT(1),   /* Front Center         */
+       FR  = BIT(2),   /* Front Right          */
+       FLC = BIT(3),   /* Front Left Center    */
+       FRC = BIT(4),   /* Front Right Center   */
+       RL  = BIT(5),   /* Rear Left            */
+       RC  = BIT(6),   /* Rear Center          */
+       RR  = BIT(7),   /* Rear Right           */
+       RLC = BIT(8),   /* Rear Left Center     */
+       RRC = BIT(9),   /* Rear Right Center    */
+       LFE = BIT(10),  /* Low Frequency Effect */
+};
+
+/*
+ * cea Speaker allocation structure
+ */
+struct hdmi_codec_cea_spk_alloc {
+       const int ca_id;
+       unsigned int n_ch;
+       unsigned long mask;
+};
+
+/* Channel maps  stereo HDMI */
+const struct snd_pcm_chmap_elem hdmi_codec_stereo_chmaps[] = {
+       { .channels = 2,
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+       { }
+};
+
+/* Channel maps for multi-channel playbacks, up to 8 n_ch */
+const struct snd_pcm_chmap_elem hdmi_codec_8ch_chmaps[] = {
+       { .channels = 2, /* CA_ID 0x00 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR } },
+       { .channels = 4, /* CA_ID 0x01 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA } },
+       { .channels = 4, /* CA_ID 0x02 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC } },
+       { .channels = 4, /* CA_ID 0x03 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC } },
+       { .channels = 6, /* CA_ID 0x04 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 6, /* CA_ID 0x05 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 6, /* CA_ID 0x06 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 6, /* CA_ID 0x07 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 6, /* CA_ID 0x08 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+       { .channels = 6, /* CA_ID 0x09 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+       { .channels = 6, /* CA_ID 0x0A */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+       { .channels = 6, /* CA_ID 0x0B */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+       { .channels = 8, /* CA_ID 0x0C */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 8, /* CA_ID 0x0D */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 8, /* CA_ID 0x0E */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 8, /* CA_ID 0x0F */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RC, SNDRV_CHMAP_NA } },
+       { .channels = 8, /* CA_ID 0x10 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+       { .channels = 8, /* CA_ID 0x11 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+       { .channels = 8, /* CA_ID 0x12 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+       { .channels = 8, /* CA_ID 0x13 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_RL, SNDRV_CHMAP_RR,
+                  SNDRV_CHMAP_RLC, SNDRV_CHMAP_RRC } },
+       { .channels = 8, /* CA_ID 0x14 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x15 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x16 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x17 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x18 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x19 */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x1A */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x1B */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x1C */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x1D */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_NA, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x1E */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { .channels = 8, /* CA_ID 0x1F */
+         .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_FR, SNDRV_CHMAP_LFE,
+                  SNDRV_CHMAP_FC, SNDRV_CHMAP_NA, SNDRV_CHMAP_NA,
+                  SNDRV_CHMAP_FLC, SNDRV_CHMAP_FRC } },
+       { }
+};
+
+/*
+ * hdmi_codec_channel_alloc: speaker configuration available for CEA
+ *
+ * This is an ordered list that must match with hdmi_codec_8ch_chmaps struct
+ * The preceding ones have better chances to be selected by
+ * hdmi_codec_get_ch_alloc_table_idx().
+ */
+static const struct hdmi_codec_cea_spk_alloc hdmi_codec_channel_alloc[] = {
+       { .ca_id = 0x00, .n_ch = 2,
+         .mask = FL | FR},
+       /* 2.1 */
+       { .ca_id = 0x01, .n_ch = 4,
+         .mask = FL | FR | LFE},
+       /* Dolby Surround */
+       { .ca_id = 0x02, .n_ch = 4,
+         .mask = FL | FR | FC },
+       /* surround51 */
+       { .ca_id = 0x0b, .n_ch = 6,
+         .mask = FL | FR | LFE | FC | RL | RR},
+       /* surround40 */
+       { .ca_id = 0x08, .n_ch = 6,
+         .mask = FL | FR | RL | RR },
+       /* surround41 */
+       { .ca_id = 0x09, .n_ch = 6,
+         .mask = FL | FR | LFE | RL | RR },
+       /* surround50 */
+       { .ca_id = 0x0a, .n_ch = 6,
+         .mask = FL | FR | FC | RL | RR },
+       /* 6.1 */
+       { .ca_id = 0x0f, .n_ch = 8,
+         .mask = FL | FR | LFE | FC | RL | RR | RC },
+       /* surround71 */
+       { .ca_id = 0x13, .n_ch = 8,
+         .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC },
+       /* others */
+       { .ca_id = 0x03, .n_ch = 8,
+         .mask = FL | FR | LFE | FC },
+       { .ca_id = 0x04, .n_ch = 8,
+         .mask = FL | FR | RC},
+       { .ca_id = 0x05, .n_ch = 8,
+         .mask = FL | FR | LFE | RC },
+       { .ca_id = 0x06, .n_ch = 8,
+         .mask = FL | FR | FC | RC },
+       { .ca_id = 0x07, .n_ch = 8,
+         .mask = FL | FR | LFE | FC | RC },
+       { .ca_id = 0x0c, .n_ch = 8,
+         .mask = FL | FR | RC | RL | RR },
+       { .ca_id = 0x0d, .n_ch = 8,
+         .mask = FL | FR | LFE | RL | RR | RC },
+       { .ca_id = 0x0e, .n_ch = 8,
+         .mask = FL | FR | FC | RL | RR | RC },
+       { .ca_id = 0x10, .n_ch = 8,
+         .mask = FL | FR | RL | RR | RLC | RRC },
+       { .ca_id = 0x11, .n_ch = 8,
+         .mask = FL | FR | LFE | RL | RR | RLC | RRC },
+       { .ca_id = 0x12, .n_ch = 8,
+         .mask = FL | FR | FC | RL | RR | RLC | RRC },
+       { .ca_id = 0x14, .n_ch = 8,
+         .mask = FL | FR | FLC | FRC },
+       { .ca_id = 0x15, .n_ch = 8,
+         .mask = FL | FR | LFE | FLC | FRC },
+       { .ca_id = 0x16, .n_ch = 8,
+         .mask = FL | FR | FC | FLC | FRC },
+       { .ca_id = 0x17, .n_ch = 8,
+         .mask = FL | FR | LFE | FC | FLC | FRC },
+       { .ca_id = 0x18, .n_ch = 8,
+         .mask = FL | FR | RC | FLC | FRC },
+       { .ca_id = 0x19, .n_ch = 8,
+         .mask = FL | FR | LFE | RC | FLC | FRC },
+       { .ca_id = 0x1a, .n_ch = 8,
+         .mask = FL | FR | RC | FC | FLC | FRC },
+       { .ca_id = 0x1b, .n_ch = 8,
+         .mask = FL | FR | LFE | RC | FC | FLC | FRC },
+       { .ca_id = 0x1c, .n_ch = 8,
+         .mask = FL | FR | RL | RR | FLC | FRC },
+       { .ca_id = 0x1d, .n_ch = 8,
+         .mask = FL | FR | LFE | RL | RR | FLC | FRC },
+       { .ca_id = 0x1e, .n_ch = 8,
+         .mask = FL | FR | FC | RL | RR | FLC | FRC },
+       { .ca_id = 0x1f, .n_ch = 8,
+         .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
+};
+
 struct hdmi_codec_priv {
        struct hdmi_codec_pdata hcd;
        struct snd_soc_dai_driver *daidrv;
@@ -41,6 +295,8 @@ struct hdmi_codec_priv {
        struct snd_pcm_substream *current_stream;
        struct snd_pcm_hw_constraint_list ratec;
        uint8_t eld[MAX_ELD_BYTES];
+       struct snd_pcm_chmap *chmap_info;
+       unsigned int chmap_idx;
 };
 
 static const struct snd_soc_dapm_widget hdmi_widgets[] = {
@@ -79,6 +335,83 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static unsigned long hdmi_codec_spk_mask_from_alloc(int spk_alloc)
+{
+       int i;
+       const unsigned long hdmi_codec_eld_spk_alloc_bits[] = {
+               [0] = FL | FR, [1] = LFE, [2] = FC, [3] = RL | RR,
+               [4] = RC, [5] = FLC | FRC, [6] = RLC | RRC,
+       };
+       unsigned long spk_mask = 0;
+
+       for (i = 0; i < ARRAY_SIZE(hdmi_codec_eld_spk_alloc_bits); i++) {
+               if (spk_alloc & (1 << i))
+                       spk_mask |= hdmi_codec_eld_spk_alloc_bits[i];
+       }
+
+       return spk_mask;
+}
+
+void hdmi_codec_eld_chmap(struct hdmi_codec_priv *hcp)
+{
+       u8 spk_alloc;
+       unsigned long spk_mask;
+
+       spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
+       spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
+
+       /* Detect if only stereo supported, else return 8 channels mappings */
+       if ((spk_mask & ~(FL | FR)) && hcp->chmap_info->max_channels > 2)
+               hcp->chmap_info->chmap = hdmi_codec_8ch_chmaps;
+       else
+               hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
+}
+
+static int hdmi_codec_get_ch_alloc_table_idx(struct hdmi_codec_priv *hcp,
+                                            unsigned char channels)
+{
+       int i;
+       u8 spk_alloc;
+       unsigned long spk_mask;
+       const struct hdmi_codec_cea_spk_alloc *cap = hdmi_codec_channel_alloc;
+
+       spk_alloc = drm_eld_get_spk_alloc(hcp->eld);
+       spk_mask = hdmi_codec_spk_mask_from_alloc(spk_alloc);
+
+       for (i = 0; i < ARRAY_SIZE(hdmi_codec_channel_alloc); i++, cap++) {
+               /* If spk_alloc == 0, HDMI is unplugged return stereo config*/
+               if (!spk_alloc && cap->ca_id == 0)
+                       return i;
+               if (cap->n_ch != channels)
+                       continue;
+               if (!(cap->mask == (spk_mask & cap->mask)))
+                       continue;
+               return i;
+       }
+
+       return -EINVAL;
+}
+static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       unsigned const char *map;
+       unsigned int i;
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdmi_codec_priv *hcp = info->private_data;
+
+       map = info->chmap[hcp->chmap_idx].map;
+
+       for (i = 0; i < info->max_channels; i++) {
+               if (hcp->chmap_idx == HDMI_CODEC_CHMAP_IDX_UNKNOWN)
+                       ucontrol->value.integer.value[i] = 0;
+               else
+                       ucontrol->value.integer.value[i] = map[i];
+       }
+
+       return 0;
+}
+
+
 static const struct snd_kcontrol_new hdmi_controls[] = {
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ |
@@ -140,6 +473,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
                        if (ret)
                                return ret;
                }
+               /* Select chmap supported */
+               hdmi_codec_eld_chmap(hcp);
        }
        return 0;
 }
@@ -153,6 +488,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
 
        WARN_ON(hcp->current_stream != substream);
 
+       hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
        hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
 
        mutex_lock(&hcp->current_stream_lock);
@@ -173,7 +509,7 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
                        .dig_subframe = { 0 },
                }
        };
-       int ret;
+       int ret, idx;
 
        dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
                params_width(params), params_rate(params),
@@ -200,6 +536,17 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
        hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
        hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
 
+       /* Select a channel allocation that matches with ELD and pcm channels */
+       idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
+       if (idx < 0) {
+               dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+                       idx);
+               hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+               return idx;
+       }
+       hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
+       hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
+
        hp.sample_width = params_width(params);
        hp.sample_rate = params_rate(params);
        hp.channels = params_channels(params);
@@ -328,6 +675,32 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = {
                         SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
                         SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
 
+static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
+                             struct snd_soc_dai *dai)
+{
+       struct snd_soc_dai_driver *drv = dai->driver;
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       dev_dbg(dai->dev, "%s()\n", __func__);
+
+       ret =  snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                     NULL, drv->playback.channels_max, 0,
+                                     &hcp->chmap_info);
+       if (ret < 0)
+               return ret;
+
+       /* override handlers */
+       hcp->chmap_info->private_data = hcp;
+       hcp->chmap_info->kctl->get = hdmi_codec_chmap_ctl_get;
+
+       /* default chmap supported is stereo */
+       hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
+       hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+
+       return 0;
+}
+
 static struct snd_soc_dai_driver hdmi_i2s_dai = {
        .id = DAI_ID_I2S,
        .playback = {
@@ -339,6 +712,7 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = {
                .sig_bits = 24,
        },
        .ops = &hdmi_dai_ops,
+       .pcm_new = hdmi_codec_pcm_new,
 };
 
 static const struct snd_soc_dai_driver hdmi_spdif_dai = {
@@ -351,6 +725,7 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
                .formats = SPDIF_FORMATS,
        },
        .ops = &hdmi_dai_ops,
+       .pcm_new = hdmi_codec_pcm_new,
 };
 
 static char hdmi_dai_name[][DAI_NAME_SIZE] = {
@@ -420,6 +795,7 @@ static int hdmi_codec_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        hd = NULL;
+       mutex_lock(&hdmi_mutex);
        list_for_each(pos, &hdmi_device_list) {
                struct hdmi_device *tmp = pos_to_hdmi_device(pos);
 
@@ -431,13 +807,16 @@ static int hdmi_codec_probe(struct platform_device *pdev)
 
        if (!hd) {
                hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL);
-               if (!hd)
+               if (!hd) {
+                       mutex_unlock(&hdmi_mutex);
                        return -ENOMEM;
+               }
 
                hd->dev = dev->parent;
 
                list_add_tail(&hd->list, &hdmi_device_list);
        }
+       mutex_unlock(&hdmi_mutex);
 
        if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {
                dev_err(dev, "too many hdmi codec are deteced\n");
@@ -479,7 +858,25 @@ static int hdmi_codec_probe(struct platform_device *pdev)
 
 static int hdmi_codec_remove(struct platform_device *pdev)
 {
-       snd_soc_unregister_codec(&pdev->dev);
+       struct device *dev = &pdev->dev;
+       struct list_head *pos;
+       struct hdmi_codec_priv *hcp;
+
+       mutex_lock(&hdmi_mutex);
+       list_for_each(pos, &hdmi_device_list) {
+               struct hdmi_device *tmp = pos_to_hdmi_device(pos);
+
+               if (tmp->dev == dev->parent) {
+                       list_del(pos);
+                       break;
+               }
+       }
+       mutex_unlock(&hdmi_mutex);
+
+       hcp = dev_get_drvdata(dev);
+       kfree(hcp->chmap_info);
+       snd_soc_unregister_codec(dev);
+
        return 0;
 }
 
index 584aab83e4783b12232cc94000431efacbc39d95..66828480d484ca11912d8b163efe0c51445b155c 100644 (file)
@@ -2456,7 +2456,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
        if (err) {
                micbias = M98090_MBVSEL_2V8;
                dev_info(codec->dev, "use default 2.8v micbias\n");
-       } else if (micbias < M98090_MBVSEL_2V2 || micbias > M98090_MBVSEL_2V8) {
+       } else if (micbias > M98090_MBVSEL_2V8) {
                dev_err(codec->dev, "micbias out of range 0x%x\n", micbias);
                micbias = M98090_MBVSEL_2V8;
        }
index 42e2e407e287c4dc0354a00d864e8b997fcacbfa..6cdf15ab46de7fa8dad6d8f14eaac4c549309feb 100644 (file)
@@ -309,7 +309,6 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
        struct snd_soc_codec *codec = codec_dai->codec;
        struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
        u8 iface1A = 0, iface1B = 0;
-       int ret;
 
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBM_CFM:
@@ -346,8 +345,8 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
                return -EINVAL;
        }
 
-       ret = regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
-       ret = regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
+       regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
+       regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
        return 0;
 }
 
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
new file mode 100644 (file)
index 0000000..9e8f0f4
--- /dev/null
@@ -0,0 +1,835 @@
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "nau8540.h"
+
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+/* the maximum frequency of CLK_ADC */
+#define CLK_ADC_MAX 6144000
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8540_fll_attr mclk_src_scaling[] = {
+       { 1, 0x0 },
+       { 2, 0x2 },
+       { 4, 0x3 },
+       { 8, 0x4 },
+       { 16, 0x5 },
+       { 32, 0x6 },
+       { 3, 0x7 },
+       { 6, 0xa },
+       { 12, 0xb },
+       { 24, 0xc },
+};
+
+/* ratio for input clk freq */
+static const struct nau8540_fll_attr fll_ratio[] = {
+       { 512000, 0x01 },
+       { 256000, 0x02 },
+       { 128000, 0x04 },
+       { 64000, 0x08 },
+       { 32000, 0x10 },
+       { 8000, 0x20 },
+       { 4000, 0x40 },
+};
+
+static const struct nau8540_fll_attr fll_pre_scalar[] = {
+       { 1, 0x0 },
+       { 2, 0x1 },
+       { 4, 0x2 },
+       { 8, 0x3 },
+};
+
+/* over sampling rate */
+static const struct nau8540_osr_attr osr_adc_sel[] = {
+       { 32, 3 },      /* OSR 32, SRC 1/8 */
+       { 64, 2 },      /* OSR 64, SRC 1/4 */
+       { 128, 1 },     /* OSR 128, SRC 1/2 */
+       { 256, 0 },     /* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8540_reg_defaults[] = {
+       {NAU8540_REG_POWER_MANAGEMENT, 0x0000},
+       {NAU8540_REG_CLOCK_CTRL, 0x0000},
+       {NAU8540_REG_CLOCK_SRC, 0x0000},
+       {NAU8540_REG_FLL1, 0x0001},
+       {NAU8540_REG_FLL2, 0x3126},
+       {NAU8540_REG_FLL3, 0x0008},
+       {NAU8540_REG_FLL4, 0x0010},
+       {NAU8540_REG_FLL5, 0xC000},
+       {NAU8540_REG_FLL6, 0x6000},
+       {NAU8540_REG_FLL_VCO_RSV, 0xF13C},
+       {NAU8540_REG_PCM_CTRL0, 0x000B},
+       {NAU8540_REG_PCM_CTRL1, 0x3010},
+       {NAU8540_REG_PCM_CTRL2, 0x0800},
+       {NAU8540_REG_PCM_CTRL3, 0x0000},
+       {NAU8540_REG_PCM_CTRL4, 0x000F},
+       {NAU8540_REG_ALC_CONTROL_1, 0x0000},
+       {NAU8540_REG_ALC_CONTROL_2, 0x700B},
+       {NAU8540_REG_ALC_CONTROL_3, 0x0022},
+       {NAU8540_REG_ALC_CONTROL_4, 0x1010},
+       {NAU8540_REG_ALC_CONTROL_5, 0x1010},
+       {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000},
+       {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000},
+       {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000},
+       {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000},
+       {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000},
+       {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000},
+       {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000},
+       {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000},
+       {NAU8540_REG_HPF_FILTER_CH12, 0x0000},
+       {NAU8540_REG_HPF_FILTER_CH34, 0x0000},
+       {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002},
+       {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400},
+       {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400},
+       {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400},
+       {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400},
+       {NAU8540_REG_DIGITAL_MUX, 0x00E4},
+       {NAU8540_REG_GPIO_CTRL, 0x0000},
+       {NAU8540_REG_MISC_CTRL, 0x0000},
+       {NAU8540_REG_I2C_CTRL, 0xEFFF},
+       {NAU8540_REG_VMID_CTRL, 0x0000},
+       {NAU8540_REG_MUTE, 0x0000},
+       {NAU8540_REG_ANALOG_ADC1, 0x0011},
+       {NAU8540_REG_ANALOG_ADC2, 0x0020},
+       {NAU8540_REG_ANALOG_PWR, 0x0000},
+       {NAU8540_REG_MIC_BIAS, 0x0004},
+       {NAU8540_REG_REFERENCE, 0x0000},
+       {NAU8540_REG_FEPGA1, 0x0000},
+       {NAU8540_REG_FEPGA2, 0x0000},
+       {NAU8540_REG_FEPGA3, 0x0101},
+       {NAU8540_REG_FEPGA4, 0x0101},
+       {NAU8540_REG_PWR, 0x0000},
+};
+
+static bool nau8540_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV:
+       case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+       case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+       case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE:
+       case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+       case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL:
+       case NAU8540_REG_I2C_DEVICE_ID:
+       case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+       case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+               return true;
+       default:
+               return false;
+       }
+
+}
+
+static bool nau8540_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV:
+       case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+       case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+       case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE:
+       case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+       case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL:
+       case NAU8540_REG_RST:
+       case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+       case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool nau8540_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case NAU8540_REG_SW_RESET:
+       case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS:
+       case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4:
+       case NAU8540_REG_I2C_DEVICE_ID:
+       case NAU8540_REG_RST:
+               return true;
+       default:
+               return false;
+       }
+}
+
+
+static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -12800, 3600);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+
+static const struct snd_kcontrol_new nau8540_snd_controls[] = {
+       SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1,
+               0, 0x520, 0, adc_vol_tlv),
+       SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2,
+               0, 0x520, 0, adc_vol_tlv),
+       SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3,
+               0, 0x520, 0, adc_vol_tlv),
+       SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4,
+               0, 0x520, 0, adc_vol_tlv),
+
+       SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3,
+               0, 0x25, 0, fepga_gain_tlv),
+       SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3,
+               8, 0x25, 0, fepga_gain_tlv),
+       SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4,
+               0, 0x25, 0, fepga_gain_tlv),
+       SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4,
+               8, 0x25, 0, fepga_gain_tlv),
+};
+
+static const char * const adc_channel[] = {
+       "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4"
+};
+static SOC_ENUM_SINGLE_DECL(
+       digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch4_mux =
+       SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch3_mux =
+       SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch2_mux =
+       SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel);
+
+static const struct snd_kcontrol_new digital_ch1_mux =
+       SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
+
+static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0),
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("MIC3"),
+       SND_SOC_DAPM_INPUT("MIC4"),
+
+       SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0),
+
+       SND_SOC_DAPM_ADC("ADC1", NULL,
+               NAU8540_REG_POWER_MANAGEMENT, 0, 0),
+       SND_SOC_DAPM_ADC("ADC2", NULL,
+               NAU8540_REG_POWER_MANAGEMENT, 1, 0),
+       SND_SOC_DAPM_ADC("ADC3", NULL,
+               NAU8540_REG_POWER_MANAGEMENT, 2, 0),
+       SND_SOC_DAPM_ADC("ADC4", NULL,
+               NAU8540_REG_POWER_MANAGEMENT, 3, 0),
+
+       SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0),
+
+       SND_SOC_DAPM_MUX("Digital CH4 Mux",
+               SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
+       SND_SOC_DAPM_MUX("Digital CH3 Mux",
+               SND_SOC_NOPM, 0, 0, &digital_ch3_mux),
+       SND_SOC_DAPM_MUX("Digital CH2 Mux",
+               SND_SOC_NOPM, 0, 0, &digital_ch2_mux),
+       SND_SOC_DAPM_MUX("Digital CH1 Mux",
+               SND_SOC_NOPM, 0, 0, &digital_ch1_mux),
+
+       SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
+       {"Frontend PGA1", NULL, "MIC1"},
+       {"Frontend PGA2", NULL, "MIC2"},
+       {"Frontend PGA3", NULL, "MIC3"},
+       {"Frontend PGA4", NULL, "MIC4"},
+
+       {"ADC1", NULL, "Frontend PGA1"},
+       {"ADC2", NULL, "Frontend PGA2"},
+       {"ADC3", NULL, "Frontend PGA3"},
+       {"ADC4", NULL, "Frontend PGA4"},
+
+       {"ADC CH1", NULL, "ADC1"},
+       {"ADC CH2", NULL, "ADC2"},
+       {"ADC CH3", NULL, "ADC3"},
+       {"ADC CH4", NULL, "ADC4"},
+
+       {"ADC1", NULL, "MICBIAS1"},
+       {"ADC2", NULL, "MICBIAS1"},
+       {"ADC3", NULL, "MICBIAS2"},
+       {"ADC4", NULL, "MICBIAS2"},
+
+       {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
+       {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
+       {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"},
+       {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"},
+
+       {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"},
+       {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"},
+       {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"},
+       {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"},
+
+       {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"},
+       {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"},
+       {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"},
+       {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"},
+
+       {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"},
+       {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"},
+       {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"},
+       {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"},
+
+       {"AIFTX", NULL, "Digital CH1 Mux"},
+       {"AIFTX", NULL, "Digital CH2 Mux"},
+       {"AIFTX", NULL, "Digital CH3 Mux"},
+       {"AIFTX", NULL, "Digital CH4 Mux"},
+};
+
+static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr)
+{
+       int osrate;
+
+       if (osr >= ARRAY_SIZE(osr_adc_sel))
+               return -EINVAL;
+       osrate = osr_adc_sel[osr].osr;
+
+       if (rate * osr > CLK_ADC_MAX) {
+               dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nau8540_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val_len = 0, osr;
+
+       /* CLK_ADC = OSR * FS
+        * ADC clock frequency is defined as Over Sampling Rate (OSR)
+        * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+        * values must be selected such that the maximum frequency is less
+        * than 6.144 MHz.
+        */
+       regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
+       osr &= NAU8540_ADC_OSR_MASK;
+       if (nau8540_clock_check(nau8540, params_rate(params), osr))
+               return -EINVAL;
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+               NAU8540_CLK_ADC_SRC_MASK,
+               osr_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT);
+
+       switch (params_width(params)) {
+       case 16:
+               val_len |= NAU8540_I2S_DL_16;
+               break;
+       case 20:
+               val_len |= NAU8540_I2S_DL_20;
+               break;
+       case 24:
+               val_len |= NAU8540_I2S_DL_24;
+               break;
+       case 32:
+               val_len |= NAU8540_I2S_DL_32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+               NAU8540_I2S_DL_MASK, val_len);
+
+       return 0;
+}
+
+static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+       unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               ctrl2_val |= NAU8540_I2S_MS_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               ctrl1_val |= NAU8540_I2S_BP_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               ctrl1_val |= NAU8540_I2S_DF_I2S;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               ctrl1_val |= NAU8540_I2S_DF_LEFT;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               ctrl1_val |= NAU8540_I2S_DF_RIGTH;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+               ctrl1_val |= NAU8540_I2S_PCMB_EN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+               NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK |
+               NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val);
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+               NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val);
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+               NAU8540_I2S_DO34_OE, 0);
+
+       return 0;
+}
+
+/**
+ * nau8540_set_tdm_slot - configure DAI TX TDM.
+ * @dai: DAI
+ * @tx_mask: bitmask representing active TX slots. Ex.
+ *                 0xf for normal 4 channel TDM.
+ *                 0xf0 for shifted 4 channel TDM
+ * @rx_mask: no used.
+ * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
+ *
+ * Configures a DAI for TDM operation. Only support 4 slots TDM.
+ */
+static int nau8540_set_tdm_slot(struct snd_soc_dai *dai,
+       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+       unsigned int ctrl2_val = 0, ctrl4_val = 0;
+
+       if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)))
+               return -EINVAL;
+
+       ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN);
+       if (tx_mask & 0xf0) {
+               ctrl2_val = 4 * slot_width;
+               ctrl4_val |= (tx_mask >> 4);
+       } else {
+               ctrl4_val |= tx_mask;
+       }
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4,
+               NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN |
+               NAU8540_TDM_TX_MASK, ctrl4_val);
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+               NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE);
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+               NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK,
+               NAU8540_I2S_DO34_OE | ctrl2_val);
+
+       return 0;
+}
+
+
+static const struct snd_soc_dai_ops nau8540_dai_ops = {
+       .hw_params = nau8540_hw_params,
+       .set_fmt = nau8540_set_fmt,
+       .set_tdm_slot = nau8540_set_tdm_slot,
+};
+
+#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000
+#define NAU8540_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8540_dai = {
+       .name = "nau8540-hifi",
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 4,
+               .rates = NAU8540_RATES,
+               .formats = NAU8540_FORMATS,
+       },
+       .ops = &nau8540_dai_ops,
+};
+
+/**
+ * nau8540_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8540_calc_fll_param(unsigned int fll_in,
+       unsigned int fs, struct nau8540_fll *fll_param)
+{
+       u64 fvco, fvco_max;
+       unsigned int fref, i, fvco_sel;
+
+       /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+        * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+        * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK
+        */
+       for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+               fref = fll_in / fll_pre_scalar[i].param;
+               if (fref <= NAU_FREF_MAX)
+                       break;
+       }
+       if (i == ARRAY_SIZE(fll_pre_scalar))
+               return -EINVAL;
+       fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+       /* Choose the FLL ratio based on FREF */
+       for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+               if (fref >= fll_ratio[i].param)
+                       break;
+       }
+       if (i == ARRAY_SIZE(fll_ratio))
+               return -EINVAL;
+       fll_param->ratio = fll_ratio[i].val;
+
+       /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+        * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+        * guaranteed across the full range of operation.
+        * FDCO = freq_out * 2 * mclk_src_scaling
+        */
+       fvco_max = 0;
+       fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+       for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+               fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+               if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+                       fvco_max < fvco) {
+                       fvco_max = fvco;
+                       fvco_sel = i;
+               }
+       }
+       if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+               return -EINVAL;
+       fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+       /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+        * input based on FDCO, FREF and FLL ratio.
+        */
+       fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+       fll_param->fll_int = (fvco >> 16) & 0x3FF;
+       fll_param->fll_frac = fvco & 0xFFFF;
+       return 0;
+}
+
+static void nau8540_fll_apply(struct regmap *regmap,
+       struct nau8540_fll *fll_param)
+{
+       regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC,
+               NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK,
+               NAU8540_CLK_SRC_MCLK | fll_param->mclk_src);
+       regmap_update_bits(regmap, NAU8540_REG_FLL1,
+               NAU8540_FLL_RATIO_MASK, fll_param->ratio);
+       /* FLL 16-bit fractional input */
+       regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac);
+       /* FLL 10-bit integer input */
+       regmap_update_bits(regmap, NAU8540_REG_FLL3,
+               NAU8540_FLL_INTEGER_MASK, fll_param->fll_int);
+       /* FLL pre-scaler */
+       regmap_update_bits(regmap, NAU8540_REG_FLL4,
+               NAU8540_FLL_REF_DIV_MASK,
+               fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT);
+       regmap_update_bits(regmap, NAU8540_REG_FLL5,
+               NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF);
+       regmap_update_bits(regmap,
+               NAU8540_REG_FLL6, NAU8540_DCO_EN, 0);
+       if (fll_param->fll_frac) {
+               regmap_update_bits(regmap, NAU8540_REG_FLL5,
+                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+                       NAU8540_FLL_FTR_SW_MASK,
+                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+                       NAU8540_FLL_FTR_SW_FILTER);
+               regmap_update_bits(regmap, NAU8540_REG_FLL6,
+                       NAU8540_SDM_EN, NAU8540_SDM_EN);
+       } else {
+               regmap_update_bits(regmap, NAU8540_REG_FLL5,
+                       NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+                       NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU);
+               regmap_update_bits(regmap,
+                       NAU8540_REG_FLL6, NAU8540_SDM_EN, 0);
+       }
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8540_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+               unsigned int freq_in, unsigned int freq_out)
+{
+       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+       struct nau8540_fll fll_param;
+       int ret, fs;
+
+       switch (pll_id) {
+       case NAU8540_CLK_FLL_MCLK:
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_MCLK);
+               break;
+
+       case NAU8540_CLK_FLL_BLK:
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_BLK);
+               break;
+
+       case NAU8540_CLK_FLL_FS:
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+                       NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_FS);
+               break;
+
+       default:
+               dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id);
+               return -EINVAL;
+       }
+       dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+               freq_out, pll_id);
+
+       fs = freq_out / 256;
+       ret = nau8540_calc_fll_param(freq_in, fs, &fll_param);
+       if (ret < 0) {
+               dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in);
+               return ret;
+       }
+       dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+               fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+               fll_param.fll_int, fll_param.clk_ref_div);
+
+       nau8540_fll_apply(nau8540->regmap, &fll_param);
+       mdelay(2);
+       regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+               NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+
+       return 0;
+}
+
+static int nau8540_set_sysclk(struct snd_soc_codec *codec,
+       int clk_id, int source, unsigned int freq, int dir)
+{
+       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+
+       switch (clk_id) {
+       case NAU8540_CLK_DIS:
+       case NAU8540_CLK_MCLK:
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+                       NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK);
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+                       NAU8540_DCO_EN, 0);
+               break;
+
+       case NAU8540_CLK_INTERNAL:
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+                       NAU8540_DCO_EN, NAU8540_DCO_EN);
+               regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+                       NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+               break;
+
+       default:
+               dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id);
+               return -EINVAL;
+       }
+
+       dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+               freq, clk_id);
+
+       return 0;
+}
+
+static void nau8540_reset_chip(struct regmap *regmap)
+{
+       regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+       regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+}
+
+static void nau8540_init_regs(struct nau8540 *nau8540)
+{
+       struct regmap *regmap = nau8540->regmap;
+
+       /* Enable Bias/VMID/VMID Tieoff */
+       regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL,
+               NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK,
+               NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT));
+       regmap_update_bits(regmap, NAU8540_REG_REFERENCE,
+               NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN,
+               NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN);
+       mdelay(2);
+       regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS,
+               NAU8540_PU_PRE, NAU8540_PU_PRE);
+       regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+               NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN,
+               NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN);
+       /* ADC OSR selection, CLK_ADC = Fs * OSR */
+       regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE,
+               NAU8540_ADC_OSR_MASK, NAU8540_ADC_OSR_64);
+}
+
+static int __maybe_unused nau8540_suspend(struct snd_soc_codec *codec)
+{
+       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(nau8540->regmap, true);
+       regcache_mark_dirty(nau8540->regmap);
+
+       return 0;
+}
+
+static int __maybe_unused nau8540_resume(struct snd_soc_codec *codec)
+{
+       struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+
+       regcache_cache_only(nau8540->regmap, false);
+       regcache_sync(nau8540->regmap);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver nau8540_codec_driver = {
+       .set_sysclk = nau8540_set_sysclk,
+       .set_pll = nau8540_set_pll,
+       .suspend = nau8540_suspend,
+       .resume = nau8540_resume,
+       .suspend_bias_off = true,
+
+       .component_driver = {
+               .controls = nau8540_snd_controls,
+               .num_controls = ARRAY_SIZE(nau8540_snd_controls),
+               .dapm_widgets = nau8540_dapm_widgets,
+               .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets),
+               .dapm_routes = nau8540_dapm_routes,
+               .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes),
+       },
+};
+
+static const struct regmap_config nau8540_regmap_config = {
+       .val_bits = 16,
+       .reg_bits = 16,
+
+       .max_register = NAU8540_REG_MAX,
+       .readable_reg = nau8540_readable_reg,
+       .writeable_reg = nau8540_writeable_reg,
+       .volatile_reg = nau8540_volatile_reg,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = nau8540_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
+};
+
+static int nau8540_i2c_probe(struct i2c_client *i2c,
+       const struct i2c_device_id *id)
+{
+       struct device *dev = &i2c->dev;
+       struct nau8540 *nau8540 = dev_get_platdata(dev);
+       int ret, value;
+
+       if (!nau8540) {
+               nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL);
+               if (!nau8540)
+                       return -ENOMEM;
+       }
+       i2c_set_clientdata(i2c, nau8540);
+
+       nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config);
+       if (IS_ERR(nau8540->regmap))
+               return PTR_ERR(nau8540->regmap);
+       ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read device id from the NAU85L40: %d\n",
+                       ret);
+               return ret;
+       }
+
+       nau8540->dev = dev;
+       nau8540_reset_chip(nau8540->regmap);
+       nau8540_init_regs(nau8540);
+
+       return snd_soc_register_codec(dev,
+               &nau8540_codec_driver, &nau8540_dai, 1);
+}
+
+static int nau8540_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+
+static const struct i2c_device_id nau8540_i2c_ids[] = {
+       { "nau8540", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8540_of_ids[] = {
+       { .compatible = "nuvoton,nau8540", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, nau8540_of_ids);
+#endif
+
+static struct i2c_driver nau8540_i2c_driver = {
+       .driver = {
+               .name = "nau8540",
+               .of_match_table = of_match_ptr(nau8540_of_ids),
+       },
+       .probe = nau8540_i2c_probe,
+       .remove = nau8540_i2c_remove,
+       .id_table = nau8540_i2c_ids,
+};
+module_i2c_driver(nau8540_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU85L40 driver");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h
new file mode 100644 (file)
index 0000000..d06e651
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __NAU8540_H__
+#define __NAU8540_H__
+
+#define NAU8540_REG_SW_RESET                   0x00
+#define NAU8540_REG_POWER_MANAGEMENT   0x01
+#define NAU8540_REG_CLOCK_CTRL         0x02
+#define NAU8540_REG_CLOCK_SRC                  0x03
+#define NAU8540_REG_FLL1                       0x04
+#define NAU8540_REG_FLL2                       0x05
+#define NAU8540_REG_FLL3                       0x06
+#define NAU8540_REG_FLL4                       0x07
+#define NAU8540_REG_FLL5                       0x08
+#define NAU8540_REG_FLL6                       0x09
+#define NAU8540_REG_FLL_VCO_RSV                0x0A
+#define NAU8540_REG_PCM_CTRL0                  0x10
+#define NAU8540_REG_PCM_CTRL1                  0x11
+#define NAU8540_REG_PCM_CTRL2                  0x12
+#define NAU8540_REG_PCM_CTRL3                  0x13
+#define NAU8540_REG_PCM_CTRL4                  0x14
+#define NAU8540_REG_ALC_CONTROL_1              0x20
+#define NAU8540_REG_ALC_CONTROL_2              0x21
+#define NAU8540_REG_ALC_CONTROL_3              0x22
+#define NAU8540_REG_ALC_CONTROL_4              0x23
+#define NAU8540_REG_ALC_CONTROL_5              0x24
+#define NAU8540_REG_ALC_GAIN_CH12              0x2D
+#define NAU8540_REG_ALC_GAIN_CH34              0x2E
+#define NAU8540_REG_ALC_STATUS         0x2F
+#define NAU8540_REG_NOTCH_FIL1_CH1             0x30
+#define NAU8540_REG_NOTCH_FIL2_CH1             0x31
+#define NAU8540_REG_NOTCH_FIL1_CH2             0x32
+#define NAU8540_REG_NOTCH_FIL2_CH2             0x33
+#define NAU8540_REG_NOTCH_FIL1_CH3             0x34
+#define NAU8540_REG_NOTCH_FIL2_CH3             0x35
+#define NAU8540_REG_NOTCH_FIL1_CH4             0x36
+#define NAU8540_REG_NOTCH_FIL2_CH4             0x37
+#define NAU8540_REG_HPF_FILTER_CH12            0x38
+#define NAU8540_REG_HPF_FILTER_CH34            0x39
+#define NAU8540_REG_ADC_SAMPLE_RATE            0x3A
+#define NAU8540_REG_DIGITAL_GAIN_CH1           0x40
+#define NAU8540_REG_DIGITAL_GAIN_CH2           0x41
+#define NAU8540_REG_DIGITAL_GAIN_CH3           0x42
+#define NAU8540_REG_DIGITAL_GAIN_CH4           0x43
+#define NAU8540_REG_DIGITAL_MUX                0x44
+#define NAU8540_REG_P2P_CH1                    0x48
+#define NAU8540_REG_P2P_CH2                    0x49
+#define NAU8540_REG_P2P_CH3                    0x4A
+#define NAU8540_REG_P2P_CH4                    0x4B
+#define NAU8540_REG_PEAK_CH1                   0x4C
+#define NAU8540_REG_PEAK_CH2                   0x4D
+#define NAU8540_REG_PEAK_CH3                   0x4E
+#define NAU8540_REG_PEAK_CH4                   0x4F
+#define NAU8540_REG_GPIO_CTRL                  0x50
+#define NAU8540_REG_MISC_CTRL                  0x51
+#define NAU8540_REG_I2C_CTRL                   0x52
+#define NAU8540_REG_I2C_DEVICE_ID              0x58
+#define NAU8540_REG_RST                        0x5A
+#define NAU8540_REG_VMID_CTRL                  0x60
+#define NAU8540_REG_MUTE                       0x61
+#define NAU8540_REG_ANALOG_ADC1                0x64
+#define NAU8540_REG_ANALOG_ADC2                0x65
+#define NAU8540_REG_ANALOG_PWR         0x66
+#define NAU8540_REG_MIC_BIAS                   0x67
+#define NAU8540_REG_REFERENCE                  0x68
+#define NAU8540_REG_FEPGA1                     0x69
+#define NAU8540_REG_FEPGA2                     0x6A
+#define NAU8540_REG_FEPGA3                     0x6B
+#define NAU8540_REG_FEPGA4                     0x6C
+#define NAU8540_REG_PWR                        0x6D
+#define NAU8540_REG_MAX                        NAU8540_REG_PWR
+
+
+/* POWER_MANAGEMENT (0x01) */
+#define NAU8540_ADC4_EN                (0x1 << 3)
+#define NAU8540_ADC3_EN                (0x1 << 2)
+#define NAU8540_ADC2_EN                (0x1 << 1)
+#define NAU8540_ADC1_EN                0x1
+
+/* CLOCK_CTRL (0x02) */
+#define NAU8540_CLK_ADC_EN             (0x1 << 15)
+#define NAU8540_CLK_I2S_EN             (0x1 << 1)
+
+/* CLOCK_SRC (0x03) */
+#define NAU8540_CLK_SRC_SFT            15
+#define NAU8540_CLK_SRC_MASK           (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_VCO            (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_MCLK           (0 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_ADC_SRC_SFT        6
+#define NAU8540_CLK_ADC_SRC_MASK       (0x3 << NAU8540_CLK_ADC_SRC_SFT)
+#define NAU8540_CLK_MCLK_SRC_MASK      0xf
+
+/* FLL1 (0x04) */
+#define NAU8540_FLL_RATIO_MASK 0x7f
+
+/* FLL3 (0x06) */
+#define NAU8540_FLL_CLK_SRC_SFT        10
+#define NAU8540_FLL_CLK_SRC_MASK       (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_MCLK       (0 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_BLK        (0x2 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_FS         (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_INTEGER_MASK       0x3ff
+
+/* FLL4 (0x07) */
+#define NAU8540_FLL_REF_DIV_SFT        10
+#define NAU8540_FLL_REF_DIV_MASK       (0x3 << NAU8540_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8540_FLL_LOOP_FTR_EN        (0x1 << 14)
+#define NAU8540_FLL_CLK_SW_MASK        (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_N2          (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8540_FLL_FTR_SW_MASK        (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_ACCU        (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_FILTER      (0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8540_DCO_EN                 (0x1 << 15)
+#define NAU8540_SDM_EN                 (0x1 << 14)
+
+/* PCM_CTRL0 (0x10) */
+#define NAU8540_I2S_BP_SFT             7
+#define NAU8540_I2S_BP_INV             (0x1 << NAU8540_I2S_BP_SFT)
+#define NAU8540_I2S_PCMB_SFT           6
+#define NAU8540_I2S_PCMB_EN            (0x1 << NAU8540_I2S_PCMB_SFT)
+#define NAU8540_I2S_DL_SFT             2
+#define NAU8540_I2S_DL_MASK            (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_16              (0 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_20              (0x1 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_24              (0x2 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_32              (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DF_MASK            0x3
+#define NAU8540_I2S_DF_RIGTH           0
+#define NAU8540_I2S_DF_LEFT            0x1
+#define NAU8540_I2S_DF_I2S             0x2
+#define NAU8540_I2S_DF_PCM_AB          0x3
+
+/* PCM_CTRL1 (0x11) */
+#define NAU8540_I2S_LRC_DIV_SFT        12
+#define NAU8540_I2S_LRC_DIV_MASK       (0x3 << NAU8540_I2S_LRC_DIV_SFT)
+#define NAU8540_I2S_DO12_OE            (0x1 << 4)
+#define NAU8540_I2S_MS_SFT             3
+#define NAU8540_I2S_MS_MASK            (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_MASTER          (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_SLAVE           (0x0 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_BLK_DIV_MASK       0x7
+
+/* PCM_CTRL1 (0x12) */
+#define NAU8540_I2S_DO34_OE            (0x1 << 11)
+#define NAU8540_I2S_TSLOT_L_MASK       0x3ff
+
+/* PCM_CTRL4 (0x14) */
+#define NAU8540_TDM_MODE               (0x1 << 15)
+#define NAU8540_TDM_OFFSET_EN          (0x1 << 14)
+#define NAU8540_TDM_TX_MASK            0xf
+
+/* ADC_SAMPLE_RATE (0x3A) */
+#define NAU8540_ADC_OSR_MASK           0x3
+#define NAU8540_ADC_OSR_256            0x3
+#define NAU8540_ADC_OSR_128            0x2
+#define NAU8540_ADC_OSR_64             0x1
+#define NAU8540_ADC_OSR_32             0x0
+
+/* VMID_CTRL (0x60) */
+#define NAU8540_VMID_EN                (1 << 6)
+#define NAU8540_VMID_SEL_SFT           4
+#define NAU8540_VMID_SEL_MASK          (0x3 << NAU8540_VMID_SEL_SFT)
+
+/* MIC_BIAS (0x67) */
+#define NAU8540_PU_PRE                 (0x1 << 8)
+
+/* REFERENCE (0x68) */
+#define NAU8540_PRECHARGE_DIS          (0x1 << 13)
+#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+
+
+/* System Clock Source */
+enum {
+       NAU8540_CLK_DIS,
+       NAU8540_CLK_MCLK,
+       NAU8540_CLK_INTERNAL,
+       NAU8540_CLK_FLL_MCLK,
+       NAU8540_CLK_FLL_BLK,
+       NAU8540_CLK_FLL_FS,
+};
+
+struct nau8540 {
+       struct device *dev;
+       struct regmap *regmap;
+};
+
+struct nau8540_fll {
+       int mclk_src;
+       int ratio;
+       int fll_frac;
+       int fll_int;
+       int clk_ref_div;
+};
+
+struct nau8540_fll_attr {
+       unsigned int param;
+       unsigned int val;
+};
+
+/* over sampling rate */
+struct nau8540_osr_attr {
+       unsigned int osr;
+       unsigned int clk_src;
+};
+
+
+#endif /* __NAU8540_H__ */
index 39bc02d5bc5dd54880ddee611cbf89000432f5e8..b9d1207ccef26fdc684324a0038771b609749e69 100644 (file)
@@ -402,10 +402,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
        u32 val, mask, shift, reg;
        unsigned int rate, fmt, ratio, max_ratio;
        int i, min_frame_size;
-       snd_pcm_format_t format;
 
        rate = params_rate(params);
-       format = params_format(params);
 
        ratio = pcm3168a->sysclk / rate;
 
index 7150a407ffd9d9e8590e83a9673d36781cf76bf1..d9e96e65e1c43f95cebd224960da0bd53de5c27d 100644 (file)
@@ -1163,6 +1163,13 @@ static const struct dmi_system_id force_combo_jack_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P")
                }
        },
+       {
+               .ident = "Intel Gemini Lake",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Geminilake")
+               }
+       },
        { }
 };
 
index e29a6defefa006dda473856fb30ca64ba152bdd9..b857a715ef648b66dc652a59ea130eeaa02545bb 100644 (file)
@@ -2313,6 +2313,7 @@ MODULE_DEVICE_TABLE(of, rt5640_of_match);
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id rt5640_acpi_match[] = {
        { "INT33CA", 0 },
+       { "10EC3276", 0 },
        { "10EC5640", 0 },
        { "10EC5642", 0 },
        { "INTCCFFD", 0 },
index 1ac96ef9ee2077dc70ba940f939e8162ce1dec26..43dee1b5779ddd101450ede09ce6afe65ccc684e 100644 (file)
@@ -3545,8 +3545,10 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id rt5645_acpi_match[] = {
        { "10EC5645", 0 },
+       { "10EC5648", 0 },
        { "10EC5650", 0 },
        { "10EC5640", 0 },
+       { "10EC3270", 0 },
        {},
 };
 MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
index 97bafac3bc15a7c0739f0aa051162f4337fbc094..17d20b99f0418728d5a73e293e18508c31f2d7b0 100644 (file)
@@ -2814,6 +2814,7 @@ MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
 static const struct acpi_device_id rt5670_acpi_match[] = {
        { "10EC5670", 0},
        { "10EC5672", 0},
+       { "10EC5640", 0}, /* quirk */
        { },
 };
 MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
index e7ab37d0dd325c0614e18ce6b2c671acee1c2915..1fe358e6be6126572a924911742f111bc7eb5c44 100644 (file)
@@ -855,6 +855,8 @@ ARIZONA_LHPF_CONTROL("LHPF2 Coefficients", ARIZONA_HPLPF2_2),
 ARIZONA_LHPF_CONTROL("LHPF3 Coefficients", ARIZONA_HPLPF3_2),
 ARIZONA_LHPF_CONTROL("LHPF4 Coefficients", ARIZONA_HPLPF4_2),
 
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
 ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
 
@@ -1944,7 +1946,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
        if (ret)
                goto err_adsp2_codec_probe;
 
-       arizona_init_spk(codec);
+       ret = arizona_init_spk(codec);
+       if (ret < 0)
+               return ret;
+
        arizona_init_gpio(codec);
        arizona_init_notifiers(codec);
 
index 585fc706c1b0e7b112887be058a755b800ed1ca9..1bc942152effa8f22e7202abd9d245a7ed8eb587 100644 (file)
@@ -778,6 +778,11 @@ SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
 SOC_ENUM("ISRC3 FSH", arizona_isrc_fsh[2]),
 SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
 
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+WM_ADSP2_PRELOAD_SWITCH("DSP2", 2),
+WM_ADSP2_PRELOAD_SWITCH("DSP3", 3),
+WM_ADSP2_PRELOAD_SWITCH("DSP4", 4),
+
 ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE),
@@ -2279,7 +2284,10 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
 
        priv->core.arizona->dapm = dapm;
 
-       arizona_init_spk(codec);
+       ret = arizona_init_spk(codec);
+       if (ret < 0)
+               return ret;
+
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
        arizona_init_notifiers(codec);
index ee0c8639c7435c823af8fcbc0161dcdc1342c35c..49401a8aae64a972ceaf4e1a4d79717a2e9b6603 100644 (file)
@@ -1062,8 +1062,12 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec)
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
        struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = arizona_init_spk(codec);
+       if (ret < 0)
+               return ret;
 
-       arizona_init_spk(codec);
        arizona_init_notifiers(codec);
 
        snd_soc_component_disable_pin(component, "HAPTICS");
index 3694f5958d869da5bc6b676bbce0e78acc769f46..44f447136e224d3206839cbf1dee1a62143cae9f 100644 (file)
@@ -1321,10 +1321,14 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)
        struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+       int ret;
 
        priv->core.arizona->dapm = dapm;
 
-       arizona_init_spk(codec);
+       ret = arizona_init_spk(codec);
+       if (ret < 0)
+               return ret;
+
        arizona_init_gpio(codec);
        arizona_init_notifiers(codec);
 
index d72ccef9e238d39807657597d1c8496022718bb5..d151224ffcca411a5685b2076c43748fd2134790 100644 (file)
@@ -2473,7 +2473,7 @@ static void wm_adsp2_boot_work(struct work_struct *work)
 
        ret = wm_adsp2_ena(dsp);
        if (ret != 0)
-               goto err_mutex;
+               goto err_mem;
 
        ret = wm_adsp_load(dsp);
        if (ret != 0)
@@ -2492,14 +2492,14 @@ static void wm_adsp2_boot_work(struct work_struct *work)
        if (ret != 0)
                goto err_ena;
 
-       dsp->booted = true;
-
        /* Turn DSP back off until we are ready to run */
        ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
                                 ADSP2_SYS_ENA, 0);
        if (ret != 0)
                goto err_ena;
 
+       dsp->booted = true;
+
        mutex_unlock(&dsp->pwr_lock);
 
        return;
@@ -2507,6 +2507,9 @@ static void wm_adsp2_boot_work(struct work_struct *work)
 err_ena:
        regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
                           ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+err_mem:
+       regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+                          ADSP2_MEM_ENA, 0);
 err_mutex:
        mutex_unlock(&dsp->pwr_lock);
 }
@@ -2523,6 +2526,43 @@ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
                adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
 }
 
+int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = dsp->preloaded;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);
+
+int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       char preload[32];
+
+       snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", mc->shift);
+
+       dsp->preloaded = ucontrol->value.integer.value[0];
+
+       if (ucontrol->value.integer.value[0])
+               snd_soc_dapm_force_enable_pin(dapm, preload);
+       else
+               snd_soc_dapm_disable_pin(dapm, preload);
+
+       snd_soc_dapm_sync(dapm);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
+
 int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol, int event,
                         unsigned int freq)
@@ -2538,6 +2578,8 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
                queue_work(system_unbound_wq, &dsp->boot_work);
                break;
        case SND_SOC_DAPM_PRE_PMD:
+               mutex_lock(&dsp->pwr_lock);
+
                wm_adsp_debugfs_clear(dsp);
 
                dsp->fw_id = 0;
@@ -2553,6 +2595,8 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
 
                wm_adsp_free_alg_regions(dsp);
 
+               mutex_unlock(&dsp->pwr_lock);
+
                adsp_dbg(dsp, "Shutdown complete\n");
                break;
        default:
@@ -2575,8 +2619,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        case SND_SOC_DAPM_POST_PMU:
                flush_work(&dsp->boot_work);
 
-               if (!dsp->booted)
-                       return -EIO;
+               mutex_lock(&dsp->pwr_lock);
+
+               if (!dsp->booted) {
+                       ret = -EIO;
+                       goto err;
+               }
 
                ret = wm_adsp2_ena(dsp);
                if (ret != 0)
@@ -2594,18 +2642,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                if (ret != 0)
                        goto err;
 
-               dsp->running = true;
-
-               mutex_lock(&dsp->pwr_lock);
-
                if (wm_adsp_fw[dsp->fw].num_caps != 0) {
                        ret = wm_adsp_buffer_init(dsp);
-                       if (ret < 0) {
-                               mutex_unlock(&dsp->pwr_lock);
+                       if (ret < 0)
                                goto err;
-                       }
                }
 
+               dsp->running = true;
+
                mutex_unlock(&dsp->pwr_lock);
 
                break;
@@ -2648,16 +2692,23 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 err:
        regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
                           ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+       mutex_unlock(&dsp->pwr_lock);
        return ret;
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_event);
 
 int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
 {
-       dsp->codec = codec;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       char preload[32];
+
+       snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num);
+       snd_soc_dapm_disable_pin(dapm, preload);
 
        wm_adsp2_init_debugfs(dsp, codec);
 
+       dsp->codec = codec;
+
        return snd_soc_add_codec_controls(codec,
                                          &wm_adsp_fw_controls[dsp->num - 1],
                                          1);
index 411d062c13f2a6f422a671215e86e4029a0c5caf..3706b11053a331856e7049bc880870f3849fabc8 100644 (file)
@@ -62,6 +62,7 @@ struct wm_adsp {
        int fw;
        int fw_ver;
 
+       bool preloaded;
        bool booted;
        bool running;
 
@@ -86,7 +87,12 @@ struct wm_adsp {
        SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
                wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
 
+#define WM_ADSP2_PRELOAD_SWITCH(wname, num) \
+       SOC_SINGLE_EXT(wname " Preload Switch", SND_SOC_NOPM, num, 1, 0, \
+               wm_adsp2_preloader_get, wm_adsp2_preloader_put)
+
 #define WM_ADSP2(wname, num, event_fn) \
+       SND_SOC_DAPM_SPK(wname " Preload", NULL), \
 {      .id = snd_soc_dapm_supply, .name = wname " Preloader", \
        .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, \
@@ -110,6 +116,11 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
 int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
 
+int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol);
+int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
+                          struct snd_ctl_elem_value *ucontrol);
+
 int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream);
 int wm_adsp_compr_free(struct snd_compr_stream *stream);
 int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
index 731fb0d86c6ab305a499baaa306112353b02782e..7a369e0f2093beb18e740b18f17cc3893da299c0 100644 (file)
@@ -358,13 +358,20 @@ static struct snd_soc_card evm_soc_card = {
 static int davinci_evm_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
-       const struct of_device_id *match =
-               of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
-       struct snd_soc_dai_link *dai = (struct snd_soc_dai_link *) match->data;
+       const struct of_device_id *match;
+       struct snd_soc_dai_link *dai;
        struct snd_soc_card_drvdata_davinci *drvdata = NULL;
        struct clk *mclk;
        int ret = 0;
 
+       match = of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
+       if (!match) {
+               dev_err(&pdev->dev, "Error: No device match found\n");
+               return -ENODEV;
+       }
+
+       dai = (struct snd_soc_dai_link *) match->data;
+
        evm_soc_card.dai_link = dai;
 
        dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0);
index bdf8398cbc81b68b445f202636e8949db5e5cae5..9c46e411202649a31c5a597cebd4af83e2826ce7 100644 (file)
@@ -121,9 +121,14 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
                        irq_valid = true;
                }
 
-               /* Data available. Record mode not supported in PIO mode */
-               if (isr[i] & ISR_RXDA)
+               /*
+                * Data available. Retrieve samples from FIFO
+                * NOTE: Only two channels supported
+                */
+               if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) {
+                       dw_pcm_pop_rx(dev);
                        irq_valid = true;
+               }
 
                /* Error Handling: TX */
                if (isr[i] & ISR_TXFO) {
index 4a83a22fa3cb148cccd50780cc68281c46e675f4..459ec861e6b6c84c04a7597dde059131523d6654 100644 (file)
@@ -41,10 +41,33 @@ static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \
        return tx_ptr; \
 }
 
+#define dw_pcm_rx_fn(sample_bits) \
+static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \
+               struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \
+               bool *period_elapsed) \
+{ \
+       u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
+       unsigned int period_pos = rx_ptr % runtime->period_size; \
+       int i; \
+\
+       for (i = 0; i < dev->fifo_th; i++) { \
+               p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \
+               p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \
+               period_pos++; \
+               if (++rx_ptr >= runtime->buffer_size) \
+                       rx_ptr = 0; \
+       } \
+       *period_elapsed = period_pos >= runtime->period_size; \
+       return rx_ptr; \
+}
+
 dw_pcm_tx_fn(16);
 dw_pcm_tx_fn(32);
+dw_pcm_rx_fn(16);
+dw_pcm_rx_fn(32);
 
 #undef dw_pcm_tx_fn
+#undef dw_pcm_rx_fn
 
 static const struct snd_pcm_hardware dw_pcm_hardware = {
        .info = SNDRV_PCM_INFO_INTERLEAVED |
@@ -57,6 +80,7 @@ static const struct snd_pcm_hardware dw_pcm_hardware = {
        .rate_min = 32000,
        .rate_max = 48000,
        .formats = SNDRV_PCM_FMTBIT_S16_LE |
+               SNDRV_PCM_FMTBIT_S24_LE |
                SNDRV_PCM_FMTBIT_S32_LE,
        .channels_min = 2,
        .channels_max = 2,
@@ -68,27 +92,51 @@ static const struct snd_pcm_hardware dw_pcm_hardware = {
        .fifo_size = 16,
 };
 
-void dw_pcm_push_tx(struct dw_i2s_dev *dev)
+static void dw_pcm_transfer(struct dw_i2s_dev *dev, bool push)
 {
-       struct snd_pcm_substream *tx_substream;
-       bool tx_active, period_elapsed;
+       struct snd_pcm_substream *substream;
+       bool active, period_elapsed;
 
        rcu_read_lock();
-       tx_substream = rcu_dereference(dev->tx_substream);
-       tx_active = tx_substream && snd_pcm_running(tx_substream);
-       if (tx_active) {
-               unsigned int tx_ptr = READ_ONCE(dev->tx_ptr);
-               unsigned int new_tx_ptr = dev->tx_fn(dev, tx_substream->runtime,
-                               tx_ptr, &period_elapsed);
-               cmpxchg(&dev->tx_ptr, tx_ptr, new_tx_ptr);
+       if (push)
+               substream = rcu_dereference(dev->tx_substream);
+       else
+               substream = rcu_dereference(dev->rx_substream);
+       active = substream && snd_pcm_running(substream);
+       if (active) {
+               unsigned int ptr;
+               unsigned int new_ptr;
+
+               if (push) {
+                       ptr = READ_ONCE(dev->tx_ptr);
+                       new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
+                                       &period_elapsed);
+                       cmpxchg(&dev->tx_ptr, ptr, new_ptr);
+               } else {
+                       ptr = READ_ONCE(dev->rx_ptr);
+                       new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
+                                       &period_elapsed);
+                       cmpxchg(&dev->rx_ptr, ptr, new_ptr);
+               }
 
                if (period_elapsed)
-                       snd_pcm_period_elapsed(tx_substream);
+                       snd_pcm_period_elapsed(substream);
        }
        rcu_read_unlock();
 }
+
+void dw_pcm_push_tx(struct dw_i2s_dev *dev)
+{
+       dw_pcm_transfer(dev, true);
+}
 EXPORT_SYMBOL_GPL(dw_pcm_push_tx);
 
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
+{
+       dw_pcm_transfer(dev, false);
+}
+EXPORT_SYMBOL_GPL(dw_pcm_pop_rx);
+
 static int dw_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -126,20 +174,18 @@ static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
        switch (params_format(hw_params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
                dev->tx_fn = dw_pcm_tx_16;
+               dev->rx_fn = dw_pcm_rx_16;
                break;
+       case SNDRV_PCM_FORMAT_S24_LE:
        case SNDRV_PCM_FORMAT_S32_LE:
                dev->tx_fn = dw_pcm_tx_32;
+               dev->rx_fn = dw_pcm_rx_32;
                break;
        default:
                dev_err(dev->dev, "invalid format\n");
                return -EINVAL;
        }
 
-       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
-               dev_err(dev->dev, "only playback is available\n");
-               return -EINVAL;
-       }
-
        ret = snd_pcm_lib_malloc_pages(substream,
                        params_buffer_bytes(hw_params));
        if (ret < 0)
@@ -163,13 +209,21 @@ static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               WRITE_ONCE(dev->tx_ptr, 0);
-               rcu_assign_pointer(dev->tx_substream, substream);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       WRITE_ONCE(dev->tx_ptr, 0);
+                       rcu_assign_pointer(dev->tx_substream, substream);
+               } else {
+                       WRITE_ONCE(dev->rx_ptr, 0);
+                       rcu_assign_pointer(dev->rx_substream, substream);
+               }
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               rcu_assign_pointer(dev->tx_substream, NULL);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       rcu_assign_pointer(dev->tx_substream, NULL);
+               else
+                       rcu_assign_pointer(dev->rx_substream, NULL);
                break;
        default:
                ret = -EINVAL;
@@ -183,7 +237,12 @@ static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct dw_i2s_dev *dev = runtime->private_data;
-       snd_pcm_uframes_t pos = READ_ONCE(dev->tx_ptr);
+       snd_pcm_uframes_t pos;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               pos = READ_ONCE(dev->tx_ptr);
+       else
+               pos = READ_ONCE(dev->rx_ptr);
 
        return pos < runtime->buffer_size ? pos : 0;
 }
index 68afd7577343f4d64a74eb26956edfb00053db3f..91dc70a826f8a28ef6063a9ffec859cfbd385540 100644 (file)
@@ -105,20 +105,27 @@ struct dw_i2s_dev {
        struct i2s_clk_config_data config;
        int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
 
-       /* data related to PIO transfers (TX) */
+       /* data related to PIO transfers */
        bool use_pio;
        struct snd_pcm_substream __rcu *tx_substream;
+       struct snd_pcm_substream __rcu *rx_substream;
        unsigned int (*tx_fn)(struct dw_i2s_dev *dev,
                        struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
                        bool *period_elapsed);
+       unsigned int (*rx_fn)(struct dw_i2s_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+                       bool *period_elapsed);
        unsigned int tx_ptr;
+       unsigned int rx_ptr;
 };
 
 #if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM)
 void dw_pcm_push_tx(struct dw_i2s_dev *dev);
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev);
 int dw_pcm_register(struct platform_device *pdev);
 #else
 void dw_pcm_push_tx(struct dw_i2s_dev *dev) { }
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev) { }
 int dw_pcm_register(struct platform_device *pdev)
 {
        return -EINVAL;
index f200d1cfc4bd4d854b425f220c5b8e3bb49b22b1..667f4215dfc0f07dd58b02c92e7dc542ac5eb1f5 100644 (file)
@@ -26,7 +26,6 @@
 #include <sound/soc.h>
 
 #include "mpc5200_dma.h"
-#include "mpc5200_psc_ac97.h"
 
 #define DRV_NAME "efika-audio-fabric"
 
index 9fadf7e31c5f86459efc0b70fb1674f45da5a289..18e5ce81527d223b98d832fcd7ab6d5ee7ee7879 100644 (file)
@@ -668,7 +668,7 @@ static struct snd_soc_dai_driver fsl_sai_dai = {
        .playback = {
                .stream_name = "CPU-Playback",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rate_min = 8000,
                .rate_max = 192000,
                .rates = SNDRV_PCM_RATE_KNOT,
@@ -677,7 +677,7 @@ static struct snd_soc_dai_driver fsl_sai_dai = {
        .capture = {
                .stream_name = "CPU-Capture",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rate_min = 8000,
                .rate_max = 192000,
                .rates = SNDRV_PCM_RATE_KNOT,
index 243700cc29e618417d2e3561f6c554031229d531..07ee355ee385b16585418025720dfb5a941ab904 100644 (file)
@@ -25,7 +25,6 @@
 #include <asm/mpc52xx_psc.h>
 
 #include "mpc5200_dma.h"
-#include "mpc5200_psc_ac97.h"
 
 #define DRV_NAME "mpc5200-psc-ac97"
 
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h
deleted file mode 100644 (file)
index e881e78..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Freescale MPC5200 PSC in AC97 mode
- * ALSA SoC Digital Audio Interface (DAI) driver
- *
- */
-
-#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
-#define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
-
-#define MPC5200_AC97_NORMAL 0
-#define MPC5200_AC97_SPDIF 1
-
-#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */
index c1610a054d65aafd9bac2032dc05d1f8b68d31eb..33ceb207ee7040ab8f32b5ef5e7546099bd42617 100644 (file)
@@ -123,10 +123,8 @@ static int img_prl_out_hw_params(struct snd_pcm_substream *substream,
        struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai);
        unsigned int rate, channels;
        u32 reg, control_set = 0;
-       snd_pcm_format_t format;
 
        rate = params_rate(params);
-       format = params_format(params);
        channels = params_channels(params);
 
        switch (params_format(params)) {
index fd5d1e0910382e94233702094658b6b868c71b83..526855ad479e3c52358522764f5735fe205bada3 100644 (file)
@@ -2,7 +2,7 @@ config SND_MFLD_MACHINE
        tristate "SOC Machine Audio driver for Intel Medfield MID platform"
        depends on INTEL_SCU_IPC
        select SND_SOC_SN95031
-       select SND_SST_MFLD_PLATFORM
+       select SND_SST_ATOM_HIFI2_PLATFORM
        select SND_SST_IPC_PCI
        help
           This adds support for ASoC machine driver for Intel(R) MID Medfield platform
@@ -10,7 +10,7 @@ config SND_MFLD_MACHINE
           Say Y if you have such a device.
           If unsure select "N".
 
-config SND_SST_MFLD_PLATFORM
+config SND_SST_ATOM_HIFI2_PLATFORM
        tristate
        select SND_SOC_COMPRESS
 
@@ -31,13 +31,10 @@ config SND_SOC_INTEL_SST
        tristate
        select SND_SOC_INTEL_SST_ACPI if ACPI
        select SND_SOC_INTEL_SST_MATCH if ACPI
-       depends on (X86 || COMPILE_TEST)
 
-# firmware stuff depends DW_DMAC_CORE; since there is no depends-on from
-# the reverse selection, each machine driver needs to select
-# SND_SOC_INTEL_SST_FIRMWARE carefully depending on DW_DMAC_CORE
 config SND_SOC_INTEL_SST_FIRMWARE
        tristate
+       select DW_DMAC_CORE
 
 config SND_SOC_INTEL_SST_ACPI
        tristate
@@ -47,16 +44,18 @@ config SND_SOC_INTEL_SST_MATCH
 
 config SND_SOC_INTEL_HASWELL
        tristate
+       select SND_SOC_INTEL_SST
        select SND_SOC_INTEL_SST_FIRMWARE
 
 config SND_SOC_INTEL_BAYTRAIL
        tristate
+       select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SST_FIRMWARE
 
 config SND_SOC_INTEL_HASWELL_MACH
        tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
        depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
-       depends on DW_DMAC_CORE
-       select SND_SOC_INTEL_SST
+       depends on DMADEVICES
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT5640
        help
@@ -68,7 +67,6 @@ config SND_SOC_INTEL_HASWELL_MACH
 config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH
        tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode"
        depends on X86 && ACPI && I2C
-       select SND_SOC_INTEL_SST
        select SND_SOC_INTEL_SKYLAKE
        select SND_SOC_DA7219
        select SND_SOC_MAX98357A
@@ -84,7 +82,6 @@ config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH
 config SND_SOC_INTEL_BXT_RT298_MACH
        tristate "ASoC Audio driver for Broxton with RT298 I2S mode"
        depends on X86 && ACPI && I2C
-       select SND_SOC_INTEL_SST
        select SND_SOC_INTEL_SKYLAKE
        select SND_SOC_RT298
        select SND_SOC_DMIC
@@ -99,9 +96,8 @@ config SND_SOC_INTEL_BXT_RT298_MACH
 config SND_SOC_INTEL_BYT_RT5640_MACH
        tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
        depends on X86_INTEL_LPSS && I2C
-       depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
-       select SND_SOC_INTEL_SST
-       select SND_SOC_INTEL_SST_FIRMWARE
+       depends on DMADEVICES
+       depends on SND_SST_IPC_ACPI = n
        select SND_SOC_INTEL_BAYTRAIL
        select SND_SOC_RT5640
        help
@@ -112,9 +108,8 @@ config SND_SOC_INTEL_BYT_RT5640_MACH
 config SND_SOC_INTEL_BYT_MAX98090_MACH
        tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
        depends on X86_INTEL_LPSS && I2C
-       depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
-       select SND_SOC_INTEL_SST
-       select SND_SOC_INTEL_SST_FIRMWARE
+       depends on DMADEVICES
+       depends on SND_SST_IPC_ACPI = n
        select SND_SOC_INTEL_BAYTRAIL
        select SND_SOC_MAX98090
        help
@@ -123,9 +118,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
 
 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
+       depends on X86_INTEL_LPSS && GPIOLIB && I2C
+       depends on DMADEVICES
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT5677
        help
@@ -134,10 +128,8 @@ config SND_SOC_INTEL_BDW_RT5677_MACH
 
 config SND_SOC_INTEL_BROADWELL_MACH
        tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
-       depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
-                  I2C_DESIGNWARE_PLATFORM
-       depends on DW_DMAC_CORE
-       select SND_SOC_INTEL_SST
+       depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
+       depends on DMADEVICES
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT286
        help
@@ -150,7 +142,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH
         tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"
        depends on X86 && I2C && ACPI
        select SND_SOC_RT5640
-       select SND_SST_MFLD_PLATFORM
+       select SND_SST_ATOM_HIFI2_PLATFORM
        select SND_SST_IPC_ACPI
        select SND_SOC_INTEL_SST_MATCH if ACPI
        help
@@ -163,7 +155,7 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
         tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec"
        depends on X86 && I2C && ACPI
        select SND_SOC_RT5651
-       select SND_SST_MFLD_PLATFORM
+       select SND_SST_ATOM_HIFI2_PLATFORM
        select SND_SST_IPC_ACPI
        select SND_SOC_INTEL_SST_MATCH if ACPI
        help
@@ -176,7 +168,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
         tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
         depends on X86_INTEL_LPSS && I2C && ACPI
         select SND_SOC_RT5670
-        select SND_SST_MFLD_PLATFORM
+        select SND_SST_ATOM_HIFI2_PLATFORM
         select SND_SST_IPC_ACPI
        select SND_SOC_INTEL_SST_MATCH if ACPI
         help
@@ -189,7 +181,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
        tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
        depends on X86_INTEL_LPSS && I2C && ACPI
        select SND_SOC_RT5645
-       select SND_SST_MFLD_PLATFORM
+       select SND_SST_ATOM_HIFI2_PLATFORM
        select SND_SST_IPC_ACPI
        select SND_SOC_INTEL_SST_MATCH if ACPI
        help
@@ -202,7 +194,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
        depends on X86_INTEL_LPSS && I2C && ACPI
        select SND_SOC_MAX98090
        select SND_SOC_TS3A227E
-       select SND_SST_MFLD_PLATFORM
+       select SND_SST_ATOM_HIFI2_PLATFORM
        select SND_SST_IPC_ACPI
        select SND_SOC_INTEL_SST_MATCH if ACPI
        help
@@ -220,7 +212,6 @@ config SND_SOC_INTEL_SKYLAKE
 config SND_SOC_INTEL_SKL_RT286_MACH
        tristate "ASoC Audio driver for SKL with RT286 I2S mode"
        depends on X86 && ACPI && I2C
-       select SND_SOC_INTEL_SST
        select SND_SOC_INTEL_SKYLAKE
        select SND_SOC_RT286
        select SND_SOC_DMIC
@@ -234,7 +225,6 @@ config SND_SOC_INTEL_SKL_RT286_MACH
 config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
        tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode"
        depends on X86_INTEL_LPSS && I2C
-       select SND_SOC_INTEL_SST
        select SND_SOC_INTEL_SKYLAKE
        select SND_SOC_NAU8825
        select SND_SOC_SSM4567
@@ -249,7 +239,6 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
 config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
        tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode"
        depends on X86_INTEL_LPSS && I2C
-       select SND_SOC_INTEL_SST
        select SND_SOC_INTEL_SKYLAKE
        select SND_SOC_NAU8825
        select SND_SOC_MAX98357A
index 2b45435e6245c6c517e98f6558d5b591576e52e6..cdd495f7ee2c46e6ec4831a97ce88f2d69ec7f8a 100644 (file)
@@ -4,7 +4,7 @@ obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
 # Platform Support
 obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
 obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
-obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += atom/
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/
 
 # Machine support
index ce8074fa6d667c7abe1214d36c47a80ffa75c291..aa6548c6feab0a530e805e08b965637fbe9e5407 100644 (file)
@@ -1,7 +1,8 @@
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
-               sst-mfld-platform-compress.o sst-atom-controls.o
+snd-soc-sst-atom-hifi2-platform-objs :=        sst-mfld-platform-pcm.o \
+                                       sst-mfld-platform-compress.o \
+                                       sst-atom-controls.o
 
-obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-soc-sst-atom-hifi2-platform.o
 
 # DSP driver
 obj-$(CONFIG_SND_SST_IPC) += sst/
index c7b3cbf92faf2508b4aee69734b3a335cf547dc5..0f3604b5594240ed74b6fb1d4661efe36665b147 100644 (file)
@@ -801,13 +801,11 @@ static int sst_get_frame_sync_polarity(struct snd_soc_dai *dai,
 
        switch (format) {
        case SND_SOC_DAIFMT_NB_NF:
-               return SSP_FS_ACTIVE_LOW;
-       case SND_SOC_DAIFMT_NB_IF:
+       case SND_SOC_DAIFMT_IB_NF:
                return SSP_FS_ACTIVE_HIGH;
+       case SND_SOC_DAIFMT_NB_IF:
        case SND_SOC_DAIFMT_IB_IF:
                return SSP_FS_ACTIVE_LOW;
-       case SND_SOC_DAIFMT_IB_NF:
-               return SSP_FS_ACTIVE_HIGH;
        default:
                dev_err(dai->dev, "Invalid frame sync polarity %d\n", format);
        }
@@ -1087,8 +1085,8 @@ static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
        SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
        SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
        SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
-       SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
-       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_STEREO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_STEREO, sst_set_media_loop),
        SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
 
        /* Media Mixers */
index f5a8050351b53a0fdfc60af61a176cf3e60b08a2..21cac1c8dd4cb295cf9013386ea6f4549e14d1bf 100644 (file)
@@ -357,14 +357,14 @@ static void sst_media_close(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
        struct sst_runtime_stream *stream;
-       int ret_val = 0, str_id;
+       int str_id;
 
        stream = substream->runtime->private_data;
        power_down_sst(stream);
 
        str_id = stream->stream_info.str_id;
        if (str_id)
-               ret_val = stream->ops->close(sst->dev, str_id);
+               stream->ops->close(sst->dev, str_id);
        module_put(sst->dev->driver->owner);
        kfree(stream);
 }
@@ -839,4 +839,5 @@ MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
 MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
 MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
 MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-atom-hifi2-platform");
 MODULE_ALIAS("platform:sst-mfld-platform");
index f4d92bbc5373c953a5e750210a4159ae6b6594dc..747c0f393d2dfd52c000ca3ef911e625a26ba0be 100644 (file)
@@ -400,6 +400,7 @@ static int sst_acpi_remove(struct platform_device *pdev)
 static unsigned long cht_machine_id;
 
 #define CHT_SURFACE_MACH 1
+#define BYT_THINKPAD_10  2
 
 static int cht_surface_quirk_cb(const struct dmi_system_id *id)
 {
@@ -407,6 +408,23 @@ static int cht_surface_quirk_cb(const struct dmi_system_id *id)
        return 1;
 }
 
+static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id)
+{
+       cht_machine_id = BYT_THINKPAD_10;
+       return 1;
+}
+
+
+static const struct dmi_system_id byt_table[] = {
+       {
+               .callback = byt_thinkpad10_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
+               },
+       },
+       { }
+};
 
 static const struct dmi_system_id cht_table[] = {
        {
@@ -424,6 +442,10 @@ static struct sst_acpi_mach cht_surface_mach = {
        "10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
                                                                &chv_platform_data };
 
+static struct sst_acpi_mach byt_thinkpad_10 = {
+       "10EC5640", "cht-bsw-rt5672", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
+                                                               &byt_rvp_platform_data };
+
 static struct sst_acpi_mach *cht_quirk(void *arg)
 {
        struct sst_acpi_mach *mach = arg;
@@ -436,8 +458,21 @@ static struct sst_acpi_mach *cht_quirk(void *arg)
                return mach;
 }
 
+static struct sst_acpi_mach *byt_quirk(void *arg)
+{
+       struct sst_acpi_mach *mach = arg;
+
+       dmi_check_system(byt_table);
+
+       if (cht_machine_id == BYT_THINKPAD_10)
+               return &byt_thinkpad_10;
+       else
+               return mach;
+}
+
+
 static struct sst_acpi_mach sst_acpi_bytcr[] = {
-       {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
+       {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", byt_quirk,
                                                &byt_rvp_platform_data },
        {"10EC5642", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
                                                &byt_rvp_platform_data },
@@ -445,6 +480,12 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = {
                                                &byt_rvp_platform_data },
        {"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,
                                                &byt_rvp_platform_data },
+       /* some Baytrail platforms rely on RT5645, use CHT machine driver */
+       {"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
+                                               &byt_rvp_platform_data },
+       {"10EC5648", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
+                                               &byt_rvp_platform_data },
+
        {},
 };
 
@@ -458,12 +499,19 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
                                                &chv_platform_data },
        {"10EC5650", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
                                                &chv_platform_data },
+       {"10EC3270", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+                                               &chv_platform_data },
+
        {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
                                                &chv_platform_data },
        /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
        {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
                                                &chv_platform_data },
-
+       {"10EC3276", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
+                                               &chv_platform_data },
+       /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */
+       {"10EC5651", "bytcr_rt5651", "intel/fw_sst_22a8.bin", "bytcr_rt5651", NULL,
+                                               &chv_platform_data },
        {},
 };
 
index 374bb61c596d8f928c3b76ea33be0be1a3cf37d9..14c2d9d1818010fd8fcb1192a79873808b7107b2 100644 (file)
@@ -260,10 +260,8 @@ static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
        u32 data_size, i;
        void *data_offset;
        struct stream_info *stream;
-       union ipc_header_high msg_high;
        u32 msg_low, pipe_id;
 
-       msg_high = msg->mrfld_header.p.header_high;
        msg_low = msg->mrfld_header.p.header_low_payload;
        msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
        data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
index 51bdeeecb7c842fdf8035716d3402a607a0ebf37..83d8dda152331054650f6fca0d5d09d160d73108 100644 (file)
@@ -394,7 +394,6 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
 {
        int retval = 0;
        struct stream_info *str_info;
-       struct intel_sst_ops *ops;
 
        dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
 
@@ -407,7 +406,6 @@ int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
        str_info = get_stream_info(sst_drv_ctx, str_id);
        if (!str_info)
                return -EINVAL;
-       ops = sst_drv_ctx->ops;
 
        mutex_lock(&str_info->lock);
        if (str_info->status != STREAM_UN_INIT) {
index 4d7e9decfa92e476af9f6dbc66454ec603ad1c85..faf865bb176540c8b17d57df4a495aacc0000a98 100644 (file)
@@ -270,6 +270,8 @@ static int broadwell_audio_probe(struct platform_device *pdev)
 {
        broadwell_rt286.dev = &pdev->dev;
 
+       snd_soc_set_dmi_name(&broadwell_rt286, NULL);
+
        return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
 }
 
index 1b4330cd273921a1f5f0ad393d6ac8b8b579425a..2cda06cde4d13823f97e90ebfa5d242cd780dd4f 100644 (file)
 #define QUAD_CHANNEL           4
 
 static struct snd_soc_jack broxton_headset;
+static struct snd_soc_jack broxton_hdmi[3];
+
+struct bxt_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+};
+
+struct bxt_card_private {
+       struct list_head hdmi_pcm_list;
+};
 
 enum {
        BXT_DPCM_AUDIO_PB = 0,
@@ -84,9 +95,9 @@ static const struct snd_soc_dapm_route broxton_map[] = {
        {"codec0_in", NULL, "ssp1 Rx"},
        {"ssp1 Rx", NULL, "Capture"},
 
-       {"HDMI1", NULL, "hif5 Output"},
-       {"HDMI2", NULL, "hif6 Output"},
-       {"HDMI3", NULL, "hif7 Output"},
+       {"HDMI1", NULL, "hif5-0 Output"},
+       {"HDMI2", NULL, "hif6-0 Output"},
+       {"HDMI2", NULL, "hif7-0 Output"},
 
        {"hifi3", NULL, "iDisp3 Tx"},
        {"iDisp3 Tx", NULL, "iDisp3_out"},
@@ -147,9 +158,20 @@ static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct bxt_card_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct bxt_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
 
-       return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
+       pcm->device = BXT_DPCM_AUDIO_HDMI1_PB + dai->id;
+       pcm->codec_dai = dai;
+
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd)
@@ -357,7 +379,6 @@ static struct snd_soc_dai_link broxton_dais[] = {
                .platform_name = "0000:00:0e.0",
                .init = NULL,
                .dpcm_capture = 1,
-               .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
                .ops = &broxton_refcap_ops,
@@ -497,6 +518,40 @@ static struct snd_soc_dai_link broxton_dais[] = {
        },
 };
 
+#define NAME_SIZE      32
+static int bxt_card_late_probe(struct snd_soc_card *card)
+{
+       struct bxt_card_private *ctx = snd_soc_card_get_drvdata(card);
+       struct bxt_hdmi_pcm *pcm;
+       struct snd_soc_codec *codec = NULL;
+       int err, i = 0;
+       char jack_name[NAME_SIZE];
+
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               codec = pcm->codec_dai->codec;
+               snprintf(jack_name, sizeof(jack_name),
+                       "HDMI/DP, pcm=%d Jack", pcm->device);
+               err = snd_soc_card_jack_new(card, jack_name,
+                                       SND_JACK_AVOUT, &broxton_hdmi[i],
+                                       NULL, 0);
+
+               if (err)
+                       return err;
+
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+                                               &broxton_hdmi[i]);
+               if (err < 0)
+                       return err;
+
+               i++;
+       }
+
+       if (!codec)
+               return -EINVAL;
+
+       return hdac_hdmi_jack_port_init(codec, &card->dapm);
+}
+
 /* broxton audio machine driver for SPT + da7219 */
 static struct snd_soc_card broxton_audio_card = {
        .name = "bxtda7219max",
@@ -510,11 +565,22 @@ static struct snd_soc_card broxton_audio_card = {
        .dapm_routes = broxton_map,
        .num_dapm_routes = ARRAY_SIZE(broxton_map),
        .fully_routed = true,
+       .late_probe = bxt_card_late_probe,
 };
 
 static int broxton_audio_probe(struct platform_device *pdev)
 {
+       struct bxt_card_private *ctx;
+
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
        broxton_audio_card.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&broxton_audio_card, ctx);
+
        return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card);
 }
 
index 1309405b38085eb5c1dc90d6d52a56eb7573fcde..176c080a98187080c59881147c5c0617089ce936 100644 (file)
 #include "../../codecs/hdac_hdmi.h"
 #include "../../codecs/rt298.h"
 
-static struct snd_soc_jack broxton_headset;
 /* Headset jack detection DAPM pins */
+static struct snd_soc_jack broxton_headset;
+static struct snd_soc_jack broxton_hdmi[3];
+
+struct bxt_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+};
+
+struct bxt_rt286_private {
+       struct list_head hdmi_pcm_list;
+};
 
 enum {
        BXT_DPCM_AUDIO_PB = 0,
@@ -82,9 +93,9 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
        {"DMIC1 Pin", NULL, "DMIC2"},
        {"DMic", NULL, "SoC DMIC"},
 
-       {"HDMI1", NULL, "hif5 Output"},
-       {"HDMI2", NULL, "hif6 Output"},
-       {"HDMI3", NULL, "hif7 Output"},
+       {"HDMI1", NULL, "hif5-0 Output"},
+       {"HDMI2", NULL, "hif6-0 Output"},
+       {"HDMI2", NULL, "hif7-0 Output"},
 
        /* CODEC BE connections */
        { "AIF1 Playback", NULL, "ssp5 Tx"},
@@ -139,9 +150,20 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct bxt_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct bxt_hdmi_pcm *pcm;
 
-       return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+
+       pcm->device = BXT_DPCM_AUDIO_HDMI1_PB + dai->id;
+       pcm->codec_dai = dai;
+
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -432,6 +454,41 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
        },
 };
 
+#define NAME_SIZE      32
+static int bxt_card_late_probe(struct snd_soc_card *card)
+{
+       struct bxt_rt286_private *ctx = snd_soc_card_get_drvdata(card);
+       struct bxt_hdmi_pcm *pcm;
+       struct snd_soc_codec *codec = NULL;
+       int err, i = 0;
+       char jack_name[NAME_SIZE];
+
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               codec = pcm->codec_dai->codec;
+               snprintf(jack_name, sizeof(jack_name),
+                       "HDMI/DP, pcm=%d Jack", pcm->device);
+               err = snd_soc_card_jack_new(card, jack_name,
+                                       SND_JACK_AVOUT, &broxton_hdmi[i],
+                                       NULL, 0);
+
+               if (err)
+                       return err;
+
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+                                               &broxton_hdmi[i]);
+               if (err < 0)
+                       return err;
+
+               i++;
+       }
+
+       if (!codec)
+               return -EINVAL;
+
+       return hdac_hdmi_jack_port_init(codec, &card->dapm);
+}
+
+
 /* broxton audio machine driver for SPT + RT298S */
 static struct snd_soc_card broxton_rt298 = {
        .name = "broxton-rt298",
@@ -445,11 +502,22 @@ static struct snd_soc_card broxton_rt298 = {
        .dapm_routes = broxton_rt298_map,
        .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map),
        .fully_routed = true,
+       .late_probe = bxt_card_late_probe,
+
 };
 
 static int broxton_audio_probe(struct platform_device *pdev)
 {
+       struct bxt_rt286_private *ctx;
+
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
        broxton_rt298.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&broxton_rt298, ctx);
 
        return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298);
 }
index 8d2fb2d6f532c833726e5dfbac2fe191eb95a15e..5c7219fb3aa86738a49cff98bf6f16693f7a0192 100644 (file)
@@ -386,6 +386,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = {
                                                 BYT_RT5640_MCLK_EN |
                                                 BYT_RT5640_SSP0_AIF1),
 
+       },
+       {
+               .callback = byt_rt5640_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+               },
+               .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP |
+                                                BYT_RT5640_MCLK_EN |
+                                                BYT_RT5640_SSP0_AIF1),
+
        },
        {}
 };
@@ -546,7 +556,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
                 */
                ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
                                        SND_SOC_DAIFMT_I2S     |
-                                       SND_SOC_DAIFMT_NB_IF   |
+                                       SND_SOC_DAIFMT_NB_NF   |
                                        SND_SOC_DAIFMT_CBS_CFS
                        );
                if (ret < 0) {
@@ -572,7 +582,7 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
                 */
                ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
                                        SND_SOC_DAIFMT_I2S     |
-                                       SND_SOC_DAIFMT_NB_IF   |
+                                       SND_SOC_DAIFMT_NB_NF   |
                                        SND_SOC_DAIFMT_CBS_CFS
                        );
                if (ret < 0) {
@@ -856,7 +866,6 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
 static struct platform_driver snd_byt_rt5640_mc_driver = {
        .driver = {
                .name = "bytcr_rt5640",
-               .pm = &snd_soc_pm_ops,
        },
        .probe = snd_byt_rt5640_mc_probe,
 };
index 2d24dc04b5977a3bc8534e7e53780f6fb9f83547..3186f015939fb5fce3e8393fd40463362464dbf8 100644 (file)
@@ -185,7 +185,7 @@ static int byt_rt5651_codec_fixup(struct snd_soc_pcm_runtime *rtd,
         */
        ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
                                  SND_SOC_DAIFMT_I2S     |
-                                 SND_SOC_DAIFMT_NB_IF   |
+                                 SND_SOC_DAIFMT_NB_NF   |
                                  SND_SOC_DAIFMT_CBS_CFS
                                  );
 
@@ -319,7 +319,6 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
 static struct platform_driver snd_byt_rt5651_mc_driver = {
        .driver = {
                .name = "bytcr_rt5651",
-               .pm = &snd_soc_pm_ops,
        },
        .probe = snd_byt_rt5651_mc_probe,
 };
index f504a0e18f9136217c3aafa990258ac1c3e92376..5bcde01d15e68d31dd6292ba9a7a8deedf7f0161 100644 (file)
 #include <linux/module.h>
 #include <linux/acpi.h>
 #include <linux/platform_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>
@@ -33,7 +37,8 @@
 #include "../common/sst-acpi.h"
 
 #define CHT_PLAT_CLK_3_HZ      19200000
-#define CHT_CODEC_DAI  "rt5645-aif1"
+#define CHT_CODEC_DAI1 "rt5645-aif1"
+#define CHT_CODEC_DAI2 "rt5645-aif2"
 
 struct cht_acpi_card {
        char *codec_id;
@@ -45,15 +50,36 @@ struct cht_mc_private {
        struct snd_soc_jack jack;
        struct cht_acpi_card *acpi_card;
        char codec_name[16];
+       struct clk *mclk;
 };
 
+#define CHT_RT5645_MAP(quirk)  ((quirk) & 0xff)
+#define CHT_RT5645_SSP2_AIF2     BIT(16) /* default is using AIF1  */
+#define CHT_RT5645_SSP0_AIF1     BIT(17)
+#define CHT_RT5645_SSP0_AIF2     BIT(18)
+
+static unsigned long cht_rt5645_quirk = 0;
+
+static void log_quirks(struct device *dev)
+{
+       if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2)
+               dev_info(dev, "quirk SSP2_AIF2 enabled");
+       if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1)
+               dev_info(dev, "quirk SSP0_AIF1 enabled");
+       if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)
+               dev_info(dev, "quirk SSP0_AIF2 enabled");
+}
+
 static inline struct snd_soc_dai *cht_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, CHT_CODEC_DAI,
-                            strlen(CHT_CODEC_DAI)))
+               if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI1,
+                            strlen(CHT_CODEC_DAI1)))
+                       return rtd->codec_dai;
+               if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI2,
+                            strlen(CHT_CODEC_DAI2)))
                        return rtd->codec_dai;
        }
        return NULL;
@@ -65,6 +91,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
        struct snd_soc_dapm_context *dapm = w->dapm;
        struct snd_soc_card *card = dapm->card;
        struct snd_soc_dai *codec_dai;
+       struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card);
        int ret;
 
        codec_dai = cht_get_codec_dai(card);
@@ -73,19 +100,30 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
                return -EIO;
        }
 
-       if (!SND_SOC_DAPM_EVENT_OFF(event))
-               return 0;
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               if (ctx->mclk) {
+                       ret = clk_prepare_enable(ctx->mclk);
+                       if (ret < 0) {
+                               dev_err(card->dev,
+                                       "could not configure MCLK state");
+                               return ret;
+                       }
+               }
+       } else {
+               /* Set codec sysclk source to its internal clock because codec PLL will
+                * be off when idle and MCLK will also be off when codec is
+                * runtime suspended. Codec needs clock for jack detection and button
+                * press. MCLK is turned off with clock framework or ACPI.
+                */
+               ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
+                                       48000 * 512, SND_SOC_CLOCK_IN);
+               if (ret < 0) {
+                       dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+                       return ret;
+               }
 
-       /* Set codec sysclk source to its internal clock because codec PLL will
-        * be off when idle and MCLK will also be off by ACPI when codec is
-        * runtime suspended. Codec needs clock for jack detection and button
-        * press.
-        */
-       ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
-                       0, SND_SOC_CLOCK_IN);
-       if (ret < 0) {
-               dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
-               return ret;
+               if (ctx->mclk)
+                       clk_disable_unprepare(ctx->mclk);
        }
 
        return 0;
@@ -97,7 +135,7 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
        SND_SOC_DAPM_MIC("Int Mic", NULL),
        SND_SOC_DAPM_SPK("Ext Spk", NULL),
        SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
-                       platform_clock_control, SND_SOC_DAPM_POST_PMD),
+                       platform_clock_control, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 };
 
 static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
@@ -109,12 +147,6 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = {
        {"Headphone", NULL, "HPOR"},
        {"Ext Spk", NULL, "SPOL"},
        {"Ext Spk", NULL, "SPOR"},
-       {"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"},
        {"Int Mic", NULL, "Platform Clock"},
@@ -130,16 +162,42 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = {
        {"Headphone", NULL, "HPOR"},
        {"Ext Spk", NULL, "SPOL"},
        {"Ext Spk", NULL, "SPOR"},
+       {"Headphone", NULL, "Platform Clock"},
+       {"Headset Mic", NULL, "Platform Clock"},
+       {"Int Mic", NULL, "Platform Clock"},
+       {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_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"},
-       {"Int Mic", NULL, "Platform Clock"},
-       {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = {
+       {"AIF2 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, "AIF2 Capture"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = {
+       {"AIF1 Playback", NULL, "ssp0 Tx"},
+       {"ssp0 Tx", NULL, "modem_out"},
+       {"modem_in", NULL, "ssp0 Rx" },
+       {"ssp0 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = {
+       {"AIF2 Playback", NULL, "ssp0 Tx"},
+       {"ssp0 Tx", NULL, "modem_out"},
+       {"modem_in", NULL, "ssp0 Rx" },
+       {"ssp0 Rx", NULL, "AIF2 Capture"},
 };
 
 static const struct snd_kcontrol_new cht_mc_controls[] = {
@@ -185,28 +243,65 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
+/* uncomment when we have a real quirk
+static int cht_rt5645_quirk_cb(const struct dmi_system_id *id)
+{
+       cht_rt5645_quirk = (unsigned long)id->driver_data;
+       return 1;
+}
+*/
+
+static const struct dmi_system_id cht_rt5645_quirk_table[] = {
+       {
+       },
+};
+
 static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 {
        int ret;
        int jack_type;
        struct snd_soc_codec *codec = runtime->codec;
-       struct snd_soc_dai *codec_dai = runtime->codec_dai;
+       struct snd_soc_card *card = runtime->card;
        struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
 
-       /* Select clk_i2s1_asrc as ASRC clock source */
-       rt5645_sel_asrc_clk_src(codec,
-                               RT5645_DA_STEREO_FILTER |
-                               RT5645_DA_MONO_L_FILTER |
-                               RT5645_DA_MONO_R_FILTER |
-                               RT5645_AD_STEREO_FILTER,
-                               RT5645_CLK_SEL_I2S1_ASRC);
+       if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) ||
+           (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
+               /* Select clk_i2s2_asrc as ASRC clock source */
+               rt5645_sel_asrc_clk_src(codec,
+                                       RT5645_DA_STEREO_FILTER |
+                                       RT5645_DA_MONO_L_FILTER |
+                                       RT5645_DA_MONO_R_FILTER |
+                                       RT5645_AD_STEREO_FILTER,
+                                       RT5645_CLK_SEL_I2S2_ASRC);
+       } else {
+               /* Select clk_i2s1_asrc as ASRC clock source */
+               rt5645_sel_asrc_clk_src(codec,
+                                       RT5645_DA_STEREO_FILTER |
+                                       RT5645_DA_MONO_L_FILTER |
+                                       RT5645_DA_MONO_R_FILTER |
+                                       RT5645_AD_STEREO_FILTER,
+                                       RT5645_CLK_SEL_I2S1_ASRC);
+       }
 
-       /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
-       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
-       if (ret < 0) {
-               dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
-               return ret;
+       if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       cht_rt5645_ssp2_aif2_map,
+                                       ARRAY_SIZE(cht_rt5645_ssp2_aif2_map));
+       } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       cht_rt5645_ssp0_aif1_map,
+                                       ARRAY_SIZE(cht_rt5645_ssp0_aif1_map));
+       } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       cht_rt5645_ssp0_aif2_map,
+                                       ARRAY_SIZE(cht_rt5645_ssp0_aif2_map));
+       } else {
+               ret = snd_soc_dapm_add_routes(&card->dapm,
+                                       cht_rt5645_ssp2_aif1_map,
+                                       ARRAY_SIZE(cht_rt5645_ssp2_aif1_map));
        }
+       if (ret)
+               return ret;
 
        if (ctx->acpi_card->codec_type == CODEC_TYPE_RT5650)
                jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
@@ -225,12 +320,33 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
 
        rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack);
 
+       if (ctx->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(ctx->mclk);
+               if (!ret)
+                       clk_disable_unprepare(ctx->mclk);
+
+               ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
+
+               if (ret)
+                       dev_err(runtime->dev, "unable to set MCLK rate\n");
+       }
        return ret;
 }
 
 static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
                            struct snd_pcm_hw_params *params)
 {
+       int ret;
        struct snd_interval *rate = hw_param_interval(params,
                        SNDRV_PCM_HW_PARAM_RATE);
        struct snd_interval *channels = hw_param_interval(params,
@@ -240,8 +356,67 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        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 ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
+               (cht_rt5645_quirk & CHT_RT5645_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_NF   |
+                                       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_fmt(rtd->codec_dai,
+                                       SND_SOC_DAIFMT_I2S     |
+                                       SND_SOC_DAIFMT_NB_NF   |
+                                       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;
+               }
+
+       } else {
+
+               /* set SSP2 to 24-bit */
+               params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+               /*
+                * Default mode for SSP configuration is TDM 4 slot
+                */
+               ret = snd_soc_dai_set_fmt(rtd->codec_dai,
+                                       SND_SOC_DAIFMT_DSP_B |
+                                       SND_SOC_DAIFMT_IB_NF |
+                                       SND_SOC_DAIFMT_CBS_CFS);
+               if (ret < 0) {
+                       dev_err(rtd->dev, "can't set format to TDM %d\n", ret);
+                       return ret;
+               }
+
+               /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+               ret = snd_soc_dai_set_tdm_slot(rtd->codec_dai, 0xF, 0xF, 4, 24);
+               if (ret < 0) {
+                       dev_err(rtd->dev, "can't set codec TDM slot %d\n", ret);
+                       return ret;
+               }
+       }
        return 0;
 }
 
@@ -303,8 +478,6 @@ static struct snd_soc_dai_link cht_dailink[] = {
                .no_pcm = 1,
                .codec_dai_name = "rt5645-aif1",
                .codec_name = "i2c-10EC5645:00",
-               .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
-                                       | SND_SOC_DAIFMT_CBS_CFS,
                .init = cht_codec_init,
                .be_hw_params_fixup = cht_codec_fixup,
                .nonatomic = true,
@@ -344,10 +517,31 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {
 static struct cht_acpi_card snd_soc_cards[] = {
        {"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
        {"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+       {"10EC5648", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
+       {"10EC3270", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
        {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
 };
 
-static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+static char cht_rt5645_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+static char cht_rt5645_codec_aif_name[12]; /*  = "rt5645-aif[1|2]" */
+static char cht_rt5645_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;
+}
+
+struct acpi_chan_package {   /* ACPICA seems to require 64 bit integers */
+       u64 aif_value;       /* 1: AIF1, 2: AIF2 */
+       u64 mclock_value;    /* usually 25MHz (0x17d7940), ignored */
+};
 
 static int snd_cht_mc_probe(struct platform_device *pdev)
 {
@@ -358,22 +552,33 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        struct sst_acpi_mach *mach;
        const char *i2c_name = NULL;
        int dai_index = 0;
+       bool found = false;
+       bool is_bytcr = false;
 
        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
        if (!drv)
                return -ENOMEM;
 
+       mach = (&pdev->dev)->platform_data;
+
        for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
-               if (acpi_dev_found(snd_soc_cards[i].codec_id)) {
+               if (acpi_dev_found(snd_soc_cards[i].codec_id) &&
+                       (!strncmp(snd_soc_cards[i].codec_id, mach->id, 8))) {
                        dev_dbg(&pdev->dev,
                                "found codec %s\n", snd_soc_cards[i].codec_id);
                        card = snd_soc_cards[i].soc_card;
                        drv->acpi_card = &snd_soc_cards[i];
+                       found = true;
                        break;
                }
        }
+
+       if (!found) {
+               dev_err(&pdev->dev, "No matching HID found in supported list\n");
+               return -ENODEV;
+       }
+
        card->dev = &pdev->dev;
-       mach = card->dev->platform_data;
        sprintf(drv->codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
 
        /* set correct codec name */
@@ -386,9 +591,105 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        /* fixup codec name based on HID */
        i2c_name = sst_acpi_find_name_from_hid(mach->id);
        if (i2c_name != NULL) {
-               snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
+               snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name),
                        "%s%s", "i2c-", i2c_name);
-               cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
+               cht_dailink[dai_index].codec_name = cht_rt5645_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;
+
+               if (res_info->acpi_ipc_irq_index == 0)
+                       is_bytcr = true;
+       }
+
+       if (is_bytcr) {
+               /*
+                * Baytrail CR platforms may have CHAN package in BIOS, try
+                * to find relevant routing quirk based as done on Windows
+                * platforms. We have to read the information directly from the
+                * BIOS, at this stage the card is not created and the links
+                * with the codec driver/pdata are non-existent
+                */
+
+               struct acpi_chan_package chan_package;
+
+               /* format specified: 2 64-bit integers */
+               struct acpi_buffer format = {sizeof("NN"), "NN"};
+               struct acpi_buffer state = {0, NULL};
+               struct sst_acpi_package_context pkg_ctx;
+               bool pkg_found = false;
+
+               state.length = sizeof(chan_package);
+               state.pointer = &chan_package;
+
+               pkg_ctx.name = "CHAN";
+               pkg_ctx.length = 2;
+               pkg_ctx.format = &format;
+               pkg_ctx.state = &state;
+               pkg_ctx.data_valid = false;
+
+               pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx);
+               if (pkg_found) {
+                       if (chan_package.aif_value == 1) {
+                               dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n");
+                               cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF1;
+                       } else  if (chan_package.aif_value == 2) {
+                               dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n");
+                               cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
+                       } else {
+                               dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n");
+                               pkg_found = false;
+                       }
+               }
+
+               if (!pkg_found) {
+                       /* no BIOS indications, assume SSP0-AIF2 connection */
+                       cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2;
+               }
+       }
+
+       /* check quirks before creating card */
+       dmi_check_system(cht_rt5645_quirk_table);
+       log_quirks(&pdev->dev);
+
+       if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) ||
+               (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
+
+               /* fixup codec aif name */
+               snprintf(cht_rt5645_codec_aif_name,
+                       sizeof(cht_rt5645_codec_aif_name),
+                       "%s", "rt5645-aif2");
+
+               cht_dailink[dai_index].codec_dai_name =
+                       cht_rt5645_codec_aif_name;
+       }
+
+       if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) ||
+               (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) {
+
+               /* fixup cpu dai name name */
+               snprintf(cht_rt5645_cpu_dai_name,
+                       sizeof(cht_rt5645_cpu_dai_name),
+                       "%s", "ssp0-port");
+
+               cht_dailink[dai_index].cpu_dai_name =
+                       cht_rt5645_cpu_dai_name;
+       }
+
+       if (is_valleyview()) {
+               drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+               if (IS_ERR(drv->mclk)) {
+                       dev_err(&pdev->dev,
+                               "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
+                               PTR_ERR(drv->mclk));
+                       return PTR_ERR(drv->mclk);
+               }
        }
 
        snd_soc_card_set_drvdata(card, drv);
index fddd1cd12f1316206958328547300208bacc16b7..3b12bc1fa518e66409764b22b261d0c532af0b9b 100644 (file)
@@ -32,6 +32,7 @@
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
 static const struct snd_pcm_hw_constraint_list *dmic_constraints;
+static struct snd_soc_jack skylake_hdmi[3];
 
 struct skl_hdmi_pcm {
        struct list_head head;
@@ -111,8 +112,8 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
        SND_SOC_DAPM_SPK("Spk", NULL),
        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
-       SND_SOC_DAPM_SPK("DP", NULL),
-       SND_SOC_DAPM_SPK("HDMI", NULL),
+       SND_SOC_DAPM_SPK("DP1", NULL),
+       SND_SOC_DAPM_SPK("DP2", 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),
@@ -130,9 +131,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
        { "MIC", NULL, "Headset Mic" },
        { "DMic", NULL, "SoC DMIC" },
 
-       {"HDMI", NULL, "hif5 Output"},
-       {"DP", NULL, "hif6 Output"},
-
        /* CODEC BE connections */
        { "HiFi Playback", NULL, "ssp0 Tx" },
        { "ssp0 Tx", NULL, "codec0_out" },
@@ -603,19 +601,39 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
 };
 
+#define NAME_SIZE      32
 static int skylake_card_late_probe(struct snd_soc_card *card)
 {
        struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(card);
        struct skl_hdmi_pcm *pcm;
-       int err;
+       struct snd_soc_codec *codec = NULL;
+       int err, i = 0;
+       char jack_name[NAME_SIZE];
 
        list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
-               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               codec = pcm->codec_dai->codec;
+               snprintf(jack_name, sizeof(jack_name),
+                       "HDMI/DP, pcm=%d Jack", pcm->device);
+               err = snd_soc_card_jack_new(card, jack_name,
+                                       SND_JACK_AVOUT,
+                                       &skylake_hdmi[i],
+                                       NULL, 0);
+
+               if (err)
+                       return err;
+
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+                                               &skylake_hdmi[i]);
                if (err < 0)
                        return err;
+
+               i++;
        }
 
-       return 0;
+       if (!codec)
+               return -EINVAL;
+
+       return hdac_hdmi_jack_port_init(codec, &card->dapm);
 }
 
 /* skylake audio machine driver for SPT + NAU88L25 */
index 8ab865ee0cad7ce13d6c45bf4e361fa1115023b5..eb7751b0599bbcb1ea343b7c284f5b28ce238aae 100644 (file)
@@ -36,6 +36,7 @@
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
 static const struct snd_pcm_hw_constraint_list *dmic_constraints;
+static struct snd_soc_jack skylake_hdmi[3];
 
 struct skl_hdmi_pcm {
        struct list_head head;
@@ -115,8 +116,8 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
        SND_SOC_DAPM_SPK("Left Speaker", NULL),
        SND_SOC_DAPM_SPK("Right Speaker", NULL),
        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
-       SND_SOC_DAPM_SPK("DP", NULL),
-       SND_SOC_DAPM_SPK("HDMI", NULL),
+       SND_SOC_DAPM_SPK("DP1", NULL),
+       SND_SOC_DAPM_SPK("DP2", 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),
@@ -135,8 +136,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
        {"MIC", NULL, "Headset Mic"},
        {"DMic", NULL, "SoC DMIC"},
 
-       {"HDMI", NULL, "hif5 Output"},
-       {"DP", NULL, "hif6 Output"},
        /* CODEC BE connections */
        { "Left Playback", NULL, "ssp0 Tx"},
        { "Right Playback", NULL, "ssp0 Tx"},
@@ -653,19 +652,39 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
 };
 
+#define NAME_SIZE      32
 static int skylake_card_late_probe(struct snd_soc_card *card)
 {
        struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(card);
        struct skl_hdmi_pcm *pcm;
-       int err;
+       struct snd_soc_codec *codec = NULL;
+       int err, i = 0;
+       char jack_name[NAME_SIZE];
 
        list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
-               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               codec = pcm->codec_dai->codec;
+               snprintf(jack_name, sizeof(jack_name),
+                       "HDMI/DP, pcm=%d Jack", pcm->device);
+               err = snd_soc_card_jack_new(card, jack_name,
+                                       SND_JACK_AVOUT,
+                                       &skylake_hdmi[i],
+                                       NULL, 0);
+
+               if (err)
+                       return err;
+
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+                                               &skylake_hdmi[i]);
                if (err < 0)
                        return err;
+
+               i++;
        }
 
-       return 0;
+       if (!codec)
+               return -EINVAL;
+
+       return hdac_hdmi_jack_port_init(codec, &card->dapm);
 }
 
 /* skylake audio machine driver for SPT + NAU88L25 */
index dc5c3611a6ff6417f197a493e63cc5e251b8fe0d..f5ab7b8d51d1ceedb1e2e09033d5a68e97aad435 100644 (file)
@@ -29,6 +29,7 @@
 #include "../../codecs/hdac_hdmi.h"
 
 static struct snd_soc_jack skylake_headset;
+static struct snd_soc_jack skylake_hdmi[3];
 
 struct skl_hdmi_pcm {
        struct list_head head;
@@ -94,10 +95,6 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
        {"DMIC1 Pin", NULL, "DMIC2"},
        {"DMic", NULL, "SoC DMIC"},
 
-       {"HDMI1", NULL, "hif5 Output"},
-       {"HDMI2", NULL, "hif6 Output"},
-       {"HDMI3", NULL, "hif7 Output"},
-
        /* CODEC BE connections */
        { "AIF1 Playback", NULL, "ssp0 Tx"},
        { "ssp0 Tx", NULL, "codec0_out"},
@@ -458,19 +455,38 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
        },
 };
 
+#define NAME_SIZE      32
 static int skylake_card_late_probe(struct snd_soc_card *card)
 {
        struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(card);
        struct skl_hdmi_pcm *pcm;
-       int err;
+       struct snd_soc_codec *codec = NULL;
+       int err, i = 0;
+       char jack_name[NAME_SIZE];
 
        list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
-               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               codec = pcm->codec_dai->codec;
+               snprintf(jack_name, sizeof(jack_name),
+                       "HDMI/DP, pcm=%d Jack", pcm->device);
+               err = snd_soc_card_jack_new(card, jack_name,
+                                       SND_JACK_AVOUT, &skylake_hdmi[i],
+                                       NULL, 0);
+
+               if (err)
+                       return err;
+
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
+                                               &skylake_hdmi[i]);
                if (err < 0)
                        return err;
+
+               i++;
        }
 
-       return 0;
+       if (!codec)
+               return -EINVAL;
+
+       return hdac_hdmi_jack_port_init(codec, &card->dapm);
 }
 
 /* skylake audio machine driver for SPT + RT286S */
index c00ede4ea4d7040c255556802ea8ebddd12bd1ad..11c0805393ff96c50b03b2b29fb70a32b53fa3dc 100644 (file)
@@ -252,44 +252,44 @@ void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset,
 EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_forced);
 
 int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
-                        u32 target, u32 timeout, char *operation)
+                        u32 target, u32 time, char *operation)
 {
-       int time, ret;
        u32 reg;
-       bool done = false;
+       unsigned long timeout;
+       int k = 0, s = 500;
 
        /*
-        * we will poll for couple of ms using mdelay, if not successful
-        * then go to longer sleep using usleep_range
+        * split the loop into sleeps of varying resolution. more accurately,
+        * the range of wakeups are:
+        * Phase 1(first 5ms): min sleep 0.5ms; max sleep 1ms.
+        * Phase 2:( 5ms to 10ms) : min sleep 0.5ms; max sleep 10ms
+        * (usleep_range (500, 1000) and usleep_range(5000, 10000) are
+        * both possible in this phase depending on whether k > 10 or not).
+        * Phase 3: (beyond 10 ms) min sleep 5ms; max sleep 10ms.
         */
 
-       /* check if set state successful */
-       for (time = 0; time < 5; time++) {
-               if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target) {
-                       done = true;
-                       break;
-               }
-               mdelay(1);
+       timeout = jiffies + msecs_to_jiffies(time);
+       while (((sst_dsp_shim_read_unlocked(ctx, offset) & mask) != target)
+               && time_before(jiffies, timeout)) {
+               k++;
+               if (k > 10)
+                       s = 5000;
+
+               usleep_range(s, 2*s);
        }
 
-       if (done ==  false) {
-               /* sleeping in 10ms steps so adjust timeout value */
-               timeout /= 10;
+       reg = sst_dsp_shim_read_unlocked(ctx, offset);
 
-               for (time = 0; time < timeout; time++) {
-                       if ((sst_dsp_shim_read_unlocked(ctx, offset) & mask) == target)
-                               break;
+       if ((reg & mask) == target) {
+               dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s successful\n",
+                                       reg, operation);
 
-                       usleep_range(5000, 10000);
-               }
+               return 0;
        }
 
-       reg = sst_dsp_shim_read_unlocked(ctx, offset);
-       dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
-                       (time < timeout) ? "successful" : "timedout");
-       ret = time < timeout ? 0 : -ETIME;
-
-       return ret;
+       dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s timedout\n",
+                                       reg, operation);
+       return -ETIME;
 }
 EXPORT_SYMBOL_GPL(sst_dsp_register_poll);
 
index 1f9f33d34000b13f752c875680038383c48561a4..15a063a403ccc28762abedb3e0124a6b1e70b78f 100644 (file)
@@ -23,7 +23,6 @@
 #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
@@ -52,7 +51,7 @@ static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
 }
 
 static int
-bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo)
+bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
 {
        struct snd_dma_buffer dmab;
        struct skl_sst *skl = ctx->thread_context;
@@ -61,11 +60,11 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo)
        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);
+       for (i = 1; i < lib_count; i++) {
+               ret = request_firmware(&fw, linfo[i].name, ctx->dev);
                if (ret < 0) {
                        dev_err(ctx->dev, "Request lib %s failed:%d\n",
-                                       minfo->lib[i].name, ret);
+                                       linfo[i].name, ret);
                        return ret;
                }
 
@@ -96,7 +95,7 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo)
                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);
+                                       linfo[i].name, ret);
 
                ctx->dsp_ops.trigger(ctx->dev, false, stream_tag);
                ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag);
@@ -119,8 +118,7 @@ load_library_failed:
 static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
                        const void *fwdata, u32 fwsize)
 {
-       int stream_tag, ret, i;
-       u32 reg;
+       int stream_tag, ret;
 
        stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
        if (stream_tag <= 0) {
@@ -153,23 +151,13 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
        }
 
        /* Step 4: Wait for DONE Bit */
-       for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
-               reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE);
-
-               if (reg & SKL_ADSP_REG_HIPCIE_DONE) {
-                       sst_dsp_shim_update_bits_forced(ctx,
-                                       SKL_ADSP_REG_HIPCIE,
+       ret = sst_dsp_register_poll(ctx, SKL_ADSP_REG_HIPCIE,
                                        SKL_ADSP_REG_HIPCIE_DONE,
-                                       SKL_ADSP_REG_HIPCIE_DONE);
-                       break;
-               }
-               mdelay(1);
-       }
-       if (!i) {
-               dev_info(ctx->dev, "Waiting for HIPCIE done, reg: 0x%x\n", reg);
-               sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCIE,
-                               SKL_ADSP_REG_HIPCIE_DONE,
-                               SKL_ADSP_REG_HIPCIE_DONE);
+                                       SKL_ADSP_REG_HIPCIE_DONE,
+                                       BXT_INIT_TIMEOUT, "HIPCIE Done");
+       if (ret < 0) {
+               dev_err(ctx->dev, "Timout for Purge Request%d\n", ret);
+               goto base_fw_load_failed;
        }
 
        /* Step 5: power down core1 */
@@ -184,19 +172,10 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
        skl_ipc_op_int_enable(ctx);
 
        /* Step 7: Wait for ROM init */
-       for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
-               if (SKL_FW_INIT ==
-                               (sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) &
-                               SKL_FW_STS_MASK)) {
-
-                       dev_info(ctx->dev, "ROM loaded, continue FW loading\n");
-                       break;
-               }
-               mdelay(1);
-       }
-       if (!i) {
-               dev_err(ctx->dev, "Timeout for ROM init, HIPCIE: 0x%x\n", reg);
-               ret = -EIO;
+       ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK,
+                       SKL_FW_INIT, BXT_INIT_TIMEOUT, "ROM Load");
+       if (ret < 0) {
+               dev_err(ctx->dev, "Timeout for ROM init, ret:%d\n", ret);
                goto base_fw_load_failed;
        }
 
@@ -432,7 +411,6 @@ 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;
@@ -442,8 +420,9 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
                        return ret;
                }
 
-               if (minfo->lib_count > 1) {
-                       ret = bxt_load_library(ctx, minfo);
+               if (skl->lib_count > 1) {
+                       ret = bxt_load_library(ctx, skl->lib_info,
+                                               skl->lib_count);
                        if (ret < 0) {
                                dev_err(ctx->dev, "reload libs failed: %d\n", ret);
                                return ret;
@@ -640,8 +619,9 @@ int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx)
 
        skl_dsp_init_core_state(sst);
 
-       if (ctx->manifest.lib_count > 1) {
-               ret = sst->fw_ops.load_library(sst, &ctx->manifest);
+       if (ctx->lib_count > 1) {
+               ret = sst->fw_ops.load_library(sst, ctx->lib_info,
+                                               ctx->lib_count);
                if (ret < 0) {
                        dev_err(dev, "Load Library failed : %x\n", ret);
                        return ret;
index e79cbcf6e4629e970f00db484df9433ab9500657..e66870474f1061f2cbe14b5c1014197a033acad6 100644 (file)
@@ -220,6 +220,13 @@ static const struct skl_dsp_ops dsp_ops[] = {
                .init_fw = bxt_sst_init_fw,
                .cleanup = bxt_sst_dsp_cleanup
        },
+       {
+               .id = 0x3198,
+               .loader_ops = bxt_get_loader_ops,
+               .init = bxt_sst_dsp_init,
+               .init_fw = bxt_sst_init_fw,
+               .cleanup = bxt_sst_dsp_cleanup
+       },
 };
 
 const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id)
index 3f8e6f0b7eb5d10647f99efe3c1e5b9be751c2e3..7eb9c419dc7f0a432c0ba0acf7894d41e621aea3 100644 (file)
@@ -102,14 +102,16 @@ static void dump_config(struct device *dev, u32 instance_id, u8 linktype,
 }
 
 static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
-                               u32 instance_id, u8 link_type, u8 dirn)
+               u32 instance_id, u8 link_type, u8 dirn, u8 dev_type)
 {
-       dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d\n",
-               epnt->virtual_bus_id, epnt->linktype, epnt->direction);
+       dev_dbg(dev, "vbus_id=%d link_type=%d dir=%d dev_type = %d\n",
+                       epnt->virtual_bus_id, epnt->linktype,
+                       epnt->direction, epnt->device_type);
 
        if ((epnt->virtual_bus_id == instance_id) &&
                        (epnt->linktype == link_type) &&
-                       (epnt->direction == dirn))
+                       (epnt->direction == dirn) &&
+                       (epnt->device_type == dev_type))
                return true;
        else
                return false;
@@ -117,7 +119,8 @@ static bool skl_check_ep_match(struct device *dev, struct nhlt_endpoint *epnt,
 
 struct nhlt_specific_cfg
 *skl_get_ep_blob(struct skl *skl, u32 instance, u8 link_type,
-                       u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn)
+                       u8 s_fmt, u8 num_ch, u32 s_rate,
+                       u8 dirn, u8 dev_type)
 {
        struct nhlt_fmt *fmt;
        struct nhlt_endpoint *epnt;
@@ -135,7 +138,8 @@ struct nhlt_specific_cfg
        dev_dbg(dev, "endpoint count =%d\n", nhlt->endpoint_count);
 
        for (j = 0; j < nhlt->endpoint_count; j++) {
-               if (skl_check_ep_match(dev, epnt, instance, link_type, dirn)) {
+               if (skl_check_ep_match(dev, epnt, instance, link_type,
+                                               dirn, dev_type)) {
                        fmt = (struct nhlt_fmt *)(epnt->config.caps +
                                                 epnt->config.size);
                        sp_config = skl_get_specific_cfg(dev, fmt, num_ch,
@@ -189,9 +193,9 @@ int skl_get_dmic_geo(struct skl *skl)
        return dmic_geo;
 }
 
-static void skl_nhlt_trim_space(struct skl *skl)
+static void skl_nhlt_trim_space(char *trim)
 {
-       char *s = skl->tplg_name;
+       char *s = trim;
        int cnt;
        int i;
 
@@ -218,7 +222,43 @@ int skl_nhlt_update_topology_bin(struct skl *skl)
                skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,
                nhlt->header.oem_revision, "-tplg.bin");
 
-       skl_nhlt_trim_space(skl);
+       skl_nhlt_trim_space(skl->tplg_name);
 
        return 0;
 }
+
+static ssize_t skl_nhlt_platform_id_show(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct pci_dev *pci = to_pci_dev(dev);
+       struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+       struct skl *skl = ebus_to_skl(ebus);
+       struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+       char platform_id[32];
+
+       sprintf(platform_id, "%x-%.6s-%.8s-%d", skl->pci_id,
+                       nhlt->header.oem_id, nhlt->header.oem_table_id,
+                       nhlt->header.oem_revision);
+
+       skl_nhlt_trim_space(platform_id);
+       return sprintf(buf, "%s\n", platform_id);
+}
+
+static DEVICE_ATTR(platform_id, 0444, skl_nhlt_platform_id_show, NULL);
+
+int skl_nhlt_create_sysfs(struct skl *skl)
+{
+       struct device *dev = &skl->pci->dev;
+
+       if (sysfs_create_file(&dev->kobj, &dev_attr_platform_id.attr))
+               dev_warn(dev, "Error creating sysfs entry\n");
+
+       return 0;
+}
+
+void skl_nhlt_remove_sysfs(struct skl *skl)
+{
+       struct device *dev = &skl->pci->dev;
+
+       sysfs_remove_file(&dev->kobj, &dev_attr_platform_id.attr);
+}
index 6c6b63a6b338f50f0eb218f231506f0017e56241..e12520e142ff5306e926e644e95bf3f81c5d2903 100644 (file)
@@ -137,6 +137,80 @@ static void skl_set_suspend_active(struct snd_pcm_substream *substream,
                skl->supend_active--;
 }
 
+int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       unsigned int format_val;
+       struct hdac_stream *hstream;
+       struct hdac_ext_stream *stream;
+       int err;
+
+       hstream = snd_hdac_get_stream(bus, params->stream,
+                                       params->host_dma_id + 1);
+       if (!hstream)
+               return -EINVAL;
+
+       stream = stream_to_hdac_ext_stream(hstream);
+       snd_hdac_ext_stream_decouple(ebus, stream, true);
+
+       format_val = snd_hdac_calc_stream_format(params->s_freq,
+                               params->ch, params->format, 32, 0);
+
+       dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
+               format_val, params->s_freq, params->ch, params->format);
+
+       snd_hdac_stream_reset(hdac_stream(stream));
+       err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
+       if (err < 0)
+               return err;
+
+       err = snd_hdac_stream_setup(hdac_stream(stream));
+       if (err < 0)
+               return err;
+
+       hdac_stream(stream)->prepared = 1;
+
+       return 0;
+}
+
+int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       unsigned int format_val;
+       struct hdac_stream *hstream;
+       struct hdac_ext_stream *stream;
+       struct hdac_ext_link *link;
+
+       hstream = snd_hdac_get_stream(bus, params->stream,
+                                       params->link_dma_id + 1);
+       if (!hstream)
+               return -EINVAL;
+
+       stream = stream_to_hdac_ext_stream(hstream);
+       snd_hdac_ext_stream_decouple(ebus, stream, true);
+       format_val = snd_hdac_calc_stream_format(params->s_freq,
+                               params->ch, params->format, 24, 0);
+
+       dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
+               format_val, params->s_freq, params->ch, params->format);
+
+       snd_hdac_ext_link_stream_reset(stream);
+
+       snd_hdac_ext_link_stream_setup(stream, format_val);
+
+       list_for_each_entry(link, &ebus->hlink_list, list) {
+               if (link->index == params->link_index)
+                       snd_hdac_ext_link_set_stream_id(link,
+                                       hstream->stream_tag);
+       }
+
+       stream->link_prepared = 1;
+
+       return 0;
+}
+
 static int skl_pcm_open(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
@@ -188,32 +262,6 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int skl_get_format(struct snd_pcm_substream *substream,
-               struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-       struct skl_dma_params *dma_params;
-       struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
-       int format_val = 0;
-
-       if ((ebus_to_hbus(ebus))->ppcap) {
-               struct snd_pcm_runtime *runtime = substream->runtime;
-
-               format_val = snd_hdac_calc_stream_format(runtime->rate,
-                                               runtime->channels,
-                                               runtime->format,
-                                               32, 0);
-       } else {
-               struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
-               dma_params = snd_soc_dai_get_dma_data(codec_dai, substream);
-               if (dma_params)
-                       format_val = dma_params->format;
-       }
-
-       return format_val;
-}
-
 static int skl_be_prepare(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
@@ -234,37 +282,19 @@ static int skl_be_prepare(struct snd_pcm_substream *substream,
 static int skl_pcm_prepare(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
-       struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
        struct skl *skl = get_skl_ctx(dai->dev);
-       unsigned int format_val;
-       int err;
        struct skl_module_cfg *mconfig;
 
        dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
 
        mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
 
-       format_val = skl_get_format(substream, dai);
-       dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
-                               hdac_stream(stream)->stream_tag, format_val);
-       snd_hdac_stream_reset(hdac_stream(stream));
-
        /* In case of XRUN recovery, reset the FW pipe to clean state */
        if (mconfig && (substream->runtime->status->state ==
                                        SNDRV_PCM_STATE_XRUN))
                skl_reset_pipe(skl->skl_sst, mconfig->pipe);
 
-       err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
-       if (err < 0)
-               return err;
-
-       err = snd_hdac_stream_setup(hdac_stream(stream));
-       if (err < 0)
-               return err;
-
-       hdac_stream(stream)->prepared = 1;
-
-       return err;
+       return 0;
 }
 
 static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -295,6 +325,7 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
        p_params.s_freq = params_rate(params);
        p_params.host_dma_id = dma_id;
        p_params.stream = substream->stream;
+       p_params.format = params_format(params);
 
        m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
        if (m_cfg)
@@ -438,7 +469,6 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_RESUME:
                if (!w->ignore_suspend) {
-                       skl_pcm_prepare(substream, dai);
                        /*
                         * enable DMA Resume enable bit for the stream, set the
                         * dpib & lpib position to resume before starting the
@@ -447,7 +477,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
                        snd_hdac_ext_stream_drsm_enable(ebus, true,
                                                hdac_stream(stream)->index);
                        snd_hdac_ext_stream_set_dpibr(ebus, stream,
-                                                       stream->dpib);
+                                                       stream->lpib);
                        snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
                }
 
@@ -459,7 +489,6 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
                 * pipeline is started but there is a delay in starting the
                 * DMA channel on the host.
                 */
-               snd_hdac_ext_stream_decouple(ebus, stream, true);
                ret = skl_decoupled_trigger(substream, cmd);
                if (ret < 0)
                        return ret;
@@ -506,9 +535,10 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
        struct hdac_ext_stream *link_dev;
        struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-       struct hdac_ext_dma_params *dma_params;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct skl_pipe_params p_params = {0};
+       struct hdac_ext_link *link;
+       int stream_tag;
 
        link_dev = snd_hdac_ext_stream_assign(ebus, substream,
                                        HDAC_EXT_STREAM_TYPE_LINK);
@@ -517,16 +547,22 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
 
        snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
 
+       link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
+       if (!link)
+               return -EINVAL;
+
+       stream_tag = hdac_stream(link_dev)->stream_tag;
+
        /* set the stream tag in the codec dai dma params  */
-       dma_params = snd_soc_dai_get_dma_data(codec_dai, substream);
-       if (dma_params)
-               dma_params->stream_tag =  hdac_stream(link_dev)->stream_tag;
+       snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
 
        p_params.s_fmt = snd_pcm_format_width(params_format(params));
        p_params.ch = params_channels(params);
        p_params.s_freq = params_rate(params);
        p_params.stream = substream->stream;
-       p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
+       p_params.link_dma_id = stream_tag - 1;
+       p_params.link_index = link->index;
+       p_params.format = params_format(params);
 
        return skl_tplg_be_update_params(dai, &p_params);
 }
@@ -534,41 +570,15 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
 static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
-       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-       struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
-       struct hdac_ext_stream *link_dev =
-                       snd_soc_dai_get_dma_data(dai, substream);
-       unsigned int format_val = 0;
-       struct skl_dma_params *dma_params;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct hdac_ext_link *link;
        struct skl *skl = get_skl_ctx(dai->dev);
        struct skl_module_cfg *mconfig = NULL;
 
-       dma_params  = (struct skl_dma_params *)
-                       snd_soc_dai_get_dma_data(codec_dai, substream);
-       if (dma_params)
-               format_val = dma_params->format;
-       dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d codec_dai_name=%s\n",
-                       hdac_stream(link_dev)->stream_tag, format_val, codec_dai->name);
-
-       link = snd_hdac_ext_bus_get_link(ebus, rtd->codec->component.name);
-       if (!link)
-               return -EINVAL;
-
-       snd_hdac_ext_link_stream_reset(link_dev);
-
        /* In case of XRUN recovery, reset the FW pipe to clean state */
        mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
-       if (mconfig && (substream->runtime->status->state ==
-                                       SNDRV_PCM_STATE_XRUN))
+       if (mconfig && !mconfig->pipe->passthru &&
+               (substream->runtime->status->state == SNDRV_PCM_STATE_XRUN))
                skl_reset_pipe(skl->skl_sst, mconfig->pipe);
 
-       snd_hdac_ext_link_stream_setup(link_dev, format_val);
-
-       snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
-       link_dev->link_prepared = 1;
-
        return 0;
 }
 
@@ -583,10 +593,8 @@ static int skl_link_pcm_trigger(struct snd_pcm_substream *substream,
        dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_RESUME:
-               skl_link_pcm_prepare(substream, dai);
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               snd_hdac_ext_stream_decouple(ebus, stream, true);
                snd_hdac_ext_link_stream_start(link_dev);
                break;
 
index 7c272ba0f4b517da8faeb590f5265ed6ec602dfd..849410d0823e96ec4cd658263cb0aaeb254a5397 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/interrupt.h>
 #include <sound/memalloc.h>
 #include "skl-sst-cldma.h"
-#include "skl-tplg-interface.h"
 #include "skl-topology.h"
 
 struct sst_dsp;
@@ -145,7 +144,7 @@ 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);
+               struct skl_lib_info *linfo, int count);
        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);
@@ -236,5 +235,4 @@ int skl_get_pvt_instance_id_map(struct skl_sst *ctx,
 void skl_freeup_uuid_list(struct skl_sst *ctx);
 
 int skl_dsp_strip_extended_manifest(struct firmware *fw);
-
 #endif /*__SKL_SST_DSP_H__*/
index cc40341233fa63c952534a3c9287b378b5f88d99..9660ace379ababf63d9ab29ab5941b7e3000c03d 100644 (file)
@@ -97,8 +97,9 @@ struct skl_sst {
        /* multi-core */
        struct skl_dsp_cores cores;
 
-       /* tplg manifest */
-       struct skl_dfw_manifest manifest;
+       /* library info */
+       struct skl_lib_info  lib_info[SKL_MAX_LIB];
+       int lib_count;
 
        /* Callback to update D0i3C register */
        void (*update_d0i3c)(struct device *dev, bool enable);
index bd313c907b20df470a8c7727f8169c036fae25b9..ed58b5b3555a869ff91772689b761369873d6c89 100644 (file)
@@ -330,6 +330,31 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
                        multiplier;
 }
 
+static u8 skl_tplg_be_dev_type(int dev_type)
+{
+       int ret;
+
+       switch (dev_type) {
+       case SKL_DEVICE_BT:
+               ret = NHLT_DEVICE_BT;
+               break;
+
+       case SKL_DEVICE_DMIC:
+               ret = NHLT_DEVICE_DMIC;
+               break;
+
+       case SKL_DEVICE_I2S:
+               ret = NHLT_DEVICE_I2S;
+               break;
+
+       default:
+               ret = NHLT_DEVICE_INVALID;
+               break;
+       }
+
+       return ret;
+}
+
 static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
                                                struct skl_sst *ctx)
 {
@@ -338,6 +363,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
        u32 ch, s_freq, s_fmt;
        struct nhlt_specific_cfg *cfg;
        struct skl *skl = get_skl_ctx(ctx->dev);
+       u8 dev_type = skl_tplg_be_dev_type(m_cfg->dev_type);
 
        /* check if we already have blob */
        if (m_cfg->formats_config.caps_size > 0)
@@ -374,7 +400,7 @@ static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
 
        /* update the blob based on virtual bus_id and default params */
        cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type,
-                                       s_fmt, ch, s_freq, dir);
+                                       s_fmt, ch, s_freq, dir, dev_type);
        if (cfg) {
                m_cfg->formats_config.caps_size = cfg->size;
                m_cfg->formats_config.caps = (u32 *) &cfg->caps;
@@ -496,6 +522,20 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
        return 0;
 }
 
+static int skl_tplg_module_prepare(struct skl_sst *ctx, struct skl_pipe *pipe,
+               struct snd_soc_dapm_widget *w, struct skl_module_cfg *mcfg)
+{
+       switch (mcfg->dev_type) {
+       case SKL_DEVICE_HDAHOST:
+               return skl_pcm_host_dma_prepare(ctx->dev, pipe->p_params);
+
+       case SKL_DEVICE_HDALINK:
+               return skl_pcm_link_dma_prepare(ctx->dev, pipe->p_params);
+       }
+
+       return 0;
+}
+
 /*
  * Inside a pipe instance, we can have various modules. These modules need
  * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
@@ -535,6 +575,11 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
                        mconfig->m_state = SKL_MODULE_LOADED;
                }
 
+               /* prepare the DMA if the module is gateway cpr */
+               ret = skl_tplg_module_prepare(ctx, pipe, w, mconfig);
+               if (ret < 0)
+                       return ret;
+
                /* update blob if blob is null for be with default value */
                skl_tplg_update_be_blob(w, ctx);
 
@@ -974,7 +1019,6 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
        struct skl_module_cfg *src_module = NULL, *dst_module;
        struct skl_sst *ctx = skl->skl_sst;
        struct skl_pipe *s_pipe = mconfig->pipe;
-       int ret = 0;
 
        if (s_pipe->state == SKL_PIPE_INVALID)
                return -EINVAL;
@@ -996,7 +1040,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
                src_module = dst_module;
        }
 
-       ret = skl_delete_pipe(ctx, mconfig->pipe);
+       skl_delete_pipe(ctx, mconfig->pipe);
 
        return skl_tplg_unload_pipe_modules(ctx, s_pipe);
 }
@@ -1207,6 +1251,7 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
                switch (mcfg->dev_type) {
                case SKL_DEVICE_HDALINK:
                        pipe->p_params->link_dma_id = params->link_dma_id;
+                       pipe->p_params->link_index = params->link_index;
                        break;
 
                case SKL_DEVICE_HDAHOST:
@@ -1220,6 +1265,7 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
                pipe->p_params->ch = params->ch;
                pipe->p_params->s_freq = params->s_freq;
                pipe->p_params->stream = params->stream;
+               pipe->p_params->format = params->format;
 
        } else {
                memcpy(pipe->p_params, params, sizeof(*params));
@@ -1428,6 +1474,7 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
        struct nhlt_specific_cfg *cfg;
        struct skl *skl = get_skl_ctx(dai->dev);
        int link_type = skl_tplg_be_link_type(mconfig->dev_type);
+       u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type);
 
        skl_tplg_fill_dma_id(mconfig, params);
 
@@ -1437,7 +1484,8 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
        /* update the blob based on virtual bus_id*/
        cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
                                        params->s_fmt, params->ch,
-                                       params->s_freq, params->stream);
+                                       params->s_freq, params->stream,
+                                       dev_type);
        if (cfg) {
                mconfig->formats_config.caps_size = cfg->size;
                mconfig->formats_config.caps = (u32 *) &cfg->caps;
@@ -2280,20 +2328,21 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
 
 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)
+               struct skl *skl)
 {
        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) {
+               if (ref_count > skl->skl_sst->lib_count - 1) {
                        ref_count = 0;
                        return -EINVAL;
                }
 
-               strncpy(minfo->lib[ref_count].name, str_elem->string,
-                               ARRAY_SIZE(minfo->lib[ref_count].name));
+               strncpy(skl->skl_sst->lib_info[ref_count].name,
+                       str_elem->string,
+                       ARRAY_SIZE(skl->skl_sst->lib_info[ref_count].name));
                ref_count++;
                tkn_count++;
                break;
@@ -2308,14 +2357,14 @@ static int skl_tplg_fill_str_mfest_tkn(struct device *dev,
 
 static int skl_tplg_get_str_tkn(struct device *dev,
                struct snd_soc_tplg_vendor_array *array,
-               struct skl_dfw_manifest *minfo)
+               struct skl *skl)
 {
        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);
+               ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, skl);
                str_elem++;
 
                if (ret < 0)
@@ -2329,13 +2378,13 @@ static int skl_tplg_get_str_tkn(struct device *dev,
 
 static int skl_tplg_get_int_tkn(struct device *dev,
                struct snd_soc_tplg_vendor_value_elem *tkn_elem,
-               struct skl_dfw_manifest *minfo)
+               struct skl *skl)
 {
        int tkn_count = 0;
 
        switch (tkn_elem->token) {
        case SKL_TKN_U32_LIB_COUNT:
-               minfo->lib_count = tkn_elem->value;
+               skl->skl_sst->lib_count = tkn_elem->value;
                tkn_count++;
                break;
 
@@ -2352,7 +2401,7 @@ static int skl_tplg_get_int_tkn(struct device *dev,
  * type.
  */
 static int skl_tplg_get_manifest_tkn(struct device *dev,
-               char *pvt_data, struct skl_dfw_manifest *minfo,
+               char *pvt_data, struct skl *skl,
                int block_size)
 {
        int tkn_count = 0, ret;
@@ -2368,7 +2417,7 @@ static int skl_tplg_get_manifest_tkn(struct device *dev,
                off += array->size;
                switch (array->type) {
                case SND_SOC_TPLG_TUPLE_TYPE_STRING:
-                       ret = skl_tplg_get_str_tkn(dev, array, minfo);
+                       ret = skl_tplg_get_str_tkn(dev, array, skl);
 
                        if (ret < 0)
                                return ret;
@@ -2390,7 +2439,7 @@ static int skl_tplg_get_manifest_tkn(struct device *dev,
 
                while (tkn_count <= array->num_elems - 1) {
                        ret = skl_tplg_get_int_tkn(dev,
-                                       tkn_elem, minfo);
+                                       tkn_elem, skl);
                        if (ret < 0)
                                return ret;
 
@@ -2411,7 +2460,7 @@ static int skl_tplg_get_manifest_tkn(struct device *dev,
  * 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 device *dev, struct skl *skl)
 {
        struct snd_soc_tplg_vendor_array *array;
        int num_blocks, block_size = 0, block_type, off = 0;
@@ -2454,7 +2503,7 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest,
                data = (manifest->priv.data + off);
 
                if (block_type == SKL_TYPE_TUPLE) {
-                       ret = skl_tplg_get_manifest_tkn(dev, data, minfo,
+                       ret = skl_tplg_get_manifest_tkn(dev, data, skl,
                                        block_size);
 
                        if (ret < 0)
@@ -2472,27 +2521,23 @@ static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest,
 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);
+       skl_tplg_get_manifest_data(manifest, bus->dev, skl);
 
-       if (minfo->lib_count > HDA_MAX_LIB) {
+       if (skl->skl_sst->lib_count > SKL_MAX_LIB) {
                dev_err(bus->dev, "Exceeding max Library count. Got:%d\n",
-                                       minfo->lib_count);
-               ret = -EINVAL;
+                                       skl->skl_sst->lib_count);
+               return  -EINVAL;
        }
 
-       return ret;
+       return 0;
 }
 
 static struct snd_soc_tplg_ops skl_tplg_ops  = {
index 08d39280b07bcd09bd3a62c7de4f99ba7290add3..fefab0e99a3bea56b1afc9c0135cb67ca91a83eb 100644 (file)
@@ -254,6 +254,8 @@ struct skl_pipe_params {
        u32 s_freq;
        u32 s_fmt;
        u8 linktype;
+       snd_pcm_format_t format;
+       int link_index;
        int stream;
 };
 
@@ -332,6 +334,19 @@ struct skl_pipeline {
        struct list_head node;
 };
 
+#define SKL_LIB_NAME_LENGTH 128
+#define SKL_MAX_LIB 16
+
+struct skl_lib_info {
+       char name[SKL_LIB_NAME_LENGTH];
+       const struct firmware *fw;
+};
+
+struct skl_manifest {
+       u32 lib_count;
+       struct skl_lib_info lib[SKL_MAX_LIB];
+};
+
 static inline struct skl *get_skl_ctx(struct device *dev)
 {
        struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
@@ -383,4 +398,8 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
 struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai,
                                                                int stream);
 enum skl_bitdepth skl_get_bit_depth(int params);
+int skl_pcm_host_dma_prepare(struct device *dev,
+                       struct skl_pipe_params *params);
+int skl_pcm_link_dma_prepare(struct device *dev,
+                       struct skl_pipe_params *params);
 #endif
index 2f6281e056d6a660fab453c47ee31a5167e74cbb..7a2febf99019be78f3f9c47c9c69edebafbefc20 100644 (file)
@@ -157,18 +157,6 @@ struct skl_dfw_algo_data {
        char params[0];
 } __packed;
 
-#define LIB_NAME_LENGTH        128
-#define HDA_MAX_LIB    16
-
-struct lib_info {
-       char name[LIB_NAME_LENGTH];
-} __packed;
-
-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
index da5db509827429d060cd429ea0c93b9f984fa6e4..0c57d4eaae3ae3bf488860f6133acdf9e7e30271 100644 (file)
@@ -732,6 +732,10 @@ static int skl_probe(struct pci_dev *pci,
                goto out_display_power_off;
        }
 
+       err = skl_nhlt_create_sysfs(skl);
+       if (err < 0)
+               goto out_nhlt_free;
+
        skl_nhlt_update_topology_bin(skl);
 
        pci_set_drvdata(skl->pci, ebus);
@@ -852,6 +856,7 @@ static void skl_remove(struct pci_dev *pci)
        skl_free_dsp(skl);
        skl_machine_device_unregister(skl);
        skl_dmic_device_unregister(skl);
+       skl_nhlt_remove_sysfs(skl);
        skl_nhlt_free(skl->nhlt);
        skl_free(ebus);
        dev_set_drvdata(&pci->dev, NULL);
@@ -878,6 +883,10 @@ static struct sst_acpi_mach sst_kbl_devdata[] = {
        {}
 };
 
+static struct sst_acpi_mach sst_glk_devdata[] = {
+       { "INT343A", "glk_alc298s_i2s", "intel/dsp_fw_glk.bin", NULL, NULL, NULL },
+};
+
 /* PCI IDs */
 static const struct pci_device_id skl_ids[] = {
        /* Sunrise Point-LP */
@@ -889,6 +898,9 @@ static const struct pci_device_id skl_ids[] = {
        /* KBL */
        { PCI_DEVICE(0x8086, 0x9D71),
                .driver_data = (unsigned long)&sst_kbl_devdata},
+       /* GLK */
+       { PCI_DEVICE(0x8086, 0x3198),
+               .driver_data = (unsigned long)&sst_glk_devdata},
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, skl_ids);
index 4986e3929dd3a77f4ed637a6fec130f1b5efb555..bbef77d2b917a9e080e0cd67b99fbaa796b79812 100644 (file)
@@ -118,7 +118,8 @@ int skl_platform_register(struct device *dev);
 struct nhlt_acpi_table *skl_nhlt_init(struct device *dev);
 void skl_nhlt_free(struct nhlt_acpi_table *addr);
 struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
-                       u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
+                                       u8 link_type, u8 s_fmt, u8 no_ch,
+                                       u32 s_rate, u8 dirn, u8 dev_type);
 
 int skl_get_dmic_geo(struct skl *skl);
 int skl_nhlt_update_topology_bin(struct skl *skl);
@@ -130,5 +131,7 @@ 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);
 void skl_update_d0i3c(struct device *dev, bool enable);
+int skl_nhlt_create_sysfs(struct skl *skl);
+void skl_nhlt_remove_sysfs(struct skl *skl);
 
 #endif /* __SOUND_SOC_SKL_H */
index 34a6123480d3e708e7b934c967ea7b5f85131336..c7fa3e6634639f8498ced1f873e984b252a415e1 100644 (file)
@@ -1578,6 +1578,7 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        if (!pm_runtime_enabled(&pdev->dev))
                goto err_pm_disable;
+       pm_runtime_get_sync(&pdev->dev);
 
        ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
        if (ret) {
@@ -1617,6 +1618,7 @@ static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
                mt2701_afe_runtime_suspend(&pdev->dev);
+       pm_runtime_put_sync(&pdev->dev);
 
        snd_soc_unregister_component(&pdev->dev);
        snd_soc_unregister_platform(&pdev->dev);
index 5524a2c727ec76ad25af3a8386eda5f09678369e..46c8e6ae00b4046f189e2b2569d9344a013fe61f 100644 (file)
@@ -79,17 +79,11 @@ static int mt8173_max98090_init(struct snd_soc_pcm_runtime *runtime)
 
        /* enable jack detection */
        ret = snd_soc_card_jack_new(card, "Headphone", SND_JACK_HEADPHONE,
-                                   &mt8173_max98090_jack, NULL, 0);
+                                   &mt8173_max98090_jack,
+                                   mt8173_max98090_jack_pins,
+                                   ARRAY_SIZE(mt8173_max98090_jack_pins));
        if (ret) {
-               dev_err(card->dev, "Can't snd_soc_jack_new %d\n", ret);
-               return ret;
-       }
-
-       ret = snd_soc_jack_add_pins(&mt8173_max98090_jack,
-                                   ARRAY_SIZE(mt8173_max98090_jack_pins),
-                                   mt8173_max98090_jack_pins);
-       if (ret) {
-               dev_err(card->dev, "Can't snd_soc_jack_add_pins %d\n", ret);
+               dev_err(card->dev, "Can't create a new Jack %d\n", ret);
                return ret;
        }
 
index a002ab892772106bae79532d4836814411eaef2f..b42f301c6b960490d9392902e16e1dbbf78045a7 100644 (file)
@@ -119,23 +119,33 @@ static int mxs_saif_set_clk(struct mxs_saif *saif,
         * Set SAIF clock
         *
         * The SAIF clock should be either 384*fs or 512*fs.
-        * If MCLK is used, the SAIF clk ratio need to match mclk ratio.
-        *  For 32x mclk, set saif clk as 512*fs.
-        *  For 48x mclk, set saif clk as 384*fs.
+        * If MCLK is used, the SAIF clk ratio needs to match mclk ratio.
+        *  For 256x, 128x, 64x, and 32x sub-rates, set saif clk as 512*fs.
+        *  For 192x, 96x, and 48x sub-rates, set saif clk as 384*fs.
         *
         * If MCLK is not used, we just set saif clk to 512*fs.
         */
        clk_prepare_enable(master_saif->clk);
 
        if (master_saif->mclk_in_use) {
-               if (mclk % 32 == 0) {
+               switch (mclk / rate) {
+               case 32:
+               case 64:
+               case 128:
+               case 256:
+               case 512:
                        scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
                        ret = clk_set_rate(master_saif->clk, 512 * rate);
-               } else if (mclk % 48 == 0) {
+                       break;
+               case 48:
+               case 96:
+               case 192:
+               case 384:
                        scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
                        ret = clk_set_rate(master_saif->clk, 384 * rate);
-               } else {
-                       /* SAIF MCLK should be either 32x or 48x */
+                       break;
+               default:
+                       /* SAIF MCLK should be a sub-rate of 512x or 384x */
                        clk_disable_unprepare(master_saif->clk);
                        return -EINVAL;
                }
@@ -299,6 +309,16 @@ static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
                return -EBUSY;
        }
 
+       /* If SAIF1 is configured as slave, the clk gate needs to be cleared
+        * before the register can be written.
+        */
+       if (saif->id != saif->master_id) {
+               __raw_writel(BM_SAIF_CTRL_SFTRST,
+                       saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+               __raw_writel(BM_SAIF_CTRL_CLKGATE,
+                       saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+       }
+
        scr0 = __raw_readl(saif->base + SAIF_CTRL);
        scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \
                & ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY;
index 61e93b1c185db761e75c3ace89d16c09a1baa6f9..46ae1269a698cb3194204d07ec9b3bf0ebc179fc 100644 (file)
@@ -323,8 +323,11 @@ struct omap_mcbsp {
 
        unsigned int fmt;
        unsigned int in_freq;
+       unsigned int latency[2];
        int clk_div;
        int wlen;
+
+       struct pm_qos_request pm_qos_req;
 };
 
 void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
index d018e966e53345ef138e72b0a359830922d5bff7..6b40bdbef336952084c1d88fbe64d767121f37a8 100644 (file)
@@ -157,6 +157,17 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
                                    struct snd_soc_dai *cpu_dai)
 {
        struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+       int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+       int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+
+       if (mcbsp->latency[stream2])
+               pm_qos_update_request(&mcbsp->pm_qos_req,
+                                     mcbsp->latency[stream2]);
+       else if (mcbsp->latency[stream1])
+               pm_qos_remove_request(&mcbsp->pm_qos_req);
+
+       mcbsp->latency[stream1] = 0;
 
        if (!cpu_dai->active) {
                omap_mcbsp_free(mcbsp);
@@ -164,6 +175,28 @@ static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
        }
 }
 
+static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *cpu_dai)
+{
+       struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+       struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req;
+       int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+       int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       int latency = mcbsp->latency[stream2];
+
+       /* Prevent omap hardware from hitting off between FIFO fills */
+       if (!latency || mcbsp->latency[stream1] < latency)
+               latency = mcbsp->latency[stream1];
+
+       if (pm_qos_request_active(pm_qos_req))
+               pm_qos_update_request(pm_qos_req, latency);
+       else if (latency)
+               pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency);
+
+       return 0;
+}
+
 static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
                                  struct snd_soc_dai *cpu_dai)
 {
@@ -226,6 +259,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
        int wlen, channels, wpf;
        int pkt_size = 0;
        unsigned int format, div, framesize, master;
+       unsigned int buffer_size = mcbsp->pdata->buffer_size;
 
        dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
        channels = params_channels(params);
@@ -240,7 +274,9 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
        default:
                return -EINVAL;
        }
-       if (mcbsp->pdata->buffer_size) {
+       if (buffer_size) {
+               int latency;
+
                if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
                        int period_words, max_thrsh;
                        int divider = 0;
@@ -271,6 +307,12 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
                        /* Use packet mode for non mono streams */
                        pkt_size = channels;
                }
+
+               latency = ((((buffer_size - pkt_size) / channels) * 1000)
+                                / (params->rate_num / params->rate_den));
+
+               mcbsp->latency[substream->stream] = latency;
+
                omap_mcbsp_set_threshold(substream, pkt_size);
        }
 
@@ -554,6 +596,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
 static const struct snd_soc_dai_ops mcbsp_dai_ops = {
        .startup        = omap_mcbsp_dai_startup,
        .shutdown       = omap_mcbsp_dai_shutdown,
+       .prepare        = omap_mcbsp_dai_prepare,
        .trigger        = omap_mcbsp_dai_trigger,
        .delay          = omap_mcbsp_dai_delay,
        .hw_params      = omap_mcbsp_dai_hw_params,
@@ -835,6 +878,9 @@ static int asoc_mcbsp_remove(struct platform_device *pdev)
        if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)
                mcbsp->pdata->ops->free(mcbsp->id);
 
+       if (pm_qos_request_active(&mcbsp->pm_qos_req))
+               pm_qos_remove_request(&mcbsp->pm_qos_req);
+
        omap_mcbsp_cleanup(mcbsp);
 
        clk_put(mcbsp->fclk);
index cda656e4afc6985e0fe6ffb78f3abe486d7374e2..9104c98deeb75ab8ac3113e5be85ff4958fa2bdf 100644 (file)
@@ -37,8 +37,12 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
        pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
        pcm_conf->compat_filter_fn = filter;
 
-       pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
-       pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
+       if (dev->of_node) {
+               pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
+               pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
+       } else {
+               flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME;
+       }
 
        return devm_snd_dmaengine_pcm_register(dev, pcm_conf, flags);
 }
index e00974bc561670ff30413a85e5fe2644148617e3..85324e61cbd56da5c07a55ae02151516ce4a8f00 100644 (file)
@@ -1305,6 +1305,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        }
        pri_dai->dma_playback.addr = regs_base + I2STXD;
        pri_dai->dma_capture.addr = regs_base + I2SRXD;
+       pri_dai->dma_playback.chan_name = "tx";
+       pri_dai->dma_capture.chan_name = "rx";
        pri_dai->dma_playback.addr_width = 4;
        pri_dai->dma_capture.addr_width = 4;
        pri_dai->quirks = quirks;
@@ -1329,6 +1331,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                sec_dai->lock = &pri_dai->spinlock;
                sec_dai->variant_regs = pri_dai->variant_regs;
                sec_dai->dma_playback.addr = regs_base + I2STXDS;
+               sec_dai->dma_playback.chan_name = "tx-sec";
 
                if (!np) {
                        sec_dai->dma_playback.filter_data = i2s_pdata->dma_play_sec;
index 6d0b8897fa6cdf64e77aec7c951234a2e285d89d..0a4718207e6ec41ae9f1535c5757066c73710f6a 100644 (file)
 #include <linux/platform_data/asoc-s3c.h>
 
 static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_out = {
+       .chan_name      = "tx",
        .addr_width     = 4,
 };
 
 static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_in = {
+       .chan_name      = "rx",
        .addr_width     = 4,
 };
 
index 07f5091b33e898bf7e6af19b02802db9645f2390..91e6871e5413a64898e33339d41903d15cba7f41 100644 (file)
 #include "s3c24xx-i2s.h"
 
 static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
+       .chan_name      = "tx",
        .addr_width     = 2,
 };
 
 static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
+       .chan_name      = "rx",
        .addr_width     = 2,
 };
 
index 99b5b0835c1e840c66e45676ff81cd57a4c1875c..47b370cb2d3b3aeb53f59f6198793f891890c0f8 100644 (file)
@@ -363,8 +363,6 @@ struct rsnd_mod *rsnd_mod_next(int *iterator,
                if (!mod)
                        continue;
 
-               (*iterator)++;
-
                return mod;
        }
 
index b90df77662dfa337e5ff50bb44ef59efa1eef6e6..7410ec0174db7db8c172c60c62f20f91c247df8d 100644 (file)
@@ -374,10 +374,10 @@ struct rsnd_mod *rsnd_mod_next(int *iterator,
                               int array_size);
 #define for_each_rsnd_mod(iterator, pos, io)                           \
        for (iterator = 0;                                              \
-            (pos = rsnd_mod_next(&iterator, io, NULL, 0));)
+            (pos = rsnd_mod_next(&iterator, io, NULL, 0)); iterator++)
 #define for_each_rsnd_mod_arrays(iterator, pos, io, array, size)       \
        for (iterator = 0;                                              \
-            (pos = rsnd_mod_next(&iterator, io, array, size));)
+            (pos = rsnd_mod_next(&iterator, io, array, size)); iterator++)
 #define for_each_rsnd_mod_array(iterator, pos, io, array)              \
        for_each_rsnd_mod_arrays(iterator, pos, io, array, ARRAY_SIZE(array))
 
index 3a8f65bd1bf95c17afbe3a0f43b7ad1b94409792..42db48db09ba9a36abfbc807469e038ad86d3eaf 100644 (file)
@@ -390,6 +390,9 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
+       /* reset sync convert_rate */
+       src->sync.val = 0;
+
        rsnd_mod_power_on(mod);
 
        rsnd_src_activation(mod);
@@ -398,9 +401,6 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 
        rsnd_src_status_clear(mod);
 
-       /* reset sync convert_rate */
-       src->sync.val = 0;
-
        return 0;
 }
 
index 6c8b0b0c56ece5e551b3d722b853c48ff22899b7..36dae41f65fc7669a4de5b5dcb332abfddb285fd 100644 (file)
@@ -251,7 +251,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
 
 /**
  * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
+ * @ac97: snd_ac97 device to be freed
  *
  * Frees AC97 codec device resources.
  */
index baa1afa41e3dd57fdc36655b7d3bbd147ade820f..a110d3987d4ae912b46c57f2f36f258b985b56e5 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/dmi.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -979,7 +980,7 @@ EXPORT_SYMBOL_GPL(snd_soc_find_dai);
  * @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
+ * @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
@@ -1593,6 +1594,27 @@ static int soc_probe_dai(struct snd_soc_dai *dai, int order)
        return 0;
 }
 
+static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais,
+                               struct snd_soc_pcm_runtime *rtd)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < num_dais; ++i) {
+               struct snd_soc_dai_driver *drv = dais[i]->driver;
+
+               if (!rtd->dai_link->no_pcm && drv->pcm_new)
+                       ret = drv->pcm_new(rtd, dais[i]);
+               if (ret < 0) {
+                       dev_err(dais[i]->dev,
+                               "ASoC: Failed to bind %s with pcm device\n",
+                               dais[i]->name);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int soc_link_dai_widgets(struct snd_soc_card *card,
                                struct snd_soc_dai_link *dai_link,
                                struct snd_soc_pcm_runtime *rtd)
@@ -1704,6 +1726,13 @@ static int soc_probe_link_dais(struct snd_soc_card *card,
                                       dai_link->stream_name, ret);
                                return ret;
                        }
+                       ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd);
+                       if (ret < 0)
+                               return ret;
+                       ret = soc_link_dai_pcm_new(rtd->codec_dais,
+                                                  rtd->num_codecs, rtd);
+                       if (ret < 0)
+                               return ret;
                } else {
                        INIT_DELAYED_WORK(&rtd->delayed_work,
                                                codec2codec_close_delayed_work);
@@ -1888,6 +1917,139 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
 }
 EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
 
+
+/* Trim special characters, and replace '-' with '_' since '-' is used to
+ * separate different DMI fields in the card long name. Only number and
+ * alphabet characters and a few separator characters are kept.
+ */
+static void cleanup_dmi_name(char *name)
+{
+       int i, j = 0;
+
+       for (i = 0; name[i]; i++) {
+               if (isalnum(name[i]) || (name[i] == '.')
+                   || (name[i] == '_'))
+                       name[j++] = name[i];
+               else if (name[i] == '-')
+                       name[j++] = '_';
+       }
+
+       name[j] = '\0';
+}
+
+/**
+ * snd_soc_set_dmi_name() - Register DMI names to card
+ * @card: The card to register DMI names
+ * @flavour: The flavour "differentiator" for the card amongst its peers.
+ *
+ * An Intel machine driver may be used by many different devices but are
+ * difficult for userspace to differentiate, since machine drivers ususally
+ * use their own name as the card short name and leave the card long name
+ * blank. To differentiate such devices and fix bugs due to lack of
+ * device-specific configurations, this function allows DMI info to be used
+ * as the sound card long name, in the format of
+ * "vendor-product-version-board"
+ * (Character '-' is used to separate different DMI fields here).
+ * This will help the user space to load the device-specific Use Case Manager
+ * (UCM) configurations for the card.
+ *
+ * Possible card long names may be:
+ * DellInc.-XPS139343-01-0310JH
+ * ASUSTeKCOMPUTERINC.-T100TA-1.0-T100TA
+ * Circuitco-MinnowboardMaxD0PLATFORM-D0-MinnowBoardMAX
+ *
+ * This function also supports flavoring the card longname to provide
+ * the extra differentiation, like "vendor-product-version-board-flavor".
+ *
+ * We only keep number and alphabet characters and a few separator characters
+ * in the card long name since UCM in the user space uses the card long names
+ * as card configuration directory names and AudoConf cannot support special
+ * charactors like SPACE.
+ *
+ * Returns 0 on success, otherwise a negative error code.
+ */
+int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
+{
+       const char *vendor, *product, *product_version, *board;
+       size_t longname_buf_size = sizeof(card->snd_card->longname);
+       size_t len;
+
+       if (card->long_name)
+               return 0; /* long name already set by driver or from DMI */
+
+       /* make up dmi long name as: vendor.product.version.board */
+       vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+       if (!vendor) {
+               dev_warn(card->dev, "ASoC: no DMI vendor name!\n");
+               return 0;
+       }
+
+       snprintf(card->dmi_longname, sizeof(card->snd_card->longname),
+                        "%s", vendor);
+       cleanup_dmi_name(card->dmi_longname);
+
+       product = dmi_get_system_info(DMI_PRODUCT_NAME);
+       if (product) {
+               len = strlen(card->dmi_longname);
+               snprintf(card->dmi_longname + len,
+                        longname_buf_size - len,
+                        "-%s", product);
+
+               len++;  /* skip the separator "-" */
+               if (len < longname_buf_size)
+                       cleanup_dmi_name(card->dmi_longname + len);
+
+               /* some vendors like Lenovo may only put a self-explanatory
+                * name in the product version field
+                */
+               product_version = dmi_get_system_info(DMI_PRODUCT_VERSION);
+               if (product_version) {
+                       len = strlen(card->dmi_longname);
+                       snprintf(card->dmi_longname + len,
+                                longname_buf_size - len,
+                                "-%s", product_version);
+
+                       len++;
+                       if (len < longname_buf_size)
+                               cleanup_dmi_name(card->dmi_longname + len);
+               }
+       }
+
+       board = dmi_get_system_info(DMI_BOARD_NAME);
+       if (board) {
+               len = strlen(card->dmi_longname);
+               snprintf(card->dmi_longname + len,
+                        longname_buf_size - len,
+                        "-%s", board);
+
+               len++;
+               if (len < longname_buf_size)
+                       cleanup_dmi_name(card->dmi_longname + len);
+       } else if (!product) {
+               /* fall back to using legacy name */
+               dev_warn(card->dev, "ASoC: no DMI board/product name!\n");
+               return 0;
+       }
+
+       /* Add flavour to dmi long name */
+       if (flavour) {
+               len = strlen(card->dmi_longname);
+               snprintf(card->dmi_longname + len,
+                        longname_buf_size - len,
+                        "-%s", flavour);
+
+               len++;
+               if (len < longname_buf_size)
+                       cleanup_dmi_name(card->dmi_longname + len);
+       }
+
+       /* set the card long name */
+       card->long_name = card->dmi_longname;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name);
+
 static int snd_soc_instantiate_card(struct snd_soc_card *card)
 {
        struct snd_soc_codec *codec;
@@ -2976,6 +3138,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
        component->remove = component->driver->remove;
        component->suspend = component->driver->suspend;
        component->resume = component->driver->resume;
+       component->pcm_new = component->driver->pcm_new;
+       component->pcm_free= component->driver->pcm_free;
 
        dapm = &component->dapm;
        dapm->dev = dev;
@@ -3158,6 +3322,21 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component)
        platform->driver->remove(platform);
 }
 
+static int snd_soc_platform_drv_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_platform *platform = rtd->platform;
+
+       return platform->driver->pcm_new(rtd);
+}
+
+static void snd_soc_platform_drv_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+
+       platform->driver->pcm_free(pcm);
+}
+
 /**
  * snd_soc_add_platform - Add a platform to the ASoC core
  * @dev: The parent device for the platform
@@ -3181,6 +3360,10 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
                platform->component.probe = snd_soc_platform_drv_probe;
        if (platform_drv->remove)
                platform->component.remove = snd_soc_platform_drv_remove;
+       if (platform_drv->pcm_new)
+               platform->component.pcm_new = snd_soc_platform_drv_pcm_new;
+       if (platform_drv->pcm_free)
+               platform->component.pcm_free = snd_soc_platform_drv_pcm_free;
 
 #ifdef CONFIG_DEBUG_FS
        platform->component.debugfs_prefix = "platform";
@@ -3492,10 +3675,10 @@ found:
 EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
 
 /* Retrieve a card's name from device tree */
-int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card,
-                                        struct device_node *np,
-                                        const char *propname)
+int snd_soc_of_parse_card_name(struct snd_soc_card *card,
+                              const char *propname)
 {
+       struct device_node *np;
        int ret;
 
        if (!card->dev) {
@@ -3503,8 +3686,7 @@ int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card,
                return -EINVAL;
        }
 
-       if (!np)
-               np = card->dev->of_node;
+       np = card->dev->of_node;
 
        ret = of_property_read_string_index(np, propname, 0, &card->name);
        /*
@@ -3521,7 +3703,7 @@ int snd_soc_of_parse_card_name_from_node(struct snd_soc_card *card,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name_from_node);
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);
 
 static const struct snd_soc_dapm_widget simple_widgets[] = {
        SND_SOC_DAPM_MIC("Microphone", NULL),
@@ -3530,17 +3712,14 @@ static const struct snd_soc_dapm_widget simple_widgets[] = {
        SND_SOC_DAPM_SPK("Speaker", NULL),
 };
 
-int snd_soc_of_parse_audio_simple_widgets_from_node(struct snd_soc_card *card,
-                                         struct device_node *np,
+int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
                                          const char *propname)
 {
+       struct device_node *np = card->dev->of_node;
        struct snd_soc_dapm_widget *widgets;
        const char *template, *wname;
        int i, j, num_widgets, ret;
 
-       if (!np)
-               np = card->dev->of_node;
-
        num_widgets = of_property_count_strings(np, propname);
        if (num_widgets < 0) {
                dev_err(card->dev,
@@ -3611,7 +3790,7 @@ int snd_soc_of_parse_audio_simple_widgets_from_node(struct snd_soc_card *card,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets_from_node);
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
 
 static int snd_soc_of_get_slot_mask(struct device_node *np,
                                    const char *prop_name,
@@ -3667,18 +3846,15 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot);
 
-void snd_soc_of_parse_audio_prefix_from_node(struct snd_soc_card *card,
-                                  struct device_node *np,
+void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,
                                   struct snd_soc_codec_conf *codec_conf,
                                   struct device_node *of_node,
                                   const char *propname)
 {
+       struct device_node *np = card->dev->of_node;
        const char *str;
        int ret;
 
-       if (!np)
-               np = card->dev->of_node;
-
        ret = of_property_read_string(np, propname, &str);
        if (ret < 0) {
                /* no prefix is not error */
@@ -3688,19 +3864,16 @@ void snd_soc_of_parse_audio_prefix_from_node(struct snd_soc_card *card,
        codec_conf->of_node     = of_node;
        codec_conf->name_prefix = str;
 }
-EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix_from_node);
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix);
 
-int snd_soc_of_parse_audio_routing_from_node(struct snd_soc_card *card,
-                                  struct device_node *np,
+int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname)
 {
+       struct device_node *np = card->dev->of_node;
        int num_routes;
        struct snd_soc_dapm_route *routes;
        int i, ret;
 
-       if (!np)
-               np = card->dev->of_node;
-
        num_routes = of_property_count_strings(np, propname);
        if (num_routes < 0 || num_routes & 1) {
                dev_err(card->dev,
@@ -3747,7 +3920,7 @@ int snd_soc_of_parse_audio_routing_from_node(struct snd_soc_card *card,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing_from_node);
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
 
 unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
                                     const char *prefix,
@@ -4020,8 +4193,6 @@ static void __exit snd_soc_exit(void)
        snd_soc_util_exit();
        snd_soc_debugfs_exit();
 
-#ifdef CONFIG_DEBUG_FS
-#endif
        platform_driver_unregister(&soc_driver);
 }
 module_exit(snd_soc_exit);
index 27dd02e57b31b0083f00a7864005a8ca867c42fb..dcef67a9bd4854a29c79ad8355343c5f16ae76a2 100644 (file)
@@ -363,6 +363,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
                                snd_soc_dapm_new_control_unlocked(widget->dapm,
                                &template);
                        kfree(name);
+                       if (IS_ERR(data->widget)) {
+                               ret = PTR_ERR(data->widget);
+                               goto err_data;
+                       }
                        if (!data->widget) {
                                ret = -ENOMEM;
                                goto err_data;
@@ -397,6 +401,10 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
                        data->widget = snd_soc_dapm_new_control_unlocked(
                                                widget->dapm, &template);
                        kfree(name);
+                       if (IS_ERR(data->widget)) {
+                               ret = PTR_ERR(data->widget);
+                               goto err_data;
+                       }
                        if (!data->widget) {
                                ret = -ENOMEM;
                                goto err_data;
@@ -3403,11 +3411,22 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 
        mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
        w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+       /* Do not nag about probe deferrals */
+       if (IS_ERR(w)) {
+               int ret = PTR_ERR(w);
+
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dapm->dev,
+                               "ASoC: Failed to create DAPM control %s (%d)\n",
+                               widget->name, ret);
+               goto out_unlock;
+       }
        if (!w)
                dev_err(dapm->dev,
                        "ASoC: Failed to create DAPM control %s\n",
                        widget->name);
 
+out_unlock:
        mutex_unlock(&dapm->card->dapm_mutex);
        return w;
 }
@@ -3430,6 +3449,8 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
                w->regulator = devm_regulator_get(dapm->dev, w->name);
                if (IS_ERR(w->regulator)) {
                        ret = PTR_ERR(w->regulator);
+                       if (ret == -EPROBE_DEFER)
+                               return ERR_PTR(ret);
                        dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
                                w->name, ret);
                        return NULL;
@@ -3448,6 +3469,8 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
                w->clk = devm_clk_get(dapm->dev, w->name);
                if (IS_ERR(w->clk)) {
                        ret = PTR_ERR(w->clk);
+                       if (ret == -EPROBE_DEFER)
+                               return ERR_PTR(ret);
                        dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
                                w->name, ret);
                        return NULL;
@@ -3566,6 +3589,16 @@ int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
        mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
        for (i = 0; i < num; i++) {
                w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+               if (IS_ERR(w)) {
+                       ret = PTR_ERR(w);
+                       /* Do not nag about probe deferrals */
+                       if (ret == -EPROBE_DEFER)
+                               break;
+                       dev_err(dapm->dev,
+                               "ASoC: Failed to create DAPM control %s (%d)\n",
+                               widget->name, ret);
+                       break;
+               }
                if (!w) {
                        dev_err(dapm->dev,
                                "ASoC: Failed to create DAPM control %s\n",
@@ -3842,6 +3875,15 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
        dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);
 
        w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
+       if (IS_ERR(w)) {
+               ret = PTR_ERR(w);
+               /* Do not nag about probe deferrals */
+               if (ret != -EPROBE_DEFER)
+                       dev_err(card->dev,
+                               "ASoC: Failed to create %s widget (%d)\n",
+                               link_name, ret);
+               goto outfree_kcontrol_news;
+       }
        if (!w) {
                dev_err(card->dev, "ASoC: Failed to create %s widget\n",
                        link_name);
@@ -3893,6 +3935,16 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
                        template.name);
 
                w = snd_soc_dapm_new_control_unlocked(dapm, &template);
+               if (IS_ERR(w)) {
+                       int ret = PTR_ERR(w);
+
+                       /* Do not nag about probe deferrals */
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(dapm->dev,
+                               "ASoC: Failed to create %s widget (%d)\n",
+                               dai->driver->playback.stream_name, ret);
+                       return ret;
+               }
                if (!w) {
                        dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
                                dai->driver->playback.stream_name);
@@ -3912,6 +3964,16 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
                        template.name);
 
                w = snd_soc_dapm_new_control_unlocked(dapm, &template);
+               if (IS_ERR(w)) {
+                       int ret = PTR_ERR(w);
+
+                       /* Do not nag about probe deferrals */
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(dapm->dev,
+                               "ASoC: Failed to create %s widget (%d)\n",
+                               dai->driver->playback.stream_name, ret);
+                       return ret;
+               }
                if (!w) {
                        dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
                                dai->driver->capture.stream_name);
index 17eb14935577764a59b9dd06e27cf515ce850ab8..d53786498b61238cd8a6ade0481df8f65a9626bd 100644 (file)
@@ -263,6 +263,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
        struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
        const struct snd_dmaengine_pcm_config *config = pcm->config;
        struct device *dev = rtd->platform->dev;
+       struct snd_dmaengine_dai_dma_data *dma_data;
        struct snd_pcm_substream *substream;
        size_t prealloc_buffer_size;
        size_t max_buffer_size;
@@ -282,6 +283,13 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
                if (!substream)
                        continue;
 
+               dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+               if (!pcm->chan[i] &&
+                   (pcm->flags & SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME))
+                       pcm->chan[i] = dma_request_slave_channel(dev,
+                               dma_data->chan_name);
+
                if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
                        pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
                                substream);
@@ -350,7 +358,9 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
        const char *name;
        struct dma_chan *chan;
 
-       if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !dev->of_node)
+       if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
+                          SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
+           !dev->of_node)
                return 0;
 
        if (config && config->dma_dev) {
index 9fc1a7bb8b953a7a2955ea1ca2d31b30fccfee37..500f98c730b964e6aee457d91ea21e29020a5334 100644 (file)
@@ -120,7 +120,7 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
 /**
- * snd_soc_read_signed - Read a codec register and interprete as signed value
+ * snd_soc_read_signed - Read a codec register and interpret as signed value
  * @component: component
  * @reg: Register to read
  * @mask: Mask to use after shifting the register value
index 6aba14009c92abc853d72575ed447e934ed5e458..efc5831f205dc41670d35013926bd0749d16d4d7 100644 (file)
@@ -1055,7 +1055,6 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
                                   int cmd)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
        int i, ret;
@@ -1071,12 +1070,6 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
                }
        }
 
-       if (platform->driver->bespoke_trigger) {
-               ret = platform->driver->bespoke_trigger(substream, cmd);
-               if (ret < 0)
-                       return ret;
-       }
-
        if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) {
                ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai);
                if (ret < 0)
@@ -1116,13 +1109,6 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
        }
        delay += codec_delay;
 
-       /*
-        * None of the existing platform drivers implement delay(), so
-        * for now the codec_dai of first multicodec entry is used
-        */
-       if (platform->driver->delay)
-               delay += platform->driver->delay(substream, rtd->codec_dais[0]);
-
        runtime->delay = delay;
 
        return offset;
@@ -2642,12 +2628,25 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
        return ret;
 }
 
+static void soc_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+       struct snd_soc_component *component;
+
+       list_for_each_entry(component, &rtd->card->component_dev_list,
+                           card_list) {
+               if (component->pcm_free)
+                       component->pcm_free(pcm);
+       }
+}
+
 /* create a new pcm */
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 {
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_component *component;
        struct snd_pcm *pcm;
        char new_name[64];
        int ret = 0, playback = 0, capture = 0;
@@ -2756,17 +2755,18 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
        if (capture)
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
 
-       if (platform->driver->pcm_new) {
-               ret = platform->driver->pcm_new(rtd);
-               if (ret < 0) {
-                       dev_err(platform->dev,
-                               "ASoC: pcm constructor failed: %d\n",
-                               ret);
-                       return ret;
+       list_for_each_entry(component, &rtd->card->component_dev_list, card_list) {
+               if (component->pcm_new) {
+                       ret = component->pcm_new(rtd);
+                       if (ret < 0) {
+                               dev_err(component->dev,
+                                       "ASoC: pcm constructor failed: %d\n",
+                                       ret);
+                               return ret;
+                       }
                }
        }
-
-       pcm->private_free = platform->driver->pcm_free;
+       pcm->private_free = soc_pcm_free;
 out:
        dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
                 (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
@@ -2874,15 +2874,6 @@ int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dpcm_can_be_params);
 
-int snd_soc_platform_trigger(struct snd_pcm_substream *substream,
-               int cmd, struct snd_soc_platform *platform)
-{
-       if (platform->driver->ops && platform->driver->ops->trigger)
-               return platform->driver->ops->trigger(substream, cmd);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_platform_trigger);
-
 #ifdef CONFIG_DEBUG_FS
 static const char *dpcm_state_string(enum snd_soc_dpcm_state state)
 {
index fbfb1fab88d5be60de7c40ed8fa1dd78d6fae727..8419edb9d8f94fb17af64782e11c024760099f76 100644 (file)
@@ -1555,6 +1555,15 @@ widget:
                widget = snd_soc_dapm_new_control(dapm, &template);
        else
                widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
+       if (IS_ERR(widget)) {
+               ret = PTR_ERR(widget);
+               /* Do not nag about probe deferrals */
+               if (ret != -EPROBE_DEFER)
+                       dev_err(tplg->dev,
+                               "ASoC: failed to create widget %s controls (%d)\n",
+                               w->name, ret);
+               goto hdr_err;
+       }
        if (widget == NULL) {
                dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
                        w->name);
@@ -1918,7 +1927,7 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
 
 /**
  * set_link_hw_format - Set the HW audio format of the physical DAI link.
- * @tplg: topology context
+ * @link: &snd_soc_dai_link which should be updated
  * @cfg: physical link configs.
  *
  * Topology context contains a list of supported HW formats (configs) and
@@ -1969,7 +1978,7 @@ static void set_link_hw_format(struct snd_soc_dai_link *link,
 /**
  * link_new_ver - Create a new physical link config from the old
  * version of source.
- * @toplogy: topology context
+ * @tplg: topology context
  * @src: old version of phyical link config as a source
  * @link: latest version of physical link config created from the source
  *
@@ -2211,7 +2220,7 @@ static int soc_tplg_dai_elems_load(struct soc_tplg *tplg,
 /**
  * manifest_new_ver - Create a new version of manifest from the old version
  * of source.
- * @toplogy: topology context
+ * @tplg: topology context
  * @src: old version of manifest as a source
  * @manifest: latest version of manifest created from the source
  *
index f24d19526603c2d2a31ef9af1cff07ed4dd8d1ca..4237323ef5945a37c11c0097f5f4ff62babca0f1 100644 (file)
@@ -694,10 +694,10 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
        }
        
        i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
-       i2s->playback_dma_data.maxburst = 4;
+       i2s->playback_dma_data.maxburst = 8;
 
        i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
-       i2s->capture_dma_data.maxburst = 4;
+       i2s->capture_dma_data.maxburst = 8;
 
        pm_runtime_enable(&pdev->dev);
        if (!pm_runtime_enabled(&pdev->dev)) {