]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
Merge branch 'asoc-fix-ep93xx' into spi-fix-ep93xx
authorMark Brown <broonie@kernel.org>
Thu, 26 May 2016 10:13:12 +0000 (11:13 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 26 May 2016 10:13:12 +0000 (11:13 +0100)
83 files changed:
Documentation/devicetree/bindings/sound/davinci-mcbsp.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl-sai.txt
Documentation/devicetree/bindings/sound/pcm5102a.txt [new file with mode: 0644]
MAINTAINERS
drivers/spi/spi-ep93xx.c
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
include/sound/dmaengine_pcm.h
include/sound/hdaudio_ext.h
include/sound/hdmi-codec.h [new file with mode: 0644]
include/sound/pcm_iec958.h
include/sound/soc-dapm.h
include/sound/soc.h
sound/core/pcm_dmaengine.c
sound/core/pcm_iec958.c
sound/hda/ext/hdac_ext_bus.c
sound/hda/ext/hdac_ext_controller.c
sound/hda/local.h
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/au1x/dbdma2.c
sound/soc/bcm/bcm2835-i2s.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/cs42l56.c
sound/soc/codecs/cs47l24.c
sound/soc/codecs/da7213.c
sound/soc/codecs/da7213.h
sound/soc/codecs/da7218.c
sound/soc/codecs/da7218.h
sound/soc/codecs/da7219.c
sound/soc/codecs/da7219.h
sound/soc/codecs/es8328.c
sound/soc/codecs/es8328.h
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/hdmi-codec.c [new file with mode: 0644]
sound/soc/codecs/pcm5102a.c [new file with mode: 0644]
sound/soc/codecs/rt298.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/davinci/Kconfig
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-mcasp.h
sound/soc/dwc/designware_i2s.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-pcm-fiq.c
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst-atom-controls.c
sound/soc/intel/boards/Makefile
sound/soc/intel/boards/broadwell.c
sound/soc/intel/boards/bxt_rt298.c [new file with mode: 0644]
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/bytcr_rt5651.c
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/cht_bsw_rt5672.c
sound/soc/intel/boards/haswell.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-acpi.h
sound/soc/intel/haswell/sst-haswell-pcm.c
sound/soc/intel/skylake/Makefile
sound/soc/intel/skylake/bxt-sst.c [new file with mode: 0644]
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.c
sound/soc/intel/skylake/skl-sst-dsp.h
sound/soc/intel/skylake/skl-sst.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl-tplg-interface.h
sound/soc/intel/skylake/skl.c
sound/soc/intel/skylake/skl.h
sound/soc/rockchip/rockchip_i2s.c
sound/soc/soc-core.c
sound/soc/soc-generic-dmaengine-pcm.c

diff --git a/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt b/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
new file mode 100644 (file)
index 0000000..55b53e1
--- /dev/null
@@ -0,0 +1,51 @@
+Texas Instruments DaVinci McBSP module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This binding describes the "Multi-channel Buffered Serial Port" (McBSP)
+audio interface found in some TI DaVinci processors like the OMAP-L138 or AM180x.
+
+
+Required properties:
+~~~~~~~~~~~~~~~~~~~~
+- compatible :
+        "ti,da850-mcbsp" : for DA850, AM180x and OPAM-L138 platforms
+
+- reg : physical base address and length of the controller memory mapped
+        region(s).
+- reg-names : Should contain:
+        * "mpu" for the main registers (required).
+        * "dat" for the data FIFO (optional).
+
+- dmas: three element list of DMA controller phandles, DMA request line and
+       TC channel ordered triplets.
+- dma-names: identifier string for each DMA request line in the dmas property.
+       These strings correspond 1:1 with the ordered pairs in dmas. The dma
+       identifiers must be "rx" and "tx".
+
+Optional properties:
+~~~~~~~~~~~~~~~~~~~~
+- interrupts : Interrupt numbers for McBSP
+- interrupt-names : Known interrupt names are "rx" and "tx"
+
+- pinctrl-0: Should specify pin control group used for this controller.
+- pinctrl-names: Should contain only one value - "default", for more details
+        please refer to pinctrl-bindings.txt
+
+Example (AM1808):
+~~~~~~~~~~~~~~~~~
+
+mcbsp0: mcbsp@1d10000 {
+       compatible = "ti,da850-mcbsp";
+       pinctrl-names = "default";
+       pinctrl-0 = <&mcbsp0_pins>;
+
+       reg =   <0x00110000 0x1000>,
+               <0x00310000 0x1000>;
+       reg-names = "mpu", "dat";
+       interrupts = <97 98>;
+       interrupts-names = "rx", "tx";
+       dmas = <&edma0 3 1
+               &edma0 2 1>;
+       dma-names = "tx", "rx";
+       status = "okay";
+};
index 044e5d76e2dd32f251be377266b8fa0e3436e584..740b467adf7d1d46f318d5a2a6bf74f35cfa80bc 100644 (file)
@@ -7,8 +7,8 @@ codec/DSP interfaces.
 
 Required properties:
 
-  - compatible         : Compatible list, contains "fsl,vf610-sai" or
-                         "fsl,imx6sx-sai".
+  - compatible         : Compatible list, contains "fsl,vf610-sai",
+                         "fsl,imx6sx-sai" or "fsl,imx6ul-sai"
 
   - reg                        : Offset and length of the register set for the device.
 
@@ -48,6 +48,11 @@ Required properties:
                          receive data by following their own bit clocks and
                          frame sync clocks separately.
 
+Optional properties (for mx6ul):
+
+  - fsl,sai-mclk-direction-output: This is a boolean property. If present,
+                        indicates that SAI will output the SAI MCLK clock.
+
 Note:
 - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
   default synchronous mode (sync Rx with Tx) will be used, which means both
diff --git a/Documentation/devicetree/bindings/sound/pcm5102a.txt b/Documentation/devicetree/bindings/sound/pcm5102a.txt
new file mode 100644 (file)
index 0000000..c63ab0b
--- /dev/null
@@ -0,0 +1,13 @@
+PCM5102a audio CODECs
+
+These devices does not use I2C or SPI.
+
+Required properties:
+
+  - compatible : set as "ti,pcm5102a"
+
+Examples:
+
+       pcm5102a: pcm5102a {
+               compatible = "ti,pcm5102a";
+       };
index 9c567a431d8d44dc30104b2962366137c29911f2..8e325edbf62cacf1ba53c728e3063162f88826b1 100644 (file)
@@ -4661,6 +4661,7 @@ FREESCALE SOC SOUND DRIVERS
 M:     Timur Tabi <timur@tabi.org>
 M:     Nicolin Chen <nicoleotsuka@gmail.com>
 M:     Xiubo Li <Xiubo.Lee@gmail.com>
+R:     Fabio Estevam <fabio.estevam@nxp.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:     linuxppc-dev@lists.ozlabs.org
 S:     Maintained
index bb00be8d185106986dc284cf8a256c0449a740fd..17a6387e20b5fb0f9cf673cfb4cc8e4322ea9fed 100644 (file)
@@ -567,7 +567,7 @@ static void ep93xx_spi_dma_transfer(struct ep93xx_spi *espi)
        txd = ep93xx_spi_dma_prepare(espi, DMA_MEM_TO_DEV);
        if (IS_ERR(txd)) {
                ep93xx_spi_dma_finish(espi, DMA_DEV_TO_MEM);
-               dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(rxd));
+               dev_err(&espi->pdev->dev, "DMA TX failed: %ld\n", PTR_ERR(txd));
                msg->status = PTR_ERR(txd);
                return;
        }
index 238c8db953eba25d61151709062968d669ae0b6d..68353822afceb7b37b95ee0a2a5e3504823559dc 100644 (file)
 #define IMX6UL_GPR1_ENET2_CLK_OUTPUT           (0x1 << 18)
 #define IMX6UL_GPR1_ENET_CLK_DIR               (0x3 << 17)
 #define IMX6UL_GPR1_ENET_CLK_OUTPUT            (0x3 << 17)
+#define IMX6UL_GPR1_SAI1_MCLK_DIR              (0x1 << 19)
+#define IMX6UL_GPR1_SAI2_MCLK_DIR              (0x1 << 20)
+#define IMX6UL_GPR1_SAI3_MCLK_DIR              (0x1 << 21)
+#define IMX6UL_GPR1_SAI_MCLK_MASK              (0x7 << 19)
+#define MCLK_DIR(x) (x == 1 ? IMX6UL_GPR1_SAI1_MCLK_DIR : x == 2 ? \
+                    IMX6UL_GPR1_SAI2_MCLK_DIR : IMX6UL_GPR1_SAI3_MCLK_DIR)
 
 #endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */
index f86ef5ea9b0147c4f51d6f885d5566ef4c6a023b..67be2445941a613bcc74918d8964bfe3aef907ff 100644 (file)
@@ -51,6 +51,16 @@ struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn,
        void *filter_data);
 struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream);
 
+/*
+ * The DAI supports packed transfers, eg 2 16-bit samples in a 32-bit word.
+ * If this flag is set the dmaengine driver won't put any restriction on
+ * the supported sample formats and set the DMA transfer size to undefined.
+ * The DAI driver is responsible to disable any unsupported formats in it's
+ * configuration and catch corner cases that are not already handled in
+ * the ALSA core.
+ */
+#define SND_DMAENGINE_PCM_DAI_FLAG_PACK BIT(0)
+
 /**
  * struct snd_dmaengine_dai_dma_data - DAI DMA configuration data
  * @addr: Address of the DAI data source or destination register.
@@ -63,6 +73,7 @@ struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream)
  * 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
  */
 struct snd_dmaengine_dai_dma_data {
        dma_addr_t addr;
@@ -72,6 +83,7 @@ struct snd_dmaengine_dai_dma_data {
        void *filter_data;
        const char *chan_name;
        unsigned int fifo_size;
+       unsigned int flags;
 };
 
 void snd_dmaengine_pcm_set_config_from_dai_data(
index 07fa59237feb032e3e6dcb6660cca6d17b5679e4..b9593b201599f94f49b35a36c82a6f927d97677b 100644 (file)
@@ -14,6 +14,8 @@
  * @gtscap: gts capabilities pointer
  * @drsmcap: dma resume capabilities pointer
  * @hlink_list: link list of HDA links
+ * @lock: lock for link mgmt
+ * @cmd_dma_state: state of cmd DMAs: CORB and RIRB
  */
 struct hdac_ext_bus {
        struct hdac_bus bus;
@@ -27,6 +29,9 @@ struct hdac_ext_bus {
        void __iomem *drsmcap;
 
        struct list_head hlink_list;
+
+       struct mutex lock;
+       bool cmd_dma_state;
 };
 
 int snd_hdac_ext_bus_init(struct hdac_ext_bus *sbus, struct device *dev,
@@ -142,6 +147,9 @@ struct hdac_ext_link {
        void __iomem *ml_addr; /* link output stream reg pointer */
        u32 lcaps;   /* link capablities */
        u16 lsdiid;  /* link sdi identifier */
+
+       int ref_count;
+
        struct list_head list;
 };
 
@@ -154,6 +162,11 @@ void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
 void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
                                 int stream);
 
+int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link);
+int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link);
+
 /* update register macro */
 #define snd_hdac_updatel(addr, reg, mask, val)         \
        writel(((readl(addr + reg) & ~(mask)) | (val)), \
diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h
new file mode 100644 (file)
index 0000000..fc3a481
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * hdmi-codec.h - HDMI Codec driver API
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Jyri Sarha <jsarha@ti.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __HDMI_CODEC_H__
+#define __HDMI_CODEC_H__
+
+#include <linux/hdmi.h>
+#include <drm/drm_edid.h>
+#include <sound/asoundef.h>
+#include <uapi/sound/asound.h>
+
+/*
+ * Protocol between ASoC cpu-dai and HDMI-encoder
+ */
+struct hdmi_codec_daifmt {
+       enum {
+               HDMI_I2S,
+               HDMI_RIGHT_J,
+               HDMI_LEFT_J,
+               HDMI_DSP_A,
+               HDMI_DSP_B,
+               HDMI_AC97,
+               HDMI_SPDIF,
+       } fmt;
+       int bit_clk_inv:1;
+       int frame_clk_inv:1;
+       int bit_clk_master:1;
+       int frame_clk_master:1;
+};
+
+/*
+ * HDMI audio parameters
+ */
+struct hdmi_codec_params {
+       struct hdmi_audio_infoframe cea;
+       struct snd_aes_iec958 iec;
+       int sample_rate;
+       int sample_width;
+       int channels;
+};
+
+struct hdmi_codec_ops {
+       /*
+        * Called when ASoC starts an audio stream setup.
+        * Optional
+        */
+       int (*audio_startup)(struct device *dev);
+
+       /*
+        * Configures HDMI-encoder for audio stream.
+        * Mandatory
+        */
+       int (*hw_params)(struct device *dev,
+                        struct hdmi_codec_daifmt *fmt,
+                        struct hdmi_codec_params *hparms);
+
+       /*
+        * Shuts down the audio stream.
+        * Mandatory
+        */
+       void (*audio_shutdown)(struct device *dev);
+
+       /*
+        * Mute/unmute HDMI audio stream.
+        * Optional
+        */
+       int (*digital_mute)(struct device *dev, bool enable);
+
+       /*
+        * Provides EDID-Like-Data from connected HDMI device.
+        * Optional
+        */
+       int (*get_eld)(struct device *dev, uint8_t *buf, size_t len);
+};
+
+/* HDMI codec initalization data */
+struct hdmi_codec_pdata {
+       const struct hdmi_codec_ops *ops;
+       uint i2s:1;
+       uint spdif:1;
+       int max_i2s_channels;
+};
+
+#define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
+
+#endif /* __HDMI_CODEC_H__ */
index 0eed397aca8e87904540ddbf8156f9abf6de6ec2..36f023acb201efef62fabd2debcb56cb8b7e1691 100644 (file)
@@ -6,4 +6,6 @@
 int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
        size_t len);
 
+int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+                                            u8 *cs, size_t len);
 #endif
index 97069466c38dd90ffb518fc8b367073c787697be..3101d53468aad5e1e86f7d65a90fdc37ffbafd8f 100644 (file)
@@ -100,6 +100,7 @@ struct device;
 {       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \
        SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
+/* DEPRECATED: use SND_SOC_DAPM_SUPPLY */
 #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_micbias, .name = wname, \
        SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
@@ -473,7 +474,7 @@ enum snd_soc_dapm_type {
        snd_soc_dapm_out_drv,                   /* output driver */
        snd_soc_dapm_adc,                       /* analog to digital converter */
        snd_soc_dapm_dac,                       /* digital to analog converter */
-       snd_soc_dapm_micbias,           /* microphone bias (power) */
+       snd_soc_dapm_micbias,           /* microphone bias (power) - DEPRECATED: use snd_soc_dapm_supply */
        snd_soc_dapm_mic,                       /* microphone */
        snd_soc_dapm_hp,                        /* headphones */
        snd_soc_dapm_spk,                       /* speaker */
index 02b4a215fd751e6fcddf1856f667fe8c3e56a2eb..fd7b58a58d6f9c26b484210a237df709e3fb2f4d 100644 (file)
@@ -1002,7 +1002,7 @@ struct snd_soc_dai_link {
         */
        const char *platform_name;
        struct device_node *platform_of_node;
-       int be_id;      /* optional ID for machine driver BE identification */
+       int id; /* optional ID for machine driver link identification */
 
        const struct snd_soc_pcm_stream *params;
        unsigned int num_params;
@@ -1683,6 +1683,9 @@ void snd_soc_remove_dai_link(struct snd_soc_card *card,
 int snd_soc_register_dai(struct snd_soc_component *component,
        struct snd_soc_dai_driver *dai_drv);
 
+struct snd_soc_dai *snd_soc_find_dai(
+       const struct snd_soc_dai_link_component *dlc);
+
 #include <sound/soc-dai.h>
 
 #ifdef CONFIG_DEBUG_FS
index 697c166acf05e60cce4a23add6a0b45978259dbe..8eb58c709b141140660ec623d94e01a01abb4a93 100644 (file)
@@ -106,8 +106,9 @@ EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config);
  * direction of the substream. If the substream is a playback stream the dst
  * fields will be initialized, if it is a capture stream the src fields will be
  * initialized. The {dst,src}_addr_width field will only be initialized if the
- * addr_width field of the DAI DMA data struct is not equal to
- * DMA_SLAVE_BUSWIDTH_UNDEFINED.
+ * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of
+ * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If
+ * both conditions are met the latter takes priority.
  */
 void snd_dmaengine_pcm_set_config_from_dai_data(
        const struct snd_pcm_substream *substream,
@@ -117,11 +118,17 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                slave_config->dst_addr = dma_data->addr;
                slave_config->dst_maxburst = dma_data->maxburst;
+               if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
+                       slave_config->dst_addr_width =
+                               DMA_SLAVE_BUSWIDTH_UNDEFINED;
                if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
                        slave_config->dst_addr_width = dma_data->addr_width;
        } else {
                slave_config->src_addr = dma_data->addr;
                slave_config->src_maxburst = dma_data->maxburst;
+               if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)
+                       slave_config->src_addr_width =
+                               DMA_SLAVE_BUSWIDTH_UNDEFINED;
                if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED)
                        slave_config->src_addr_width = dma_data->addr_width;
        }
index 36b2d7aca1bdc3af6eefa9b9793343e32f12ebec..5e6aed64f45150e839a4ab3bf9a4a9554791d281 100644 (file)
@@ -9,30 +9,18 @@
 #include <linux/types.h>
 #include <sound/asoundef.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include <sound/pcm_iec958.h>
 
-/**
- * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
- * @runtime: pcm runtime structure with ->rate filled in
- * @cs: channel status buffer, at least four bytes
- * @len: length of channel status buffer
- *
- * Create the consumer format channel status data in @cs of maximum size
- * @len corresponding to the parameters of the PCM runtime @runtime.
- *
- * Drivers may wish to tweak the contents of the buffer after creation.
- *
- * Returns: length of buffer, or negative error code if something failed.
- */
-int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
-       size_t len)
+static int create_iec958_consumer(uint rate, uint sample_width,
+                                 u8 *cs, size_t len)
 {
        unsigned int fs, ws;
 
        if (len < 4)
                return -EINVAL;
 
-       switch (runtime->rate) {
+       switch (rate) {
        case 32000:
                fs = IEC958_AES3_CON_FS_32000;
                break;
@@ -59,7 +47,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
        }
 
        if (len > 4) {
-               switch (snd_pcm_format_width(runtime->format)) {
+               switch (sample_width) {
                case 16:
                        ws = IEC958_AES4_CON_WORDLEN_20_16;
                        break;
@@ -71,6 +59,7 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
                             IEC958_AES4_CON_MAX_WORDLEN_24;
                        break;
                case 24:
+               case 32: /* Assume 24-bit width for 32-bit samples. */
                        ws = IEC958_AES4_CON_WORDLEN_24_20 |
                             IEC958_AES4_CON_MAX_WORDLEN_24;
                        break;
@@ -92,4 +81,46 @@ int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
 
        return len;
 }
+
+/**
+ * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
+ * @runtime: pcm runtime structure with ->rate filled in
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len corresponding to the parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
+       size_t len)
+{
+       return create_iec958_consumer(runtime->rate,
+                                     snd_pcm_format_width(runtime->format),
+                                     cs, len);
+}
 EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
+
+/**
+ * snd_pcm_create_iec958_consumer_hw_params - create IEC958 channel status
+ * @hw_params: the hw_params instance for extracting rate and sample format
+ * @cs: channel status buffer, at least four bytes
+ * @len: length of channel status buffer
+ *
+ * Create the consumer format channel status data in @cs of maximum size
+ * @len corresponding to the parameters of the PCM runtime @runtime.
+ *
+ * Drivers may wish to tweak the contents of the buffer after creation.
+ *
+ * Returns: length of buffer, or negative error code if something failed.
+ */
+int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+                                            u8 *cs, size_t len)
+{
+       return create_iec958_consumer(params_rate(params), params_width(params),
+                                     cs, len);
+}
+EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
index 2433f7c81472848be51b9af420ec198b523871b1..3b7ae24900fda110fe1465bfe762b4d7974def15 100644 (file)
@@ -105,6 +105,9 @@ int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev,
        INIT_LIST_HEAD(&ebus->hlink_list);
        ebus->idx = idx++;
 
+       mutex_init(&ebus->lock);
+       ebus->cmd_dma_state = true;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init);
index 548cc1e4114bb81af0136cf77d517b4e751c6736..860f8cad6602d59332f1990b2e3ee216da0b72f6 100644 (file)
@@ -186,6 +186,9 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
                hlink->lcaps  = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
                hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
 
+               /* since link in On, update the ref */
+               hlink->ref_count = 1;
+
                list_add_tail(&hlink->list, &ebus->hlink_list);
        }
 
@@ -327,3 +330,66 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_ext_bus *ebus)
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_power_down_all);
+
+int snd_hdac_ext_bus_link_get(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link)
+{
+       int ret = 0;
+
+       mutex_lock(&ebus->lock);
+
+       /*
+        * if we move from 0 to 1, count will be 1 so power up this link
+        * as well, also check the dma status and trigger that
+        */
+       if (++link->ref_count == 1) {
+               if (!ebus->cmd_dma_state) {
+                       snd_hdac_bus_init_cmd_io(&ebus->bus);
+                       ebus->cmd_dma_state = true;
+               }
+
+               ret = snd_hdac_ext_bus_link_power_up(link);
+       }
+
+       mutex_unlock(&ebus->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_get);
+
+int snd_hdac_ext_bus_link_put(struct hdac_ext_bus *ebus,
+                               struct hdac_ext_link *link)
+{
+       int ret = 0;
+       struct hdac_ext_link *hlink;
+       bool link_up = false;
+
+       mutex_lock(&ebus->lock);
+
+       /*
+        * if we move from 1 to 0, count will be 0
+        * so power down this link as well
+        */
+       if (--link->ref_count == 0) {
+               ret = snd_hdac_ext_bus_link_power_down(link);
+
+               /*
+                * now check if all links are off, if so turn off
+                * cmd dma as well
+                */
+               list_for_each_entry(hlink, &ebus->hlink_list, list) {
+                       if (hlink->ref_count) {
+                               link_up = true;
+                               break;
+                       }
+               }
+
+               if (!link_up) {
+                       snd_hdac_bus_stop_cmd_io(&ebus->bus);
+                       ebus->cmd_dma_state = false;
+               }
+       }
+
+       mutex_unlock(&ebus->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_link_put);
index d692f417ddc0a2c159092bd76aaa2c2fef8d44a5..0d5bb159d538ef53dcddb1914a3b4068c90f4795 100644 (file)
@@ -16,6 +16,16 @@ static inline int get_wcaps_type(unsigned int wcaps)
        return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 }
 
+static inline unsigned int get_wcaps_channels(u32 wcaps)
+{
+       unsigned int chans;
+
+       chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+       chans = (chans + 1) * 2;
+
+       return chans;
+}
+
 extern const struct attribute_group *hdac_dev_attr_groups[];
 int hda_widget_sysfs_init(struct hdac_device *codec);
 void hda_widget_sysfs_exit(struct hdac_device *codec);
index 276897033639b0d719d0d4408e1ffb9ec9efe90d..1267e1af0fae420c694577b208c4643dcc3b41ad 100644 (file)
@@ -652,7 +652,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                rcmr =    SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
                        | SSC_BF(RCMR_STTDLY, 1)
                        | SSC_BF(RCMR_START, SSC_START_RISING_RF)
-                       | SSC_BF(RCMR_CKI, SSC_CKI_FALLING)
+                       | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
                        | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
                        | SSC_BF(RCMR_CKS, SSC_CKS_DIV);
 
@@ -692,7 +692,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
                rcmr =    SSC_BF(RCMR_PERIOD, 0)
                        | SSC_BF(RCMR_STTDLY, START_DELAY)
                        | SSC_BF(RCMR_START, SSC_START_RISING_RF)
-                       | SSC_BF(RCMR_CKI, SSC_CKI_FALLING)
+                       | SSC_BF(RCMR_CKI, SSC_CKI_RISING)
                        | SSC_BF(RCMR_CKO, SSC_CKO_NONE)
                        | SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
                                           SSC_CKS_PIN : SSC_CKS_CLOCK);
index 5741c0aa6c0303607cb738668ecaa2d023044354..b5d1caa04d8e77e275bfbfaaca2c9e6bd61a92ed 100644 (file)
@@ -206,8 +206,8 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
        stype = substream->stream;
        pcd = to_dmadata(substream);
 
-       DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
-           "runtime->min_align %d\n",
+       DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %zu "
+           "runtime->min_align %lu\n",
                (unsigned long)runtime->dma_area,
                (unsigned long)runtime->dma_addr, runtime->dma_bytes,
                runtime->min_align);
index 1c1f2210387b26e9551959378f6792f18648652e..6ba20498202ed36906b52096893a88867a79269f 100644 (file)
@@ -259,6 +259,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
        case SNDRV_PCM_FORMAT_S16_LE:
                data_length = 16;
                break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               data_length = 24;
+               break;
        case SNDRV_PCM_FORMAT_S32_LE:
                data_length = 32;
                break;
@@ -273,13 +276,20 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
                /* otherwise calculate a fitting block ratio */
                bclk_ratio = 2 * data_length;
 
-       /* set target clock rate*/
-       clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
+       /* Clock should only be set up here if CPU is clock master */
+       switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+       case SND_SOC_DAIFMT_CBS_CFM:
+               clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
+               break;
+       default:
+               break;
+       }
 
        /* Setup the frame format */
        format = BCM2835_I2S_CHEN;
 
-       if (data_length > 24)
+       if (data_length >= 24)
                format |= BCM2835_I2S_CHWEX;
 
        format |= BCM2835_I2S_CHWID((data_length-8)&0xf);
@@ -570,6 +580,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
                .channels_max = 2,
                .rates =        SNDRV_PCM_RATE_8000_192000,
                .formats =      SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
                                | SNDRV_PCM_FMTBIT_S32_LE
                },
        .capture = {
@@ -577,6 +588,7 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = {
                .channels_max = 2,
                .rates =        SNDRV_PCM_RATE_8000_192000,
                .formats =      SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
                                | SNDRV_PCM_FMTBIT_S32_LE
                },
        .ops = &bcm2835_i2s_dai_ops,
@@ -678,6 +690,15 @@ static int bcm2835_i2s_probe(struct platform_device *pdev)
        dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2;
        dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2;
 
+       /*
+        * Set the PACK flag to enable S16_LE support (2 S16_LE values
+        * packed into 32-bit transfers).
+        */
+       dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].flags =
+               SND_DMAENGINE_PCM_DAI_FLAG_PACK;
+       dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags =
+               SND_DMAENGINE_PCM_DAI_FLAG_PACK;
+
        /* BCLK ratio - use default */
        dev->bclk_ratio = 0;
 
index 7ef3a0c16478d707b1ef843aecf0631a51813fb1..b3afae990e39633067b4050f0bee756a0aee128d 100644 (file)
@@ -88,12 +88,14 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_MC13783 if MFD_MC13XXX
        select SND_SOC_ML26124 if I2C
        select SND_SOC_NAU8825 if I2C
+       select SND_SOC_HDMI_CODEC
        select SND_SOC_PCM1681 if I2C
        select SND_SOC_PCM179X_I2C if I2C
        select SND_SOC_PCM179X_SPI if SPI_MASTER
        select SND_SOC_PCM3008
        select SND_SOC_PCM3168A_I2C if I2C
        select SND_SOC_PCM3168A_SPI if SPI_MASTER
+       select SND_SOC_PCM5102A
        select SND_SOC_PCM512x_I2C if I2C
        select SND_SOC_PCM512x_SPI if SPI_MASTER
        select SND_SOC_RT286 if I2C
@@ -477,6 +479,11 @@ config SND_SOC_BT_SCO
 config SND_SOC_DMIC
        tristate
 
+config SND_SOC_HDMI_CODEC
+       tristate
+       select SND_PCM_ELD
+       select SND_PCM_IEC958
+
 config SND_SOC_ES8328
        tristate "Everest Semi ES8328 CODEC"
 
@@ -575,6 +582,9 @@ config SND_SOC_PCM3168A_SPI
        select SND_SOC_PCM3168A
        select REGMAP_SPI
 
+config SND_SOC_PCM5102A
+       tristate
+
 config SND_SOC_PCM512x
        tristate
 
index 185a712a7fe763bbba79fa1f4969cf5ba5424753..b7b99416537fd9016360e8b78676d0ea401d0ae6 100644 (file)
@@ -81,6 +81,7 @@ snd-soc-max9850-objs := max9850.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
 snd-soc-nau8825-objs := nau8825.o
+snd-soc-hdmi-codec-objs := hdmi-codec.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm179x-codec-objs := pcm179x.o
 snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
@@ -89,6 +90,7 @@ snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-pcm3168a-objs := pcm3168a.o
 snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
 snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
+snd-soc-pcm5102a-objs := pcm5102a.o
 snd-soc-pcm512x-objs := pcm512x.o
 snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
 snd-soc-pcm512x-spi-objs := pcm512x-spi.o
@@ -290,6 +292,7 @@ obj-$(CONFIG_SND_SOC_MAX9850)       += snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MC13783)  += snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)  += snd-soc-ml26124.o
 obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
+obj-$(CONFIG_SND_SOC_HDMI_CODEC)       += snd-soc-hdmi-codec.o
 obj-$(CONFIG_SND_SOC_PCM1681)  += snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM179X)  += snd-soc-pcm179x-codec.o
 obj-$(CONFIG_SND_SOC_PCM179X_I2C)      += snd-soc-pcm179x-i2c.o
@@ -298,6 +301,7 @@ obj-$(CONFIG_SND_SOC_PCM3008)       += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
 obj-$(CONFIG_SND_SOC_PCM3168A_I2C)     += snd-soc-pcm3168a-i2c.o
 obj-$(CONFIG_SND_SOC_PCM3168A_SPI)     += snd-soc-pcm3168a-spi.o
+obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
 obj-$(CONFIG_SND_SOC_PCM512x)  += snd-soc-pcm512x.o
 obj-$(CONFIG_SND_SOC_PCM512x_I2C)      += snd-soc-pcm512x-i2c.o
 obj-$(CONFIG_SND_SOC_PCM512x_SPI)      += snd-soc-pcm512x-spi.o
index cda27c22812a71442dd3d927088c2118aa16dc17..1ee8506c06c75705ab643dc4f8ef6b04b9b55e64 100644 (file)
@@ -608,9 +608,7 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
 
        of_property_read_string(np, "clock-output-names", &clk_name);
 
-       clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
-                                     (parent_clk_name) ? 0 : CLK_IS_ROOT,
-                                     rate);
+       clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name, 0, rate);
        if (!IS_ERR(clk))
                of_clk_add_provider(np, of_clk_src_simple_get, clk);
 
index 83959312f7a0f5d5e1fcee33385fd1cba0e4db6f..664a8c044ffb1c3abd58afe458ba538a775d9a47 100644 (file)
@@ -221,6 +221,8 @@ int arizona_init_spk(struct snd_soc_codec *codec)
 
        switch (arizona->type) {
        case WM8997:
+       case CS47L24:
+       case WM1831:
                break;
        default:
                ret = snd_soc_dapm_new_controls(dapm, &arizona_spkr, 1);
@@ -1134,7 +1136,6 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
                   int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-       unsigned int mask = 0x3 << w->shift;
        unsigned int val;
 
        switch (event) {
@@ -1148,7 +1149,7 @@ int arizona_anc_ev(struct snd_soc_dapm_widget *w,
                return 0;
        }
 
-       snd_soc_update_bits(codec, ARIZONA_CLOCK_CONTROL, mask, val);
+       snd_soc_write(codec, ARIZONA_CLOCK_CONTROL, val);
 
        return 0;
 }
@@ -2047,7 +2048,21 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
                        init_ratio, Fref, refdiv);
 
        while (div <= ARIZONA_FLL_MAX_REFDIV) {
-               for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
+               /* start from init_ratio because this may already give a
+                * fractional N.K
+                */
+               for (ratio = init_ratio; ratio > 0; ratio--) {
+                       if (target % (ratio * Fref)) {
+                               cfg->refdiv = refdiv;
+                               cfg->fratio = ratio - 1;
+                               arizona_fll_dbg(fll,
+                                       "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
+                                       Fref, refdiv, div, ratio);
+                               return ratio;
+                       }
+               }
+
+               for (ratio = init_ratio + 1; ratio <= ARIZONA_FLL_MAX_FRATIO;
                     ratio++) {
                        if ((ARIZONA_FLL_VCO_CORNER / 2) /
                            (fll->vco_mult * ratio) < Fref) {
@@ -2073,17 +2088,6 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
                        }
                }
 
-               for (ratio = init_ratio - 1; ratio > 0; ratio--) {
-                       if (target % (ratio * Fref)) {
-                               cfg->refdiv = refdiv;
-                               cfg->fratio = ratio - 1;
-                               arizona_fll_dbg(fll,
-                                       "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
-                                       Fref, refdiv, div, ratio);
-                               return ratio;
-                       }
-               }
-
                div *= 2;
                Fref /= 2;
                refdiv++;
index 7cd5f769bb614d207daaf26ca24efb51156d3aca..eec1ff853b98bdc1ca7c2390a9cd50ec44edbf2c 100644 (file)
@@ -56,7 +56,7 @@ struct  cs42l56_private {
        u8 iface;
        u8 iface_fmt;
        u8 iface_inv;
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#if IS_ENABLED(CONFIG_INPUT)
        struct input_dev *beep;
        struct work_struct beep_work;
        int beep_rate;
index 00e9b6fc1b5cab0ddf8b4c3d945a3b2ab27dc749..5ec5a682d186971daa09d532d0b4627806eecaa3 100644 (file)
@@ -807,6 +807,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
        { "IN2L PGA", NULL, "IN2L" },
        { "IN2R PGA", NULL, "IN2R" },
 
+       { "Audio Trace DSP", NULL, "DSP2" },
+       { "Audio Trace DSP", NULL, "SYSCLK" },
+
        ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
        ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
 
@@ -1016,6 +1019,27 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
                        .formats = CS47L24_FORMATS,
                },
        },
+       {
+               .name = "cs47l24-cpu-trace",
+               .capture = {
+                       .stream_name = "Audio Trace CPU",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = CS47L24_RATES,
+                       .formats = CS47L24_FORMATS,
+               },
+               .compress_new = snd_soc_new_compress,
+       },
+       {
+               .name = "cs47l24-dsp-trace",
+               .capture = {
+                       .stream_name = "Audio Trace DSP",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = CS47L24_RATES,
+                       .formats = CS47L24_FORMATS,
+               },
+       },
 };
 
 static int cs47l24_open(struct snd_compr_stream *stream)
@@ -1027,6 +1051,8 @@ static int cs47l24_open(struct snd_compr_stream *stream)
 
        if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
                n_adsp = 2;
+       } else if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-trace") == 0) {
+               n_adsp = 1;
        } else {
                dev_err(arizona->dev,
                        "No suitable compressed stream for DAI '%s'\n",
@@ -1041,10 +1067,16 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
 {
        struct cs47l24_priv *priv = data;
        struct arizona *arizona = priv->core.arizona;
-       int ret;
+       int serviced = 0;
+       int i, ret;
 
-       ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]);
-       if (ret == -ENODEV) {
+       for (i = 1; i <= 2; ++i) {
+               ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+               if (ret != -ENODEV)
+                       serviced++;
+       }
+
+       if (!serviced) {
                dev_err(arizona->dev, "Spurious compressed data IRQ\n");
                return IRQ_NONE;
        }
@@ -1160,6 +1192,7 @@ static struct snd_compr_ops cs47l24_compr_ops = {
 static struct snd_soc_platform_driver cs47l24_compr_platform = {
        .compr_ops = &cs47l24_compr_ops,
 };
+
 static int cs47l24_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -1228,9 +1261,9 @@ static int cs47l24_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
                return ret;
        }
+
        ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
                                      cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
-
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
                snd_soc_unregister_platform(&pdev->dev);
@@ -1241,10 +1274,15 @@ static int cs47l24_probe(struct platform_device *pdev)
 
 static int cs47l24_remove(struct platform_device *pdev)
 {
+       struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
+
        snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
+       wm_adsp2_remove(&cs47l24->core.adsp[1]);
+       wm_adsp2_remove(&cs47l24->core.adsp[2]);
+
        return 0;
 }
 
index 7278f93460c1ee1f5eb48fafaf2f95d0aa406c62..e5527bc570ae550394807f8062092af3e8f0a6e1 100644 (file)
@@ -725,6 +725,68 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = {
 };
 
 
+/*
+ * DAPM Events
+ */
+
+static int da7213_dai_event(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+       u8 pll_ctrl, pll_status;
+       int i = 0;
+       bool srm_lock = false;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               /* Enable DAI clks for master mode */
+               if (da7213->master)
+                       snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE,
+                                           DA7213_DAI_CLK_EN_MASK,
+                                           DA7213_DAI_CLK_EN_MASK);
+
+               /* PC synchronised to DAI */
+               snd_soc_update_bits(codec, DA7213_PC_COUNT,
+                                   DA7213_PC_FREERUN_MASK, 0);
+
+               /* Slave mode, if SRM not enabled no need for status checks */
+               pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL);
+               if (!(pll_ctrl & DA7213_PLL_SRM_EN))
+                       return 0;
+
+               /* Check SRM has locked */
+               do {
+                       pll_status = snd_soc_read(codec, DA7213_PLL_STATUS);
+                       if (pll_status & DA7219_PLL_SRM_LOCK) {
+                               srm_lock = true;
+                       } else {
+                               ++i;
+                               msleep(50);
+                       }
+               } while ((i < DA7213_SRM_CHECK_RETRIES) & (!srm_lock));
+
+               if (!srm_lock)
+                       dev_warn(codec->dev, "SRM failed to lock\n");
+
+               return 0;
+       case SND_SOC_DAPM_POST_PMD:
+               /* PC free-running */
+               snd_soc_update_bits(codec, DA7213_PC_COUNT,
+                                   DA7213_PC_FREERUN_MASK,
+                                   DA7213_PC_FREERUN_MASK);
+
+               /* Disable DAI clks if in master mode */
+               if (da7213->master)
+                       snd_soc_update_bits(codec, DA7213_DAI_CLK_MODE,
+                                           DA7213_DAI_CLK_EN_MASK, 0);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+
 /*
  * DAPM widgets
  */
@@ -736,7 +798,8 @@ static const struct snd_soc_dapm_widget da7213_dapm_widgets[] = {
 
        /* Use a supply here as this controls both input & output DAIs */
        SND_SOC_DAPM_SUPPLY("DAI", DA7213_DAI_CTRL, DA7213_DAI_EN_SHIFT,
-                           DA7213_NO_INVERT, NULL, 0),
+                           DA7213_NO_INVERT, da7213_dai_event,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
        /*
         * Input
@@ -1143,11 +1206,9 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        /* Set master/slave mode */
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBM_CFM:
-               dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE;
                da7213->master = true;
                break;
        case SND_SOC_DAIFMT_CBS_CFS:
-               dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE;
                da7213->master = false;
                break;
        default:
@@ -1281,28 +1342,28 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
        pll_ctrl = 0;
 
        /* Workout input divider based on MCLK rate */
-       if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) {
+       if (da7213->mclk_rate == 32768) {
                /* 32KHz PLL Mode */
-               indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
-               indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
+               indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
+               indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
                freq_ref = 3750000;
                pll_ctrl |= DA7213_PLL_32K_MODE;
        } else {
                /* 5 - 54MHz MCLK */
                if (da7213->mclk_rate < 5000000) {
                        goto pll_err;
-               } else if (da7213->mclk_rate <= 10000000) {
-                       indiv_bits = DA7213_PLL_INDIV_5_10_MHZ;
-                       indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL;
-               } else if (da7213->mclk_rate <= 20000000) {
-                       indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
-                       indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
-               } else if (da7213->mclk_rate <= 40000000) {
-                       indiv_bits = DA7213_PLL_INDIV_20_40_MHZ;
-                       indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL;
+               } else if (da7213->mclk_rate <= 9000000) {
+                       indiv_bits = DA7213_PLL_INDIV_5_TO_9_MHZ;
+                       indiv = DA7213_PLL_INDIV_5_TO_9_MHZ_VAL;
+               } else if (da7213->mclk_rate <= 18000000) {
+                       indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
+                       indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
+               } else if (da7213->mclk_rate <= 36000000) {
+                       indiv_bits = DA7213_PLL_INDIV_18_TO_36_MHZ;
+                       indiv = DA7213_PLL_INDIV_18_TO_36_MHZ_VAL;
                } else if (da7213->mclk_rate <= 54000000) {
-                       indiv_bits = DA7213_PLL_INDIV_40_54_MHZ;
-                       indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL;
+                       indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
+                       indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
                } else {
                        goto pll_err;
                }
@@ -1547,6 +1608,10 @@ static int da7213_probe(struct snd_soc_codec *codec)
        /* Default to using SRM for slave mode */
        da7213->srm_en = true;
 
+       /* Default PC counter to free-running */
+       snd_soc_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK,
+                           DA7213_PC_FREERUN_MASK);
+
        /* Enable all Gain Ramps */
        snd_soc_update_bits(codec, DA7213_AUX_L_CTRL,
                            DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
index 030fd691b07626ab599fe53bd0b3a1903268b94b..fbb7a356a5016d95c65f5fb8675751b40ac725ff 100644 (file)
  * Bit fields
  */
 
+/* DA7213_PLL_STATUS = 0x03 */
+#define DA7219_PLL_SRM_LOCK                                    (0x1 << 1)
+
 /* DA7213_SR = 0x22 */
 #define DA7213_SR_8000                                         (0x1 << 0)
 #define DA7213_SR_11025                                                (0x2 << 0)
 #define DA7213_VMID_EN                                         (0x1 << 7)
 
 /* DA7213_PLL_CTRL = 0x27 */
-#define DA7213_PLL_INDIV_5_10_MHZ                              (0x0 << 2)
-#define DA7213_PLL_INDIV_10_20_MHZ                             (0x1 << 2)
-#define DA7213_PLL_INDIV_20_40_MHZ                             (0x2 << 2)
-#define DA7213_PLL_INDIV_40_54_MHZ                             (0x3 << 2)
+#define DA7213_PLL_INDIV_5_TO_9_MHZ                            (0x0 << 2)
+#define DA7213_PLL_INDIV_9_TO_18_MHZ                           (0x1 << 2)
+#define DA7213_PLL_INDIV_18_TO_36_MHZ                          (0x2 << 2)
+#define DA7213_PLL_INDIV_36_TO_54_MHZ                          (0x3 << 2)
 #define DA7213_PLL_INDIV_MASK                                  (0x3 << 2)
 #define DA7213_PLL_MCLK_SQR_EN                                 (0x1 << 4)
 #define DA7213_PLL_32K_MODE                                    (0x1 << 5)
 #define DA7213_DAI_BCLKS_PER_WCLK_MASK                         (0x3 << 0)
 #define DA7213_DAI_CLK_POL_INV                                 (0x1 << 2)
 #define DA7213_DAI_WCLK_POL_INV                                        (0x1 << 3)
-#define DA7213_DAI_CLK_EN_SLAVE_MODE                           (0x0 << 7)
-#define DA7213_DAI_CLK_EN_MASTER_MODE                          (0x1 << 7)
 #define DA7213_DAI_CLK_EN_MASK                                 (0x1 << 7)
 
 /* DA7213_DAI_CTRL = 0x29 */
 #define DA7213_DMIC_CLK_RATE_SHIFT                             2
 #define DA7213_DMIC_CLK_RATE_MASK                              (0x1 << 2)
 
+/* DA7213_PC_COUNT = 0x94 */
+#define DA7213_PC_FREERUN_MASK                                 (0x1 << 0)
+
 /* DA7213_DIG_CTRL = 0x99 */
 #define DA7213_DAC_L_INV_SHIFT                                 3
 #define DA7213_DAC_R_INV_SHIFT                                 7
 #define DA7213_ALC_AVG_ITERATIONS      5
 
 /* PLL related */
-#define DA7213_SYSCLK_MCLK             0
-#define DA7213_SYSCLK_PLL              1
-#define DA7213_PLL_FREQ_OUT_90316800   90316800
-#define DA7213_PLL_FREQ_OUT_98304000   98304000
-#define DA7213_PLL_FREQ_OUT_94310400   94310400
-#define DA7213_PLL_INDIV_5_10_MHZ_VAL  2
-#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4
-#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
-#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
+#define DA7213_SYSCLK_MCLK                     0
+#define DA7213_SYSCLK_PLL                      1
+#define DA7213_PLL_FREQ_OUT_90316800           90316800
+#define DA7213_PLL_FREQ_OUT_98304000           98304000
+#define DA7213_PLL_FREQ_OUT_94310400           94310400
+#define DA7213_PLL_INDIV_5_TO_9_MHZ_VAL                2
+#define DA7213_PLL_INDIV_9_TO_18_MHZ_VAL       4
+#define DA7213_PLL_INDIV_18_TO_36_MHZ_VAL      8
+#define DA7213_PLL_INDIV_36_TO_54_MHZ_VAL      16
+#define DA7213_SRM_CHECK_RETRIES               8
 
 enum da7213_clk_src {
        DA7213_CLKSRC_MCLK = 0,
index 93575f25186687f7fe21f5c6d514165c6cd27140..99ce23e113bf74804e671642e5d836a8ea273157 100644 (file)
@@ -1868,27 +1868,27 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
 
        /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
        if (da7218->mclk_rate == 32768) {
-               indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
-               indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
+               indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
+               indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
        } else if (da7218->mclk_rate < 2000000) {
                dev_err(codec->dev, "PLL input clock %d below valid range\n",
                        da7218->mclk_rate);
                return -EINVAL;
-       } else if (da7218->mclk_rate <= 5000000) {
-               indiv_bits = DA7218_PLL_INDIV_2_5_MHZ;
-               indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
-       } else if (da7218->mclk_rate <= 10000000) {
-               indiv_bits = DA7218_PLL_INDIV_5_10_MHZ;
-               indiv = DA7218_PLL_INDIV_2_10_MHZ_VAL;
-       } else if (da7218->mclk_rate <= 20000000) {
-               indiv_bits = DA7218_PLL_INDIV_10_20_MHZ;
-               indiv = DA7218_PLL_INDIV_10_20_MHZ_VAL;
-       } else if (da7218->mclk_rate <= 40000000) {
-               indiv_bits = DA7218_PLL_INDIV_20_40_MHZ;
-               indiv = DA7218_PLL_INDIV_20_40_MHZ_VAL;
+       } else if (da7218->mclk_rate <= 4500000) {
+               indiv_bits = DA7218_PLL_INDIV_2_TO_4_5_MHZ;
+               indiv = DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL;
+       } else if (da7218->mclk_rate <= 9000000) {
+               indiv_bits = DA7218_PLL_INDIV_4_5_TO_9_MHZ;
+               indiv = DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL;
+       } else if (da7218->mclk_rate <= 18000000) {
+               indiv_bits = DA7218_PLL_INDIV_9_TO_18_MHZ;
+               indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
+       } else if (da7218->mclk_rate <= 36000000) {
+               indiv_bits = DA7218_PLL_INDIV_18_TO_36_MHZ;
+               indiv = DA7218_PLL_INDIV_18_TO_36_MHZ_VAL;
        } else if (da7218->mclk_rate <= 54000000) {
-               indiv_bits = DA7218_PLL_INDIV_40_54_MHZ;
-               indiv = DA7218_PLL_INDIV_40_54_MHZ_VAL;
+               indiv_bits = DA7218_PLL_INDIV_36_TO_54_MHZ;
+               indiv = DA7218_PLL_INDIV_36_TO_54_MHZ_VAL;
        } else {
                dev_err(codec->dev, "PLL input clock %d above valid range\n",
                        da7218->mclk_rate);
index c2c59049a2ad1e7bf2e0368728c06f1109fcbbb8..477cd37723cfd90a28cd0461e8f8ff277db58ef0 100644 (file)
 /* DA7218_PLL_CTRL = 0x91 */
 #define DA7218_PLL_INDIV_SHIFT         0
 #define DA7218_PLL_INDIV_MASK          (0x7 << 0)
-#define DA7218_PLL_INDIV_2_5_MHZ       (0x0 << 0)
-#define DA7218_PLL_INDIV_5_10_MHZ      (0x1 << 0)
-#define DA7218_PLL_INDIV_10_20_MHZ     (0x2 << 0)
-#define DA7218_PLL_INDIV_20_40_MHZ     (0x3 << 0)
-#define DA7218_PLL_INDIV_40_54_MHZ     (0x4 << 0)
-#define DA7218_PLL_INDIV_2_10_MHZ_VAL  2
-#define DA7218_PLL_INDIV_10_20_MHZ_VAL 4
-#define DA7218_PLL_INDIV_20_40_MHZ_VAL 8
-#define DA7218_PLL_INDIV_40_54_MHZ_VAL 16
+#define DA7218_PLL_INDIV_2_TO_4_5_MHZ  (0x0 << 0)
+#define DA7218_PLL_INDIV_4_5_TO_9_MHZ  (0x1 << 0)
+#define DA7218_PLL_INDIV_9_TO_18_MHZ   (0x2 << 0)
+#define DA7218_PLL_INDIV_18_TO_36_MHZ  (0x3 << 0)
+#define DA7218_PLL_INDIV_36_TO_54_MHZ  (0x4 << 0)
 #define DA7218_PLL_MCLK_SQR_EN_SHIFT   4
 #define DA7218_PLL_MCLK_SQR_EN_MASK    (0x1 << 4)
 #define DA7218_PLL_MODE_SHIFT          6
 #define DA7218_PLL_FREQ_OUT_90316      90316800
 #define DA7218_PLL_FREQ_OUT_98304      98304000
 
+/* PLL Frequency Dividers */
+#define DA7218_PLL_INDIV_2_TO_4_5_MHZ_VAL      1
+#define DA7218_PLL_INDIV_4_5_TO_9_MHZ_VAL      2
+#define DA7218_PLL_INDIV_9_TO_18_MHZ_VAL       4
+#define DA7218_PLL_INDIV_18_TO_36_MHZ_VAL      8
+#define DA7218_PLL_INDIV_36_TO_54_MHZ_VAL      16
+
 /* ALC Calibration */
 #define DA7218_ALC_CALIB_DELAY_MIN     2500
 #define DA7218_ALC_CALIB_DELAY_MAX     5000
index 81c0708b85c1569ad6e109dfac647c732d504b55..5c93899f1f0e51390f171c6f9cdf2c2482be4388 100644 (file)
@@ -11,6 +11,7 @@
  * option) any later version.
  */
 
+#include <linux/acpi.h>
 #include <linux/clk.h>
 #include <linux/i2c.h>
 #include <linux/of_device.h>
@@ -1025,7 +1026,7 @@ static int da7219_set_dai_sysclk(struct snd_soc_dai *codec_dai,
        if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
                return 0;
 
-       if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
+       if ((freq < 2000000) || (freq > 54000000)) {
                dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
                        freq);
                return -EINVAL;
@@ -1079,21 +1080,21 @@ static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
                dev_err(codec->dev, "PLL input clock %d below valid range\n",
                        da7219->mclk_rate);
                return -EINVAL;
-       } else if (da7219->mclk_rate <= 5000000) {
-               indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
-               indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
-       } else if (da7219->mclk_rate <= 10000000) {
-               indiv_bits = DA7219_PLL_INDIV_5_10_MHZ;
-               indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL;
-       } else if (da7219->mclk_rate <= 20000000) {
-               indiv_bits = DA7219_PLL_INDIV_10_20_MHZ;
-               indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL;
-       } else if (da7219->mclk_rate <= 40000000) {
-               indiv_bits = DA7219_PLL_INDIV_20_40_MHZ;
-               indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 4500000) {
+               indiv_bits = DA7219_PLL_INDIV_2_TO_4_5_MHZ;
+               indiv = DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 9000000) {
+               indiv_bits = DA7219_PLL_INDIV_4_5_TO_9_MHZ;
+               indiv = DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 18000000) {
+               indiv_bits = DA7219_PLL_INDIV_9_TO_18_MHZ;
+               indiv = DA7219_PLL_INDIV_9_TO_18_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 36000000) {
+               indiv_bits = DA7219_PLL_INDIV_18_TO_36_MHZ;
+               indiv = DA7219_PLL_INDIV_18_TO_36_MHZ_VAL;
        } else if (da7219->mclk_rate <= 54000000) {
-               indiv_bits = DA7219_PLL_INDIV_40_54_MHZ;
-               indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL;
+               indiv_bits = DA7219_PLL_INDIV_36_TO_54_MHZ;
+               indiv = DA7219_PLL_INDIV_36_TO_54_MHZ_VAL;
        } else {
                dev_err(codec->dev, "PLL input clock %d above valid range\n",
                        da7219->mclk_rate);
@@ -1426,6 +1427,12 @@ static const struct of_device_id da7219_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, da7219_of_match);
 
+static const struct acpi_device_id da7219_acpi_match[] = {
+       { .id = "DLGS7219", },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
+
 static enum da7219_micbias_voltage
        da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
 {
@@ -1955,6 +1962,7 @@ static struct i2c_driver da7219_i2c_driver = {
        .driver = {
                .name = "da7219",
                .of_match_table = of_match_ptr(da7219_of_match),
+               .acpi_match_table = ACPI_PTR(da7219_acpi_match),
        },
        .probe          = da7219_i2c_probe,
        .remove         = da7219_i2c_remove,
index 5a787e738084194e3cdd88a8f75eb034fd2ab032..ff2a2f02ce4049059218b35cc3beb0bddaf6e685 100644 (file)
 /* DA7219_PLL_CTRL = 0x20 */
 #define DA7219_PLL_INDIV_SHIFT         2
 #define DA7219_PLL_INDIV_MASK          (0x7 << 2)
-#define DA7219_PLL_INDIV_2_5_MHZ       (0x0 << 2)
-#define DA7219_PLL_INDIV_5_10_MHZ      (0x1 << 2)
-#define DA7219_PLL_INDIV_10_20_MHZ     (0x2 << 2)
-#define DA7219_PLL_INDIV_20_40_MHZ     (0x3 << 2)
-#define DA7219_PLL_INDIV_40_54_MHZ     (0x4 << 2)
+#define DA7219_PLL_INDIV_2_TO_4_5_MHZ  (0x0 << 2)
+#define DA7219_PLL_INDIV_4_5_TO_9_MHZ  (0x1 << 2)
+#define DA7219_PLL_INDIV_9_TO_18_MHZ   (0x2 << 2)
+#define DA7219_PLL_INDIV_18_TO_36_MHZ  (0x3 << 2)
+#define DA7219_PLL_INDIV_36_TO_54_MHZ  (0x4 << 2)
 #define DA7219_PLL_MCLK_SQR_EN_SHIFT   5
 #define DA7219_PLL_MCLK_SQR_EN_MASK    (0x1 << 5)
 #define DA7219_PLL_MODE_SHIFT          6
 #define DA7219_PLL_FREQ_OUT_98304      98304000
 
 /* PLL Frequency Dividers */
-#define DA7219_PLL_INDIV_2_5_MHZ_VAL   1
-#define DA7219_PLL_INDIV_5_10_MHZ_VAL  2
-#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4
-#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8
-#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16
+#define DA7219_PLL_INDIV_2_TO_4_5_MHZ_VAL      1
+#define DA7219_PLL_INDIV_4_5_TO_9_MHZ_VAL      2
+#define DA7219_PLL_INDIV_9_TO_18_MHZ_VAL       4
+#define DA7219_PLL_INDIV_18_TO_36_MHZ_VAL      8
+#define DA7219_PLL_INDIV_36_TO_54_MHZ_VAL      16
 
 /* SRM */
 #define DA7219_SRM_CHECK_RETRIES       8
index afa6c5db9dccc9b00f049f8421b04e3091a1bcfc..2086d7107622d17ac3d750591f91c58db4993d8d 100644 (file)
 #include <sound/tlv.h>
 #include "es8328.h"
 
-#define ES8328_SYSCLK_RATE_1X 11289600
-#define ES8328_SYSCLK_RATE_2X 22579200
+static const unsigned int rates_12288[] = {
+       8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
 
-/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
-static struct {
-       int rate;
-       u8 ratio;
-} mclk_ratios[] = {
-       { 8000, 9 },
-       {11025, 7 },
-       {22050, 4 },
-       {44100, 2 },
+static const int ratios_12288[] = {
+       10, 7, 6, 4, 3, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_12288 = {
+       .count  = ARRAY_SIZE(rates_12288),
+       .list   = rates_12288,
+};
+
+static const unsigned int rates_11289[] = {
+       8018, 11025, 22050, 44100, 88200,
+};
+
+static const int ratios_11289[] = {
+       9, 7, 4, 2, 0,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_11289 = {
+       .count  = ARRAY_SIZE(rates_11289),
+       .list   = rates_11289,
 };
 
 /* regulator supplies for sgtl5000, VDDD is an optional external supply */
@@ -57,16 +69,28 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
        "HPVDD",
 };
 
-#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
+#define ES8328_RATES (SNDRV_PCM_RATE_96000 | \
+               SNDRV_PCM_RATE_48000 | \
+               SNDRV_PCM_RATE_44100 | \
+               SNDRV_PCM_RATE_32000 | \
                SNDRV_PCM_RATE_22050 | \
-               SNDRV_PCM_RATE_11025)
-#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+               SNDRV_PCM_RATE_16000 | \
+               SNDRV_PCM_RATE_11025 | \
+               SNDRV_PCM_RATE_8000)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+               SNDRV_PCM_FMTBIT_S18_3LE | \
+               SNDRV_PCM_FMTBIT_S20_3LE | \
+               SNDRV_PCM_FMTBIT_S24_LE | \
+               SNDRV_PCM_FMTBIT_S32_LE)
 
 struct es8328_priv {
        struct regmap *regmap;
        struct clk *clk;
        int playback_fs;
        bool deemph;
+       int mclkdiv2;
+       const struct snd_pcm_hw_constraint_list *sysclk_constraints;
+       const int *mclk_ratios;
        struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
 };
 
@@ -439,54 +463,131 @@ static int es8328_mute(struct snd_soc_dai *dai, int mute)
                        mute ? ES8328_DACCONTROL3_DACMUTE : 0);
 }
 
+static int es8328_startup(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+
+       if (es8328->sysclk_constraints)
+               snd_pcm_hw_constraint_list(substream->runtime, 0,
+                               SNDRV_PCM_HW_PARAM_RATE,
+                               es8328->sysclk_constraints);
+
+       return 0;
+}
+
 static int es8328_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 es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
-       int clk_rate;
        int i;
        int reg;
-       u8 ratio;
+       int wl;
+       int ratio;
+
+       if (!es8328->sysclk_constraints) {
+               dev_err(codec->dev, "No MCLK configured\n");
+               return -EINVAL;
+       }
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                reg = ES8328_DACCONTROL2;
        else
                reg = ES8328_ADCCONTROL5;
 
-       clk_rate = clk_get_rate(es8328->clk);
+       for (i = 0; i < es8328->sysclk_constraints->count; i++)
+               if (es8328->sysclk_constraints->list[i] == params_rate(params))
+                       break;
 
-       if ((clk_rate != ES8328_SYSCLK_RATE_1X) &&
-               (clk_rate != ES8328_SYSCLK_RATE_2X)) {
-               dev_err(codec->dev,
-                       "%s: clock is running at %d Hz, not %d or %d Hz\n",
-                        __func__, clk_rate,
-                        ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
+       if (i == es8328->sysclk_constraints->count) {
+               dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
+                       params_rate(params));
                return -EINVAL;
        }
 
-       /* find master mode MCLK to sampling frequency ratio */
-       ratio = mclk_ratios[0].rate;
-       for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
-               if (params_rate(params) <= mclk_ratios[i].rate)
-                       ratio = mclk_ratios[i].ratio;
+       ratio = es8328->mclk_ratios[i];
+       snd_soc_update_bits(codec, ES8328_MASTERMODE,
+                       ES8328_MASTERMODE_MCLKDIV2,
+                       es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
+
+       switch (params_width(params)) {
+       case 16:
+               wl = 3;
+               break;
+       case 18:
+               wl = 2;
+               break;
+       case 20:
+               wl = 1;
+               break;
+       case 24:
+               wl = 0;
+               break;
+       case 32:
+               wl = 4;
+               break;
+       default:
+               return -EINVAL;
+       }
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               snd_soc_update_bits(codec, ES8328_DACCONTROL1,
+                               ES8328_DACCONTROL1_DACWL_MASK,
+                               wl << ES8328_DACCONTROL1_DACWL_SHIFT);
+
                es8328->playback_fs = params_rate(params);
                es8328_set_deemph(codec);
-       }
+       } else
+               snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
+                               ES8328_ADCCONTROL4_ADCWL_MASK,
+                               wl << ES8328_ADCCONTROL4_ADCWL_SHIFT);
 
        return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
 }
 
+static int es8328_set_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+       int mclkdiv2 = 0;
+
+       switch (freq) {
+       case 0:
+               es8328->sysclk_constraints = NULL;
+               es8328->mclk_ratios = NULL;
+               break;
+       case 22579200:
+               mclkdiv2 = 1;
+               /* fallthru */
+       case 11289600:
+               es8328->sysclk_constraints = &constraints_11289;
+               es8328->mclk_ratios = ratios_11289;
+               break;
+       case 24576000:
+               mclkdiv2 = 1;
+               /* fallthru */
+       case 12288000:
+               es8328->sysclk_constraints = &constraints_12288;
+               es8328->mclk_ratios = ratios_12288;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       es8328->mclkdiv2 = mclkdiv2;
+       return 0;
+}
+
 static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
                unsigned int fmt)
 {
        struct snd_soc_codec *codec = codec_dai->codec;
-       struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
-       int clk_rate;
-       u8 mode = ES8328_DACCONTROL1_DACWL_16;
+       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)
@@ -495,13 +596,16 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
        /* interface format */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_I2S:
-               mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+               dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+               adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
                break;
        case SND_SOC_DAIFMT_RIGHT_J:
-               mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+               dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+               adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
                break;
        case SND_SOC_DAIFMT_LEFT_J:
-               mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+               dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+               adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
                break;
        default:
                return -EINVAL;
@@ -511,18 +615,14 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
        if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
                return -EINVAL;
 
-       snd_soc_write(codec, ES8328_DACCONTROL1, mode);
-       snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
+       snd_soc_update_bits(codec, ES8328_DACCONTROL1,
+                       ES8328_DACCONTROL1_DACFORMAT_MASK, dac_mode);
+       snd_soc_update_bits(codec, ES8328_ADCCONTROL4,
+                       ES8328_ADCCONTROL4_ADCFORMAT_MASK, adc_mode);
 
        /* Master serial port mode, with BCLK generated automatically */
-       clk_rate = clk_get_rate(es8328->clk);
-       if (clk_rate == ES8328_SYSCLK_RATE_1X)
-               snd_soc_write(codec, ES8328_MASTERMODE,
-                               ES8328_MASTERMODE_MSC);
-       else
-               snd_soc_write(codec, ES8328_MASTERMODE,
-                               ES8328_MASTERMODE_MCLKDIV2 |
-                               ES8328_MASTERMODE_MSC);
+       snd_soc_update_bits(codec, ES8328_MASTERMODE,
+                       ES8328_MASTERMODE_MSC, ES8328_MASTERMODE_MSC);
 
        return 0;
 }
@@ -579,8 +679,10 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
 }
 
 static const struct snd_soc_dai_ops es8328_dai_ops = {
+       .startup        = es8328_startup,
        .hw_params      = es8328_hw_params,
        .digital_mute   = es8328_mute,
+       .set_sysclk     = es8328_set_sysclk,
        .set_fmt        = es8328_set_dai_fmt,
 };
 
@@ -601,6 +703,7 @@ static struct snd_soc_dai_driver es8328_dai = {
                .formats = ES8328_FORMATS,
        },
        .ops = &es8328_dai_ops,
+       .symmetric_rates = 1,
 };
 
 static int es8328_suspend(struct snd_soc_codec *codec)
@@ -708,6 +811,7 @@ const struct regmap_config es8328_regmap_config = {
        .val_bits       = 8,
        .max_register   = ES8328_REG_MAX,
        .cache_type     = REGCACHE_RBTREE,
+       .use_single_rw  = true,
 };
 EXPORT_SYMBOL_GPL(es8328_regmap_config);
 
index 156c748c89c7e55d86421b27522b22f9b16d6f76..1a736e72a929999d3117a154cf8bc99188f2332e 100644 (file)
@@ -22,7 +22,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
 #define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
 #define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
 #define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
-#define ES8328_CONTROL1_VMIDSEL_MASK (7 << 0)
+#define ES8328_CONTROL1_VMIDSEL_MASK (3 << 0)
 #define ES8328_CONTROL1_ENREF (1 << 2)
 #define ES8328_CONTROL1_SEQEN (1 << 3)
 #define ES8328_CONTROL1_SAMEFS (1 << 4)
@@ -84,7 +84,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
 #define ES8328_ADCCONTROL1     0x09
 #define ES8328_ADCCONTROL2     0x0a
 #define ES8328_ADCCONTROL3     0x0b
+
 #define ES8328_ADCCONTROL4     0x0c
+#define ES8328_ADCCONTROL4_ADCFORMAT_MASK (3 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_I2S (0 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_LJUST (1 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_RJUST (2 << 0)
+#define ES8328_ADCCONTROL4_ADCFORMAT_PCM (3 << 0)
+#define ES8328_ADCCONTROL4_ADCWL_SHIFT 2
+#define ES8328_ADCCONTROL4_ADCWL_MASK (7 << 2)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_NORMAL (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_I2S_POL_INV (1 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK2 (0 << 5)
+#define ES8328_ADCCONTROL4_ADCLRP_PCM_MSB_CLK1 (1 << 5)
+
 #define ES8328_ADCCONTROL5     0x0d
 #define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
 
@@ -109,15 +122,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
 #define ES8328_ADCCONTROL14    0x16
 
 #define ES8328_DACCONTROL1     0x17
+#define ES8328_DACCONTROL1_DACFORMAT_MASK (3 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
 #define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
-#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
-#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
-#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
-#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
-#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
+#define ES8328_DACCONTROL1_DACWL_SHIFT 3
+#define ES8328_DACCONTROL1_DACWL_MASK (7 << 3)
 #define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
 #define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
 #define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
index aaa038ffc8a50173a500483b307a76ac2b58b09c..181cd3bf0b926c750257eee7351558405e2a7e30 100644 (file)
@@ -29,6 +29,7 @@
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_i915.h>
 #include <sound/pcm_drm_eld.h>
+#include <sound/hda_chmap.h>
 #include "../../hda/local.h"
 #include "hdac_hdmi.h"
 
@@ -60,11 +61,17 @@ struct hdac_hdmi_cvt {
        struct hdac_hdmi_cvt_params params;
 };
 
+/* Currently only spk_alloc, more to be added */
+struct hdac_hdmi_parsed_eld {
+       u8 spk_alloc;
+};
+
 struct hdac_hdmi_eld {
        bool    monitor_present;
        bool    eld_valid;
        int     eld_size;
        char    eld_buffer[ELD_MAX_SIZE];
+       struct  hdac_hdmi_parsed_eld info;
 };
 
 struct hdac_hdmi_pin {
@@ -76,6 +83,10 @@ struct hdac_hdmi_pin {
        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 */
 };
 
 struct hdac_hdmi_pcm {
@@ -100,8 +111,22 @@ struct hdac_hdmi_priv {
        int num_pin;
        int num_cvt;
        struct mutex pin_mutex;
+       struct hdac_chmap chmap;
 };
 
+static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
+                                               int pcm_idx)
+{
+       struct hdac_hdmi_pcm *pcm;
+
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->pcm_id == pcm_idx)
+                       return pcm;
+       }
+
+       return NULL;
+}
+
 static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
 {
        struct hdac_device *hdac = dev_to_hdac_dev(dev);
@@ -278,26 +303,31 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
        int i;
        const u8 *eld_buf;
        u8 conn_type;
-       int channels = 2;
+       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);
+
+       channels = snd_hdac_get_active_channels(ca);
+       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);
+
        eld_buf = pin->eld.eld_buffer;
        conn_type = drm_eld_get_conn_type(eld_buf);
 
-       /* setup channel count */
-       snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-                           AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
-
        switch (conn_type) {
        case DRM_ELD_CONN_TYPE_HDMI:
                hdmi_audio_infoframe_init(&frame);
 
-               /* Default stereo for now */
                frame.channels = channels;
+               frame.channel_allocation = ca;
 
                ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
                if (ret < 0)
@@ -311,7 +341,7 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
                dp_ai.len       = 0x1b;
                dp_ai.ver       = 0x11 << 2;
                dp_ai.CC02_CT47 = channels - 1;
-               dp_ai.CA        = 0;
+               dp_ai.CA        = ca;
 
                dip = (u8 *)&dp_ai;
                break;
@@ -370,17 +400,23 @@ static int hdac_hdmi_playback_prepare(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;
        int ret;
 
        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;
+
        ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
                                                dai_map->pin->nid);
+       mutex_unlock(&pin->lock);
        if (ret < 0)
                return ret;
 
@@ -640,6 +676,12 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
                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);
+
                dai_map->pin = NULL;
        }
 }
@@ -647,10 +689,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
 static int
 hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
 {
+       unsigned int chans;
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
        int err;
 
-       /* Only stereo supported as of now */
-       cvt->params.channels_min = cvt->params.channels_max = 2;
+       chans = get_wcaps(hdac, cvt->nid);
+       chans = get_wcaps_channels(chans);
+
+       cvt->params.channels_min = 2;
+
+       cvt->params.channels_max = chans;
+       if (chans > hdmi->chmap.channels_max)
+               hdmi->chmap.channels_max = chans;
 
        err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
                        &cvt->params.rates,
@@ -1008,6 +1059,12 @@ 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)
+{
+       pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER];
+}
+
 static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 {
        struct hdac_ext_device *edev = pin->edev;
@@ -1065,6 +1122,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
 
                                snd_jack_report(pcm->jack, SND_JACK_AVOUT);
                        }
+                       hdac_hdmi_parse_eld(edev, pin);
 
                        print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
                                        pin->eld.eld_buffer, pin->eld.eld_size);
@@ -1123,6 +1181,7 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
        hdmi->num_pin++;
 
        pin->edev = edev;
+       mutex_init(&pin->lock);
        INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
 
        return 0;
@@ -1342,6 +1401,19 @@ static struct i915_audio_component_audio_ops aops = {
        .pin_eld_notify = hdac_hdmi_eld_notify_cb,
 };
 
+static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
+                                               int device)
+{
+       struct snd_soc_pcm_runtime *rtd;
+
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (rtd->pcm && (rtd->pcm->device == device))
+                       return rtd->pcm;
+       }
+
+       return NULL;
+}
+
 int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
 {
        char jack_name[NAME_SIZE];
@@ -1351,6 +1423,8 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
                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;
+       int err;
 
        /*
         * this is a new PCM device, create new pcm and
@@ -1362,6 +1436,18 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
        pcm->pcm_id = device;
        pcm->cvt = hdmi->dai_map[dai->id].cvt;
 
+       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);
+               if (err < 0) {
+                       dev_err(&edev->hdac.dev,
+                               "chmap control add failed with err: %d for pcm: %d\n",
+                               err, device);
+                       kfree(pcm);
+                       return err;
+               }
+       }
+
        list_add_tail(&pcm->head, &hdmi->pcm_list);
 
        sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
@@ -1378,10 +1464,18 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
        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;
 
        edev->scodec = codec;
 
+       /*
+        * hold the ref while we probe, also no need to drop the ref on
+        * exit, we call pm_runtime_suspend() so that will do for us
+        */
+       hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+       snd_hdac_ext_bus_link_get(edev->ebus, hlink);
+
        ret = create_fill_widget_route_map(dapm);
        if (ret < 0)
                return ret;
@@ -1475,19 +1569,83 @@ static struct snd_soc_codec_driver hdmi_hda_codec = {
        .idle_bias_off  = true,
 };
 
+static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap)
+{
+       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));
+}
+
+static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+                               unsigned char *chmap, int prepared)
+{
+       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);
+}
+
+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;
+}
+
+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;
+
+       if (!pin || !pin->eld.eld_valid)
+               return 0;
+
+       return pin->eld.info.spk_alloc;
+}
+
 static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 {
        struct hdac_device *codec = &edev->hdac;
        struct hdac_hdmi_priv *hdmi_priv;
        struct snd_soc_dai_driver *hdmi_dais = NULL;
+       struct hdac_ext_link *hlink = NULL;
        int num_dais = 0;
        int ret = 0;
 
+       /* hold the ref while we probe */
+       hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+       snd_hdac_ext_bus_link_get(edev->ebus, hlink);
+
        hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
        if (hdmi_priv == NULL)
                return -ENOMEM;
 
        edev->private_data = hdmi_priv;
+       snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap);
+       hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
+       hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
+       hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
+       hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
 
        dev_set_drvdata(&codec->dev, edev);
 
@@ -1516,8 +1674,12 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
        }
 
        /* ASoC specific initialization */
-       return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
-                       hdmi_dais, num_dais);
+       ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
+                                       hdmi_dais, num_dais);
+
+       snd_hdac_ext_bus_link_put(edev->ebus, hlink);
+
+       return ret;
 }
 
 static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
@@ -1556,6 +1718,8 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
        struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_device *hdac = &edev->hdac;
        struct hdac_bus *bus = hdac->bus;
+       struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
+       struct hdac_ext_link *hlink = NULL;
        int err;
 
        dev_dbg(dev, "Enter: %s\n", __func__);
@@ -1579,6 +1743,9 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
                return err;
        }
 
+       hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+       snd_hdac_ext_bus_link_put(ebus, hlink);
+
        return 0;
 }
 
@@ -1587,6 +1754,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
        struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_device *hdac = &edev->hdac;
        struct hdac_bus *bus = hdac->bus;
+       struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
+       struct hdac_ext_link *hlink = NULL;
        int err;
 
        dev_dbg(dev, "Enter: %s\n", __func__);
@@ -1595,6 +1764,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
        if (!bus)
                return 0;
 
+       hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+       snd_hdac_ext_bus_link_get(ebus, hlink);
+
        err = snd_hdac_display_power(bus, true);
        if (err < 0) {
                dev_err(bus->dev, "Cannot turn on display power on i915\n");
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
new file mode 100644 (file)
index 0000000..8e36e88
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * ALSA SoC codec for HDMI encoder drivers
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Jyri Sarha <jsarha@ti.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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/hdmi-codec.h>
+#include <sound/pcm_iec958.h>
+
+#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
+
+struct hdmi_codec_priv {
+       struct hdmi_codec_pdata hcd;
+       struct snd_soc_dai_driver *daidrv;
+       struct hdmi_codec_daifmt daifmt[2];
+       struct mutex current_stream_lock;
+       struct snd_pcm_substream *current_stream;
+       struct snd_pcm_hw_constraint_list ratec;
+       uint8_t eld[MAX_ELD_BYTES];
+};
+
+static const struct snd_soc_dapm_widget hdmi_widgets[] = {
+       SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route hdmi_routes[] = {
+       { "TX", NULL, "Playback" },
+};
+
+enum {
+       DAI_ID_I2S = 0,
+       DAI_ID_SPDIF,
+};
+
+static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = sizeof(hcp->eld);
+
+       return 0;
+}
+
+static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
+
+       memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld));
+
+       return 0;
+}
+
+static const struct snd_kcontrol_new hdmi_controls[] = {
+       {
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                         SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .name = "ELD",
+               .info = hdmi_eld_ctl_info,
+               .get = hdmi_eld_ctl_get,
+       },
+};
+
+static int hdmi_codec_new_stream(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       int ret = 0;
+
+       mutex_lock(&hcp->current_stream_lock);
+       if (!hcp->current_stream) {
+               hcp->current_stream = substream;
+       } else if (hcp->current_stream != substream) {
+               dev_err(dai->dev, "Only one simultaneous stream supported!\n");
+               ret = -EINVAL;
+       }
+       mutex_unlock(&hcp->current_stream_lock);
+
+       return ret;
+}
+
+static int hdmi_codec_startup(struct snd_pcm_substream *substream,
+                             struct snd_soc_dai *dai)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       int ret = 0;
+
+       dev_dbg(dai->dev, "%s()\n", __func__);
+
+       ret = hdmi_codec_new_stream(substream, dai);
+       if (ret)
+               return ret;
+
+       if (hcp->hcd.ops->audio_startup) {
+               ret = hcp->hcd.ops->audio_startup(dai->dev->parent);
+               if (ret) {
+                       mutex_lock(&hcp->current_stream_lock);
+                       hcp->current_stream = NULL;
+                       mutex_unlock(&hcp->current_stream_lock);
+                       return ret;
+               }
+       }
+
+       if (hcp->hcd.ops->get_eld) {
+               ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld,
+                                           sizeof(hcp->eld));
+
+               if (!ret) {
+                       ret = snd_pcm_hw_constraint_eld(substream->runtime,
+                                                       hcp->eld);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
+static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(dai->dev, "%s()\n", __func__);
+
+       WARN_ON(hcp->current_stream != substream);
+
+       hcp->hcd.ops->audio_shutdown(dai->dev->parent);
+
+       mutex_lock(&hcp->current_stream_lock);
+       hcp->current_stream = NULL;
+       mutex_unlock(&hcp->current_stream_lock);
+}
+
+static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       struct hdmi_codec_params hp = {
+               .iec = {
+                       .status = { 0 },
+                       .subcode = { 0 },
+                       .pad = 0,
+                       .dig_subframe = { 0 },
+               }
+       };
+       int ret;
+
+       dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
+               params_width(params), params_rate(params),
+               params_channels(params));
+
+       if (params_width(params) > 24)
+               params->msbits = 24;
+
+       ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
+                                                      sizeof(hp.iec.status));
+       if (ret < 0) {
+               dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = hdmi_codec_new_stream(substream, dai);
+       if (ret)
+               return ret;
+
+       hdmi_audio_infoframe_init(&hp.cea);
+       hp.cea.channels = params_channels(params);
+       hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+       hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+       hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+
+       hp.sample_width = params_width(params);
+       hp.sample_rate = params_rate(params);
+       hp.channels = params_channels(params);
+
+       return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id],
+                                      &hp);
+}
+
+static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
+                             unsigned int fmt)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+       struct hdmi_codec_daifmt cf = { 0 };
+       int ret = 0;
+
+       dev_dbg(dai->dev, "%s()\n", __func__);
+
+       if (dai->id == DAI_ID_SPDIF) {
+               cf.fmt = HDMI_SPDIF;
+       } else {
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       cf.bit_clk_master = 1;
+                       cf.frame_clk_master = 1;
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+                       cf.frame_clk_master = 1;
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFS:
+                       cf.bit_clk_master = 1;
+                       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_NB_IF:
+                       cf.frame_clk_inv = 1;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       cf.bit_clk_inv = 1;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       cf.frame_clk_inv = 1;
+                       cf.bit_clk_inv = 1;
+                       break;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       cf.fmt = HDMI_I2S;
+                       break;
+               case SND_SOC_DAIFMT_DSP_A:
+                       cf.fmt = HDMI_DSP_A;
+                       break;
+               case SND_SOC_DAIFMT_DSP_B:
+                       cf.fmt = HDMI_DSP_B;
+                       break;
+               case SND_SOC_DAIFMT_RIGHT_J:
+                       cf.fmt = HDMI_RIGHT_J;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       cf.fmt = HDMI_LEFT_J;
+                       break;
+               case SND_SOC_DAIFMT_AC97:
+                       cf.fmt = HDMI_AC97;
+                       break;
+               default:
+                       dev_err(dai->dev, "Invalid DAI interface format\n");
+                       return -EINVAL;
+               }
+       }
+
+       hcp->daifmt[dai->id] = cf;
+
+       return ret;
+}
+
+static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(dai->dev, "%s()\n", __func__);
+
+       if (hcp->hcd.ops->digital_mute)
+               return hcp->hcd.ops->digital_mute(dai->dev->parent, mute);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops hdmi_dai_ops = {
+       .startup        = hdmi_codec_startup,
+       .shutdown       = hdmi_codec_shutdown,
+       .hw_params      = hdmi_codec_hw_params,
+       .set_fmt        = hdmi_codec_set_fmt,
+       .digital_mute   = hdmi_codec_digital_mute,
+};
+
+
+#define HDMI_RATES     (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+                        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+                        SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+                        SNDRV_PCM_RATE_192000)
+
+#define SPDIF_FORMATS  (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
+                        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
+                        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
+                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+
+/*
+ * This list is only for formats allowed on the I2S bus. So there is
+ * some formats listed that are not supported by HDMI interface. For
+ * instance allowing the 32-bit formats enables 24-precision with CPU
+ * DAIs that do not support 24-bit formats. If the extra formats cause
+ * problems, we should add the video side driver an option to disable
+ * them.
+ */
+#define I2S_FORMATS    (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
+                        SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
+                        SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
+                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
+                        SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+
+static struct snd_soc_dai_driver hdmi_i2s_dai = {
+       .name = "i2s-hifi",
+       .id = DAI_ID_I2S,
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = HDMI_RATES,
+               .formats = I2S_FORMATS,
+               .sig_bits = 24,
+       },
+       .ops = &hdmi_dai_ops,
+};
+
+static const struct snd_soc_dai_driver hdmi_spdif_dai = {
+       .name = "spdif-hifi",
+       .id = DAI_ID_SPDIF,
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = HDMI_RATES,
+               .formats = SPDIF_FORMATS,
+       },
+       .ops = &hdmi_dai_ops,
+};
+
+static struct snd_soc_codec_driver hdmi_codec = {
+       .controls = hdmi_controls,
+       .num_controls = ARRAY_SIZE(hdmi_controls),
+       .dapm_widgets = hdmi_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
+       .dapm_routes = hdmi_routes,
+       .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+};
+
+static int hdmi_codec_probe(struct platform_device *pdev)
+{
+       struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
+       struct device *dev = &pdev->dev;
+       struct hdmi_codec_priv *hcp;
+       int dai_count, i = 0;
+       int ret;
+
+       dev_dbg(dev, "%s()\n", __func__);
+
+       if (!hcd) {
+               dev_err(dev, "%s: No plalform data\n", __func__);
+               return -EINVAL;
+       }
+
+       dai_count = hcd->i2s + hcd->spdif;
+       if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
+           !hcd->ops->audio_shutdown) {
+               dev_err(dev, "%s: Invalid parameters\n", __func__);
+               return -EINVAL;
+       }
+
+       hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL);
+       if (!hcp)
+               return -ENOMEM;
+
+       hcp->hcd = *hcd;
+       mutex_init(&hcp->current_stream_lock);
+
+       hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
+                                  GFP_KERNEL);
+       if (!hcp->daidrv)
+               return -ENOMEM;
+
+       if (hcd->i2s) {
+               hcp->daidrv[i] = hdmi_i2s_dai;
+               hcp->daidrv[i].playback.channels_max =
+                       hcd->max_i2s_channels;
+               i++;
+       }
+
+       if (hcd->spdif)
+               hcp->daidrv[i] = hdmi_spdif_dai;
+
+       ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
+                                    dai_count);
+       if (ret) {
+               dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       dev_set_drvdata(dev, hcp);
+       return 0;
+}
+
+static int hdmi_codec_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static struct platform_driver hdmi_codec_driver = {
+       .driver = {
+               .name = HDMI_CODEC_DRV_NAME,
+       },
+       .probe = hdmi_codec_probe,
+       .remove = hdmi_codec_remove,
+};
+
+module_platform_driver(hdmi_codec_driver);
+
+MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
+MODULE_DESCRIPTION("HDMI Audio Codec Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
new file mode 100644 (file)
index 0000000..ed51567
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Driver for the PCM5102A codec
+ *
+ * Author:     Florian Meier <florian.meier@koalo.de>
+ *             Copyright 2013
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver pcm5102a_dai = {
+       .name = "pcm5102a-hifi",
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                          SNDRV_PCM_FMTBIT_S24_LE |
+                          SNDRV_PCM_FMTBIT_S32_LE
+       },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm5102a;
+
+static int pcm5102a_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a,
+                       &pcm5102a_dai, 1);
+}
+
+static int pcm5102a_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static const struct of_device_id pcm5102a_of_match[] = {
+       { .compatible = "ti,pcm5102a", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pcm5102a_of_match);
+
+static struct platform_driver pcm5102a_codec_driver = {
+       .probe          = pcm5102a_probe,
+       .remove         = pcm5102a_remove,
+       .driver         = {
+               .name   = "pcm5102a-codec",
+               .owner  = THIS_MODULE,
+               .of_match_table = pcm5102a_of_match,
+       },
+};
+
+module_platform_driver(pcm5102a_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM5102A codec driver");
+MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_LICENSE("GPL v2");
index f0e6c06e89ac0448afd7580245353b12acbb3d4f..a1aaffc2086272afa932d8137522c383f4ac1377 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -1132,6 +1133,17 @@ static const struct acpi_device_id rt298_acpi_match[] = {
 };
 MODULE_DEVICE_TABLE(acpi, rt298_acpi_match);
 
+static const struct dmi_system_id force_combo_jack_table[] = {
+       {
+               .ident = "Intel Broxton P",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Broxton P")
+               }
+       },
+       { }
+};
+
 static int rt298_i2c_probe(struct i2c_client *i2c,
                           const struct i2c_device_id *id)
 {
@@ -1184,11 +1196,16 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
 
        /* enable jack combo mode on supported devices */
        acpiid = acpi_match_device(dev->driver->acpi_match_table, dev);
-       if (acpiid) {
+       if (acpiid && acpiid->driver_data) {
                rt298->pdata = *(struct rt298_platform_data *)
                                acpiid->driver_data;
        }
 
+       if (dmi_check_system(force_combo_jack_table)) {
+               rt298->pdata.cbj_en = true;
+               rt298->pdata.gpio2_en = false;
+       }
+
        /* VREF Charging */
        regmap_update_bits(rt298->regmap, 0x04, 0x80, 0x80);
        regmap_update_bits(rt298->regmap, 0x1b, 0x860, 0x860);
index 7af5e7380d61e45658feb4ba190b2e2a7cd8efec..3c6594da6c9c95c8abb198706cac920bae3959da 100644 (file)
@@ -3286,10 +3286,8 @@ static void rt5645_jack_detect_work(struct work_struct *work)
                if (btn_type == 0)/* button release */
                        report =  rt5645->jack_type;
                else {
-                       if (rt5645->pdata.jd_invert) {
-                               mod_timer(&rt5645->btn_check_timer,
-                                       msecs_to_jiffies(100));
-                       }
+                       mod_timer(&rt5645->btn_check_timer,
+                               msecs_to_jiffies(100));
                }
 
                break;
@@ -3557,6 +3555,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
                        DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
                },
        },
+       {
+               .ident = "Google Setzer",
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
+               },
+       },
        { }
 };
 
@@ -3810,9 +3814,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
        if (rt5645->pdata.jd_invert) {
                regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
                        RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
-               setup_timer(&rt5645->btn_check_timer,
-                       rt5645_btn_check_callback, (unsigned long)rt5645);
        }
+       setup_timer(&rt5645->btn_check_timer,
+               rt5645_btn_check_callback, (unsigned long)rt5645);
 
        INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
        INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work);
index 1bae17ee88175ed2556cebe4bf3f554bf721acab..da60e3fe5ee7afb37740b750e0f15f6156d6a4eb 100644 (file)
@@ -2098,10 +2098,14 @@ static int wm5102_probe(struct platform_device *pdev)
 
 static int wm5102_remove(struct platform_device *pdev)
 {
+       struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
+
        snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
+       wm_adsp2_remove(&wm5102->core.adsp[0]);
+
        return 0;
 }
 
index 2728ac545ffe64463a44a2f4b2b70df52df7b9e8..b5820e4d547170a4a02ce0a46b09a3712ea42ce1 100644 (file)
@@ -2437,10 +2437,16 @@ static int wm5110_probe(struct platform_device *pdev)
 
 static int wm5110_remove(struct platform_device *pdev)
 {
+       struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
+       int i;
+
        snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
+       for (i = 0; i < WM5110_NUM_ADSP; i++)
+               wm_adsp2_remove(&wm5110->core.adsp[i]);
+
        return 0;
 }
 
index d3b1cb15e7f0766111b6ced6fbe36d65571703aa..a07bd7c2c587dd9d9d0135015b1aa4b578307e93 100644 (file)
 #define ADSP2_RAM_RDY_SHIFT                    0
 #define ADSP2_RAM_RDY_WIDTH                    1
 
+#define ADSP_MAX_STD_CTRL_SIZE               512
+
 struct wm_adsp_buf {
        struct list_head list;
        void *buf;
@@ -271,8 +273,11 @@ struct wm_adsp_buffer {
        __be32 words_written[2];        /* total words written (64 bit) */
 };
 
+struct wm_adsp_compr;
+
 struct wm_adsp_compr_buf {
        struct wm_adsp *dsp;
+       struct wm_adsp_compr *compr;
 
        struct wm_adsp_buffer_region *regions;
        u32 host_buf_ptr;
@@ -435,6 +440,7 @@ struct wm_coeff_ctl {
        size_t len;
        unsigned int set:1;
        struct snd_kcontrol *kcontrol;
+       struct soc_bytes_ext bytes_ext;
        unsigned int flags;
 };
 
@@ -711,10 +717,17 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
                 be16_to_cpu(scratch[3]));
 }
 
+static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
+{
+       return container_of(ext, struct wm_coeff_ctl, bytes_ext);
+}
+
 static int wm_coeff_info(struct snd_kcontrol *kctl,
                         struct snd_ctl_elem_info *uinfo)
 {
-       struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
        uinfo->count = ctl->len;
@@ -763,7 +776,9 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
 static int wm_coeff_put(struct snd_kcontrol *kctl,
                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
        char *p = ucontrol->value.bytes.data;
        int ret = 0;
 
@@ -780,6 +795,29 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
        return ret;
 }
 
+static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
+                           const unsigned int __user *bytes, unsigned int size)
+{
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+       int ret = 0;
+
+       mutex_lock(&ctl->dsp->pwr_lock);
+
+       if (copy_from_user(ctl->cache, bytes, size)) {
+               ret = -EFAULT;
+       } else {
+               ctl->set = 1;
+               if (ctl->enabled)
+                       ret = wm_coeff_write_control(ctl, ctl->cache, size);
+       }
+
+       mutex_unlock(&ctl->dsp->pwr_lock);
+
+       return ret;
+}
+
 static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
                                 void *buf, size_t len)
 {
@@ -822,7 +860,9 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
 static int wm_coeff_get(struct snd_kcontrol *kctl,
                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value;
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
        char *p = ucontrol->value.bytes.data;
        int ret = 0;
 
@@ -845,12 +885,72 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
        return ret;
 }
 
+static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
+                           unsigned int __user *bytes, unsigned int size)
+{
+       struct soc_bytes_ext *bytes_ext =
+               (struct soc_bytes_ext *)kctl->private_value;
+       struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+       int ret = 0;
+
+       mutex_lock(&ctl->dsp->pwr_lock);
+
+       if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
+               if (ctl->enabled)
+                       ret = wm_coeff_read_control(ctl, ctl->cache, size);
+               else
+                       ret = -EPERM;
+       } else {
+               if (!ctl->flags && ctl->enabled)
+                       ret = wm_coeff_read_control(ctl, ctl->cache, size);
+       }
+
+       if (!ret && copy_to_user(bytes, ctl->cache, size))
+               ret = -EFAULT;
+
+       mutex_unlock(&ctl->dsp->pwr_lock);
+
+       return ret;
+}
+
 struct wmfw_ctl_work {
        struct wm_adsp *dsp;
        struct wm_coeff_ctl *ctl;
        struct work_struct work;
 };
 
+static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)
+{
+       unsigned int out, rd, wr, vol;
+
+       if (len > ADSP_MAX_STD_CTRL_SIZE) {
+               rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;
+               vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+
+               out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+       } else {
+               rd = SNDRV_CTL_ELEM_ACCESS_READ;
+               wr = SNDRV_CTL_ELEM_ACCESS_WRITE;
+               vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+
+               out = 0;
+       }
+
+       if (in) {
+               if (in & WMFW_CTL_FLAG_READABLE)
+                       out |= rd;
+               if (in & WMFW_CTL_FLAG_WRITEABLE)
+                       out |= wr;
+               if (in & WMFW_CTL_FLAG_VOLATILE)
+                       out |= vol;
+       } else {
+               out |= rd | wr | vol;
+       }
+
+       return out;
+}
+
 static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
 {
        struct snd_kcontrol_new *kcontrol;
@@ -868,19 +968,15 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
        kcontrol->info = wm_coeff_info;
        kcontrol->get = wm_coeff_get;
        kcontrol->put = wm_coeff_put;
-       kcontrol->private_value = (unsigned long)ctl;
+       kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
+       kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
 
-       if (ctl->flags) {
-               if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
-                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
-               if (ctl->flags & WMFW_CTL_FLAG_READABLE)
-                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
-               if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
-                       kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
-       } else {
-               kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
-               kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
-       }
+       ctl->bytes_ext.max = ctl->len;
+       ctl->bytes_ext.get = wm_coeff_tlv_get;
+       ctl->bytes_ext.put = wm_coeff_tlv_put;
+
+       kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
 
        ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
        if (ret < 0)
@@ -944,6 +1040,13 @@ static void wm_adsp_ctl_work(struct work_struct *work)
        kfree(ctl_work);
 }
 
+static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
+{
+       kfree(ctl->cache);
+       kfree(ctl->name);
+       kfree(ctl);
+}
+
 static int wm_adsp_create_control(struct wm_adsp *dsp,
                                  const struct wm_adsp_alg_region *alg_region,
                                  unsigned int offset, unsigned int len,
@@ -1032,11 +1135,6 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
 
        ctl->flags = flags;
        ctl->offset = offset;
-       if (len > 512) {
-               adsp_warn(dsp, "Truncating control %s from %d\n",
-                         ctl->name, len);
-               len = 512;
-       }
        ctl->len = len;
        ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
        if (!ctl->cache) {
@@ -1564,6 +1662,19 @@ static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
        return alg_region;
 }
 
+static void wm_adsp_free_alg_regions(struct wm_adsp *dsp)
+{
+       struct wm_adsp_alg_region *alg_region;
+
+       while (!list_empty(&dsp->alg_regions)) {
+               alg_region = list_first_entry(&dsp->alg_regions,
+                                             struct wm_adsp_alg_region,
+                                             list);
+               list_del(&alg_region->list);
+               kfree(alg_region);
+       }
+}
+
 static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
 {
        struct wmfw_adsp1_id_hdr adsp1_id;
@@ -1994,7 +2105,6 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
-       struct wm_adsp_alg_region *alg_region;
        struct wm_coeff_ctl *ctl;
        int ret;
        unsigned int val;
@@ -2074,13 +2184,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                list_for_each_entry(ctl, &dsp->ctl_list, list)
                        ctl->enabled = 0;
 
-               while (!list_empty(&dsp->alg_regions)) {
-                       alg_region = list_first_entry(&dsp->alg_regions,
-                                                     struct wm_adsp_alg_region,
-                                                     list);
-                       list_del(&alg_region->list);
-                       kfree(alg_region);
-               }
+
+               wm_adsp_free_alg_regions(dsp);
                break;
 
        default:
@@ -2222,7 +2327,6 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
-       struct wm_adsp_alg_region *alg_region;
        struct wm_coeff_ctl *ctl;
        int ret;
 
@@ -2240,9 +2344,13 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                if (ret != 0)
                        goto err;
 
+               mutex_lock(&dsp->pwr_lock);
+
                if (wm_adsp_fw[dsp->fw].num_caps != 0)
                        ret = wm_adsp_buffer_init(dsp);
 
+               mutex_unlock(&dsp->pwr_lock);
+
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
@@ -2269,13 +2377,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                list_for_each_entry(ctl, &dsp->ctl_list, list)
                        ctl->enabled = 0;
 
-               while (!list_empty(&dsp->alg_regions)) {
-                       alg_region = list_first_entry(&dsp->alg_regions,
-                                                     struct wm_adsp_alg_region,
-                                                     list);
-                       list_del(&alg_region->list);
-                       kfree(alg_region);
-               }
+               wm_adsp_free_alg_regions(dsp);
 
                if (wm_adsp_fw[dsp->fw].num_caps != 0)
                        wm_adsp_buffer_free(dsp);
@@ -2340,6 +2442,54 @@ int wm_adsp2_init(struct wm_adsp *dsp)
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_init);
 
+void wm_adsp2_remove(struct wm_adsp *dsp)
+{
+       struct wm_coeff_ctl *ctl;
+
+       while (!list_empty(&dsp->ctl_list)) {
+               ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl,
+                                       list);
+               list_del(&ctl->list);
+               wm_adsp_free_ctl_blk(ctl);
+       }
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_remove);
+
+static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
+{
+       return compr->buf != NULL;
+}
+
+static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
+{
+       /*
+        * Note this will be more complex once each DSP can support multiple
+        * streams
+        */
+       if (!compr->dsp->buffer)
+               return -EINVAL;
+
+       compr->buf = compr->dsp->buffer;
+       compr->buf->compr = compr;
+
+       return 0;
+}
+
+static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)
+{
+       if (!compr)
+               return;
+
+       /* Wake the poll so it can see buffer is no longer attached */
+       if (compr->stream)
+               snd_compr_fragment_elapsed(compr->stream);
+
+       if (wm_adsp_compr_attached(compr)) {
+               compr->buf->compr = NULL;
+               compr->buf = NULL;
+       }
+}
+
 int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)
 {
        struct wm_adsp_compr *compr;
@@ -2393,6 +2543,7 @@ int wm_adsp_compr_free(struct snd_compr_stream *stream)
 
        mutex_lock(&dsp->pwr_lock);
 
+       wm_adsp_compr_detach(compr);
        dsp->compr = NULL;
 
        kfree(compr->raw_buf);
@@ -2689,6 +2840,8 @@ err_buffer:
 static int wm_adsp_buffer_free(struct wm_adsp *dsp)
 {
        if (dsp->buffer) {
+               wm_adsp_compr_detach(dsp->buffer->compr);
+
                kfree(dsp->buffer->regions);
                kfree(dsp->buffer);
 
@@ -2698,25 +2851,6 @@ static int wm_adsp_buffer_free(struct wm_adsp *dsp)
        return 0;
 }
 
-static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)
-{
-       return compr->buf != NULL;
-}
-
-static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)
-{
-       /*
-        * Note this will be more complex once each DSP can support multiple
-        * streams
-        */
-       if (!compr->dsp->buffer)
-               return -EINVAL;
-
-       compr->buf = compr->dsp->buffer;
-
-       return 0;
-}
-
 int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd)
 {
        struct wm_adsp_compr *compr = stream->runtime->private_data;
@@ -2805,21 +2939,41 @@ static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
                avail += wm_adsp_buffer_size(buf);
 
        adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
-                buf->read_index, write_index, avail);
+                buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE);
 
        buf->avail = avail;
 
        return 0;
 }
 
+static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)
+{
+       int ret;
+
+       ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
+       if (ret < 0) {
+               adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret);
+               return ret;
+       }
+       if (buf->error != 0) {
+               adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error);
+               return -EIO;
+       }
+
+       return 0;
+}
+
 int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
 {
-       struct wm_adsp_compr_buf *buf = dsp->buffer;
-       struct wm_adsp_compr *compr = dsp->compr;
+       struct wm_adsp_compr_buf *buf;
+       struct wm_adsp_compr *compr;
        int ret = 0;
 
        mutex_lock(&dsp->pwr_lock);
 
+       buf = dsp->buffer;
+       compr = dsp->compr;
+
        if (!buf) {
                ret = -ENODEV;
                goto out;
@@ -2827,16 +2981,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
 
        adsp_dbg(dsp, "Handling buffer IRQ\n");
 
-       ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
-       if (ret < 0) {
-               adsp_err(dsp, "Failed to check buffer error: %d\n", ret);
-               goto out;
-       }
-       if (buf->error != 0) {
-               adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
-               ret = -EIO;
-               goto out;
-       }
+       ret = wm_adsp_buffer_get_error(buf);
+       if (ret < 0)
+               goto out_notify; /* Wake poll to report error */
 
        ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
                                  &buf->irq_count);
@@ -2851,6 +2998,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
                goto out;
        }
 
+out_notify:
        if (compr && compr->stream)
                snd_compr_fragment_elapsed(compr->stream);
 
@@ -2879,14 +3027,16 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
                          struct snd_compr_tstamp *tstamp)
 {
        struct wm_adsp_compr *compr = stream->runtime->private_data;
-       struct wm_adsp_compr_buf *buf = compr->buf;
        struct wm_adsp *dsp = compr->dsp;
+       struct wm_adsp_compr_buf *buf;
        int ret = 0;
 
        adsp_dbg(dsp, "Pointer request\n");
 
        mutex_lock(&dsp->pwr_lock);
 
+       buf = compr->buf;
+
        if (!compr->buf) {
                ret = -ENXIO;
                goto out;
@@ -2909,6 +3059,10 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
                 * DSP to inform us once a whole fragment is available.
                 */
                if (buf->avail < wm_adsp_compr_frag_words(compr)) {
+                       ret = wm_adsp_buffer_get_error(buf);
+                       if (ret < 0)
+                               goto out;
+
                        ret = wm_adsp_buffer_reenable_irq(buf);
                        if (ret < 0) {
                                adsp_err(dsp,
index b61cb57e600fb6447798e5cf9684c51f641d094e..feb61e2c4bb46717831414bc81f6d0267630722c 100644 (file)
@@ -92,6 +92,7 @@ extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
 
 int wm_adsp1_init(struct wm_adsp *dsp);
 int wm_adsp2_init(struct wm_adsp *dsp);
+void wm_adsp2_remove(struct wm_adsp *dsp);
 int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec);
 int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
index 50ca291cc225321fb82090c31a5edae523ee9b0d..6b732d8e5896bc4b8c0f87213590e12d286c4bee 100644 (file)
@@ -16,7 +16,11 @@ config SND_EDMA_SOC
          - DRA7xx family
 
 config SND_DAVINCI_SOC_I2S
-       tristate
+       tristate "DaVinci Multichannel Buffered Serial Port (McBSP) support"
+       depends on SND_EDMA_SOC
+       help
+         Say Y or M here if you want to have support for McBSP IP found in
+         Texas Instruments DaVinci DA850 SoCs.
 
 config SND_DAVINCI_SOC_MCASP
        tristate "Multichannel Audio Serial Port (McASP) support"
index ec98548a5fc9282b9c0b47abd354f22d8d95cb38..3849616519048401a44e691db64945050b7a8d68 100644 (file)
@@ -4,9 +4,15 @@
  * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>
  * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>
  *
+ * DT support  (c) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
+ *             based on davinci-mcasp.c DT support
+ *
  * 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.
+ *
+ * TODO:
+ * on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers
  */
 
 #include <linux/init.h>
@@ -650,13 +656,24 @@ static const struct snd_soc_component_driver davinci_i2s_component = {
 
 static int davinci_i2s_probe(struct platform_device *pdev)
 {
+       struct snd_dmaengine_dai_dma_data *dma_data;
        struct davinci_mcbsp_dev *dev;
        struct resource *mem, *res;
        void __iomem *io_base;
        int *dma;
        int ret;
 
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+       if (!mem) {
+               dev_warn(&pdev->dev,
+                        "\"mpu\" mem resource not found, using index 0\n");
+               mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               if (!mem) {
+                       dev_err(&pdev->dev, "no mem resource?\n");
+                       return -ENODEV;
+               }
+       }
+
        io_base = devm_ioremap_resource(&pdev->dev, mem);
        if (IS_ERR(io_base))
                return PTR_ERR(io_base);
@@ -666,39 +683,43 @@ static int davinci_i2s_probe(struct platform_device *pdev)
        if (!dev)
                return -ENOMEM;
 
-       dev->clk = clk_get(&pdev->dev, NULL);
-       if (IS_ERR(dev->clk))
-               return -ENODEV;
-       clk_enable(dev->clk);
-
        dev->base = io_base;
 
-       dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
-           (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
+       /* setup DMA, first TX, then RX */
+       dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+       dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
 
-       dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
-           (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
-
-       /* first TX, then RX */
        res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "no DMA resource\n");
-               ret = -ENXIO;
-               goto err_release_clk;
+       if (res) {
+               dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
+               *dma = res->start;
+               dma_data->filter_data = dma;
+       } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+               dma_data->filter_data = "tx";
+       } else {
+               dev_err(&pdev->dev, "Missing DMA tx resource\n");
+               return -ENODEV;
        }
-       dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
-       *dma = res->start;
-       dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = dma;
+
+       dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+       dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
 
        res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (!res) {
-               dev_err(&pdev->dev, "no DMA resource\n");
-               ret = -ENXIO;
-               goto err_release_clk;
+       if (res) {
+               dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
+               *dma = res->start;
+               dma_data->filter_data = dma;
+       } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+               dma_data->filter_data = "rx";
+       } else {
+               dev_err(&pdev->dev, "Missing DMA rx resource\n");
+               return -ENODEV;
        }
-       dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
-       *dma = res->start;
-       dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = dma;
+
+       dev->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk))
+               return -ENODEV;
+       clk_enable(dev->clk);
 
        dev->dev = &pdev->dev;
        dev_set_drvdata(&pdev->dev, dev);
@@ -737,11 +758,18 @@ static int davinci_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id davinci_i2s_match[] = {
+       { .compatible = "ti,da850-mcbsp" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, davinci_i2s_match);
+
 static struct platform_driver davinci_mcbsp_driver = {
        .probe          = davinci_i2s_probe,
        .remove         = davinci_i2s_remove,
        .driver         = {
                .name   = "davinci-mcbsp",
+               .of_match_table = of_match_ptr(davinci_i2s_match),
        },
 };
 
index e1324989bd6b9a498d35698f9748f5dae14ffe07..0f66fda2c7727c3c0821c97228054234b7fcbdba 100644 (file)
@@ -489,7 +489,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
 
                mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG,
-                              ACLKX | AHCLKX | AFSX | ACLKR | AHCLKR | AFSR);
+                              ACLKX | AFSX | ACLKR | AHCLKR | AFSR);
                mcasp->bclk_master = 0;
                break;
        default:
@@ -540,21 +540,19 @@ out:
        return ret;
 }
 
-static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
+static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
                                      int div, bool explicit)
 {
-       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-
        pm_runtime_get_sync(mcasp->dev);
        switch (div_id) {
-       case 0:         /* MCLK divider */
+       case MCASP_CLKDIV_AUXCLK:                       /* MCLK divider */
                mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
                               AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
                mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
                               AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
                break;
 
-       case 1:         /* BCLK divider */
+       case MCASP_CLKDIV_BCLK:                 /* BCLK divider */
                mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
                               ACLKXDIV(div - 1), ACLKXDIV_MASK);
                mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
@@ -563,7 +561,8 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
                        mcasp->bclk_div = div;
                break;
 
-       case 2: /*
+       case MCASP_CLKDIV_BCLK_FS_RATIO:
+               /*
                 * BCLK/LRCLK ratio descries how many bit-clock cycles
                 * fit into one frame. The clock ratio is given for a
                 * full period of data (for I2S format both left and
@@ -591,7 +590,9 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
 static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
                                    int div)
 {
-       return __davinci_mcasp_set_clkdiv(dai, div_id, div, 1);
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+       return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1);
 }
 
 static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -999,27 +1000,53 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
 }
 
 static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
-                                     unsigned int bclk_freq,
-                                     int *error_ppm)
+                                     unsigned int bclk_freq, bool set)
 {
-       int div = mcasp->sysclk_freq / bclk_freq;
-       int rem = mcasp->sysclk_freq % bclk_freq;
+       int error_ppm;
+       unsigned int sysclk_freq = mcasp->sysclk_freq;
+       u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
+       int div = sysclk_freq / bclk_freq;
+       int rem = sysclk_freq % bclk_freq;
+       int aux_div = 1;
+
+       if (div > (ACLKXDIV_MASK + 1)) {
+               if (reg & AHCLKXE) {
+                       aux_div = div / (ACLKXDIV_MASK + 1);
+                       if (div % (ACLKXDIV_MASK + 1))
+                               aux_div++;
+
+                       sysclk_freq /= aux_div;
+                       div = sysclk_freq / bclk_freq;
+                       rem = sysclk_freq % bclk_freq;
+               } else if (set) {
+                       dev_warn(mcasp->dev, "Too fast reference clock (%u)\n",
+                                sysclk_freq);
+               }
+       }
 
        if (rem != 0) {
                if (div == 0 ||
-                   ((mcasp->sysclk_freq / div) - bclk_freq) >
-                   (bclk_freq - (mcasp->sysclk_freq / (div+1)))) {
+                   ((sysclk_freq / div) - bclk_freq) >
+                   (bclk_freq - (sysclk_freq / (div+1)))) {
                        div++;
                        rem = rem - bclk_freq;
                }
        }
-       if (error_ppm)
-               *error_ppm =
-                       (div*1000000 + (int)div64_long(1000000LL*rem,
-                                                      (int)bclk_freq))
-                       /div - 1000000;
+       error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem,
+                    (int)bclk_freq)) / div - 1000000;
+
+       if (set) {
+               if (error_ppm)
+                       dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
+                                error_ppm);
+
+               __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
+               if (reg & AHCLKXE)
+                       __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
+                                                  aux_div, 0);
+       }
 
-       return div;
+       return error_ppm;
 }
 
 static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
@@ -1044,18 +1071,11 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
                int slots = mcasp->tdm_slots;
                int rate = params_rate(params);
                int sbits = params_width(params);
-               int ppm, div;
 
                if (mcasp->slot_width)
                        sbits = mcasp->slot_width;
 
-               div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
-                                                &ppm);
-               if (ppm)
-                       dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
-                                ppm);
-
-               __davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0);
+               davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true);
        }
 
        ret = mcasp_common_hw_param(mcasp, substream->stream,
@@ -1166,7 +1186,8 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
                                davinci_mcasp_dai_rates[i];
                        int ppm;
 
-                       davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
+                       ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq,
+                                                        false);
                        if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
                                if (range.empty) {
                                        range.min = davinci_mcasp_dai_rates[i];
@@ -1205,8 +1226,9 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
                        if (rd->mcasp->slot_width)
                                sbits = rd->mcasp->slot_width;
 
-                       davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
-                                                  &ppm);
+                       ppm = davinci_mcasp_calc_clk_div(rd->mcasp,
+                                                        sbits * slots * rate,
+                                                        false);
                        if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
                                snd_mask_set(&nfmt, i);
                                count++;
@@ -1230,11 +1252,15 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
        int i, dir;
        int tdm_slots = mcasp->tdm_slots;
 
-       if (mcasp->tdm_mask[substream->stream])
-               tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
+       /* Do not allow more then one stream per direction */
+       if (mcasp->substreams[substream->stream])
+               return -EBUSY;
 
        mcasp->substreams[substream->stream] = substream;
 
+       if (mcasp->tdm_mask[substream->stream])
+               tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
+
        if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
                return 0;
 
index a3be108a8c1775eae827459fbb4133edeefebd8a..1e8787fb3fb766bf386e51b629e2634294712a85 100644 (file)
 #define NUMEVT(x)      (((x) & 0xFF) << 8)
 #define NUMDMA_MASK    (0xFF)
 
+/* clock divider IDs */
+#define MCASP_CLKDIV_AUXCLK            0 /* HCLK divider from AUXCLK */
+#define MCASP_CLKDIV_BCLK              1 /* BCLK divider from HCLK */
+#define MCASP_CLKDIV_BCLK_FS_RATIO     2 /* to set BCLK FS ration */
+
 #endif /* DAVINCI_MCASP_H */
index bff258d7bcea1f380403df5b9380ac7c9aabf835..0db69b7e96170e471fe3101982a1cfeaa50dfb60 100644 (file)
@@ -100,6 +100,7 @@ struct dw_i2s_dev {
        struct device *dev;
        u32 ccr;
        u32 xfer_resolution;
+       u32 fifo_th;
 
        /* data related to DMA transfers b/w i2s and DMAC */
        union dw_i2s_snd_dma_data play_dma_data;
@@ -147,17 +148,18 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
 static void i2s_start(struct dw_i2s_dev *dev,
                      struct snd_pcm_substream *substream)
 {
+       struct i2s_clk_config_data *config = &dev->config;
        u32 i, irq;
        i2s_write_reg(dev->i2s_base, IER, 1);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               for (i = 0; i < 4; i++) {
+               for (i = 0; i < (config->chan_nr / 2); i++) {
                        irq = i2s_read_reg(dev->i2s_base, IMR(i));
                        i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
                }
                i2s_write_reg(dev->i2s_base, ITER, 1);
        } else {
-               for (i = 0; i < 4; i++) {
+               for (i = 0; i < (config->chan_nr / 2); i++) {
                        irq = i2s_read_reg(dev->i2s_base, IMR(i));
                        i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
                }
@@ -231,14 +233,16 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
                if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                        i2s_write_reg(dev->i2s_base, TCR(ch_reg),
                                      dev->xfer_resolution);
-                       i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
+                       i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
+                                     dev->fifo_th - 1);
                        irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
                        i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
                        i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
                } else {
                        i2s_write_reg(dev->i2s_base, RCR(ch_reg),
                                      dev->xfer_resolution);
-                       i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
+                       i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
+                                     dev->fifo_th - 1);
                        irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
                        i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
                        i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
@@ -498,6 +502,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
         */
        u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
        u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
+       u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
        u32 idx;
 
        if (dev->capability & DWC_I2S_RECORD &&
@@ -536,6 +541,7 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
                dev->capability |= DW_I2S_SLAVE;
        }
 
+       dev->fifo_th = fifo_depth / 2;
        return 0;
 }
 
index 0754df771e3b4c8a945c29e8c3c99f5c23403719..2147994ab46f6826824a3954947ed45285e0baae 100644 (file)
@@ -21,6 +21,8 @@
 #include <sound/core.h>
 #include <sound/dmaengine_pcm.h>
 #include <sound/pcm_params.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 
 #include "fsl_sai.h"
 #include "imx-pcm.h"
@@ -786,10 +788,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
        struct fsl_sai *sai;
+       struct regmap *gpr;
        struct resource *res;
        void __iomem *base;
        char tmp[8];
        int irq, ret, i;
+       int index;
 
        sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
        if (!sai)
@@ -797,7 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
 
        sai->pdev = pdev;
 
-       if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai"))
+       if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") ||
+           of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai"))
                sai->sai_on_imx = true;
 
        sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
@@ -877,6 +882,22 @@ static int fsl_sai_probe(struct platform_device *pdev)
                fsl_sai_dai.symmetric_samplebits = 0;
        }
 
+       if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
+           of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) {
+               gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
+               if (IS_ERR(gpr)) {
+                       dev_err(&pdev->dev, "cannot find iomuxc registers\n");
+                       return PTR_ERR(gpr);
+               }
+
+               index = of_alias_get_id(np, "sai");
+               if (index < 0)
+                       return index;
+
+               regmap_update_bits(gpr, IOMUXC_GPR1, MCLK_DIR(index),
+                                  MCLK_DIR(index));
+       }
+
        sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
        sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
        sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
@@ -898,6 +919,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
 static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,vf610-sai", },
        { .compatible = "fsl,imx6sx-sai", },
+       { .compatible = "fsl,imx6ul-sai", },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, fsl_sai_ids);
index ed8de1035cda159d0d186f2cded0fb7a97fbceb4..632ecc0e39562aab90a880e0c8b678a4ccea1078 100644 (file)
@@ -137,6 +137,7 @@ static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
        case CCSR_SSI_SACDAT:
        case CCSR_SSI_SATAG:
        case CCSR_SSI_SACCST:
+       case CCSR_SSI_SOR:
                return true;
        default:
                return false;
@@ -261,6 +262,7 @@ struct fsl_ssi_private {
        struct fsl_ssi_dbg dbg_stats;
 
        const struct fsl_ssi_soc_data *soc;
+       struct device *dev;
 };
 
 /*
@@ -399,6 +401,26 @@ static void fsl_ssi_rxtx_config(struct fsl_ssi_private *ssi_private,
        }
 }
 
+/*
+ * Clear RX or TX FIFO to remove samples from the previous
+ * stream session which may be still present in the FIFO and
+ * may introduce bad samples and/or channel slipping.
+ *
+ * Note: The SOR is not documented in recent IMX datasheet, but
+ * is described in IMX51 reference manual at section 56.3.3.15.
+ */
+static void fsl_ssi_fifo_clear(struct fsl_ssi_private *ssi_private,
+               bool is_rx)
+{
+       if (is_rx) {
+               regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
+                       CCSR_SSI_SOR_RX_CLR, CCSR_SSI_SOR_RX_CLR);
+       } else {
+               regmap_update_bits(ssi_private->regs, CCSR_SSI_SOR,
+                       CCSR_SSI_SOR_TX_CLR, CCSR_SSI_SOR_TX_CLR);
+       }
+}
+
 /*
  * Calculate the bits that have to be disabled for the current stream that is
  * getting disabled. This keeps the bits enabled that are necessary for the
@@ -474,9 +496,11 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
         * (online configuration)
         */
        if (enable) {
-               regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
+               fsl_ssi_fifo_clear(ssi_private, vals->scr & CCSR_SSI_SCR_RE);
+
                regmap_update_bits(regs, CCSR_SSI_SRCR, vals->srcr, vals->srcr);
                regmap_update_bits(regs, CCSR_SSI_STCR, vals->stcr, vals->stcr);
+               regmap_update_bits(regs, CCSR_SSI_SIER, vals->sier, vals->sier);
        } else {
                u32 sier;
                u32 srcr;
@@ -506,8 +530,40 @@ static void fsl_ssi_config(struct fsl_ssi_private *ssi_private, bool enable,
 
 config_done:
        /* Enabling of subunits is done after configuration */
-       if (enable)
+       if (enable) {
+               if (ssi_private->use_dma && (vals->scr & CCSR_SSI_SCR_TE)) {
+                       /*
+                        * Be sure the Tx FIFO is filled when TE is set.
+                        * Otherwise, there are some chances to start the
+                        * playback with some void samples inserted first,
+                        * generating a channel slip.
+                        *
+                        * First, SSIEN must be set, to let the FIFO be filled.
+                        *
+                        * Notes:
+                        * - Limit this fix to the DMA case until FIQ cases can
+                        *   be tested.
+                        * - Limit the length of the busy loop to not lock the
+                        *   system too long, even if 1-2 loops are sufficient
+                        *   in general.
+                        */
+                       int i;
+                       int max_loop = 100;
+                       regmap_update_bits(regs, CCSR_SSI_SCR,
+                                       CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN);
+                       for (i = 0; i < max_loop; i++) {
+                               u32 sfcsr;
+                               regmap_read(regs, CCSR_SSI_SFCSR, &sfcsr);
+                               if (CCSR_SSI_SFCSR_TFCNT0(sfcsr))
+                                       break;
+                       }
+                       if (i == max_loop) {
+                               dev_err(ssi_private->dev,
+                                       "Timeout waiting TX FIFO filling\n");
+                       }
+               }
                regmap_update_bits(regs, CCSR_SSI_SCR, vals->scr, vals->scr);
+       }
 }
 
 
@@ -670,6 +726,15 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
        if (IS_ERR(ssi_private->baudclk))
                return -EINVAL;
 
+       /*
+        * Hardware limitation: The bclk rate must be
+        * never greater than 1/5 IPG clock rate
+        */
+       if (freq * 5 > clk_get_rate(ssi_private->clk)) {
+               dev_err(cpu_dai->dev, "bitclk > ipgclk/5\n");
+               return -EINVAL;
+       }
+
        baudclk_is_used = ssi_private->baudclk_streams & ~(BIT(substream->stream));
 
        /* It should be already enough to divide clock by setting pm alone */
@@ -686,13 +751,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
                else
                        clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
 
-               /*
-                * Hardware limitation: The bclk rate must be
-                * never greater than 1/5 IPG clock rate
-                */
-               if (clkrate * 5 > clk_get_rate(ssi_private->clk))
-                       continue;
-
                clkrate /= factor;
                afreq = clkrate / (i + 1);
 
@@ -1158,14 +1216,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
        .playback = {
                .stream_name = "CPU-Playback",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rates = FSLSSI_I2S_RATES,
                .formats = FSLSSI_I2S_FORMATS,
        },
        .capture = {
                .stream_name = "CPU-Capture",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 32,
                .rates = FSLSSI_I2S_RATES,
                .formats = FSLSSI_I2S_FORMATS,
        },
@@ -1402,6 +1460,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        }
 
        ssi_private->soc = of_id->data;
+       ssi_private->dev = &pdev->dev;
 
        sprop = of_get_property(np, "fsl,mode", NULL);
        if (sprop) {
index e63cd5ecfd8feacd396f835756af90107fd2e2a6..dac6688540dc32ff966b56d05a3a6556d344e57c 100644 (file)
@@ -220,7 +220,7 @@ static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
        ret = dma_mmap_wc(substream->pcm->card->dev, vma, runtime->dma_area,
                          runtime->dma_addr, runtime->dma_bytes);
 
-       pr_debug("%s: ret: %d %p %pad 0x%08x\n", __func__, ret,
+       pr_debug("%s: ret: %d %p %pad 0x%08zx\n", __func__, ret,
                        runtime->dma_area,
                        &runtime->dma_addr,
                        runtime->dma_bytes);
index 1120f4f4d011cdc188dff6edd112d00ab027a769..91c15abb625e81390311f3f875060af387cd6ae5 100644 (file)
@@ -58,6 +58,21 @@ config SND_SOC_INTEL_HASWELL_MACH
          Say Y if you have such a device
          If unsure select "N".
 
+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
+       select SND_SOC_HDAC_HDMI
+       select SND_HDA_DSP_LOADER
+       help
+          This adds support for ASoC machine driver for Broxton platforms
+          with RT286 I2S audio codec.
+          Say Y if you have such a device
+          If unsure select "N".
+
 config SND_SOC_INTEL_BYT_RT5640_MACH
        tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
        depends on X86_INTEL_LPSS && I2C
@@ -162,6 +177,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
 config SND_SOC_INTEL_SKYLAKE
        tristate
        select SND_HDA_EXT_CORE
+       select SND_HDA_DSP_LOADER
        select SND_SOC_TOPOLOGY
        select SND_SOC_INTEL_SST
 
index b97e6adcf1b268d993493508fdcab21640072af4..98720a93de8a1fa20999bb464d4e02e8ee5c45c1 100644 (file)
@@ -195,7 +195,7 @@ static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol
 
        if (e->w && e->w->power)
                ret = sst_send_slot_map(drv);
-       else
+       else if (!e->w)
                dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
                                kcontrol->id.name);
        return ret;
index 3310c0f9c356e93e88486f8dc21e77e7dca739d4..a8506774f510e6072ebdda949fcb5ddd99bb680b 100644 (file)
@@ -2,6 +2,7 @@ snd-soc-sst-haswell-objs := haswell.o
 snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
 snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
 snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
 snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
 snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
 snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
@@ -14,6 +15,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
 obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
+obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
 obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
 obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
 obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
index 3f8a1e10bed02841f6b437d3d4a24615879ca35e..7486a0022fdea1f6e06ff7be167f1b60ee558462 100644 (file)
@@ -201,7 +201,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
        {
                /* SSP0 - Codec */
                .name = "Codec",
-               .be_id = 0,
+               .id = 0,
                .cpu_dai_name = "snd-soc-dummy-dai",
                .platform_name = "snd-soc-dummy",
                .no_pcm = 1,
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
new file mode 100644 (file)
index 0000000..f478751
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Intel Broxton-P I2S Machine Driver
+ *
+ * Copyright (C) 2014-2016, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ *   Intel Skylake I2S Machine driver
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include "../../codecs/hdac_hdmi.h"
+#include "../../codecs/rt298.h"
+
+static struct snd_soc_jack broxton_headset;
+/* Headset jack detection DAPM pins */
+
+enum {
+       BXT_DPCM_AUDIO_PB = 0,
+       BXT_DPCM_AUDIO_CP,
+       BXT_DPCM_AUDIO_REF_CP,
+       BXT_DPCM_AUDIO_HDMI1_PB,
+       BXT_DPCM_AUDIO_HDMI2_PB,
+       BXT_DPCM_AUDIO_HDMI3_PB,
+};
+
+static struct snd_soc_jack_pin broxton_headset_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static const struct snd_kcontrol_new broxton_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+       SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static const struct snd_soc_dapm_widget broxton_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_MIC("DMIC2", NULL),
+       SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+       SND_SOC_DAPM_SPK("HDMI1", NULL),
+       SND_SOC_DAPM_SPK("HDMI2", NULL),
+       SND_SOC_DAPM_SPK("HDMI3", NULL),
+};
+
+static const struct snd_soc_dapm_route broxton_rt298_map[] = {
+       /* speaker */
+       {"Speaker", NULL, "SPOR"},
+       {"Speaker", NULL, "SPOL"},
+
+       /* HP jack connectors - unknown if we have jack detect */
+       {"Headphone Jack", NULL, "HPO Pin"},
+
+       /* other jacks */
+       {"MIC1", NULL, "Mic Jack"},
+
+       /* digital mics */
+       {"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, "ssp5 Tx"},
+       { "ssp5 Tx", NULL, "codec0_out"},
+
+       { "codec0_in", NULL, "ssp5 Rx" },
+       { "ssp5 Rx", NULL, "AIF1 Capture" },
+
+       { "dmic01_hifi", NULL, "DMIC01 Rx" },
+       { "DMIC01 Rx", NULL, "Capture" },
+
+       { "hifi3", NULL, "iDisp3 Tx"},
+       { "iDisp3 Tx", NULL, "iDisp3_out"},
+       { "hifi2", NULL, "iDisp2 Tx"},
+       { "iDisp2 Tx", NULL, "iDisp2_out"},
+       { "hifi1", NULL, "iDisp1 Tx"},
+       { "iDisp1 Tx", NULL, "iDisp1_out"},
+
+};
+
+static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       int ret = 0;
+
+       ret = snd_soc_card_jack_new(rtd->card, "Headset",
+               SND_JACK_HEADSET | SND_JACK_BTN_0,
+               &broxton_headset,
+               broxton_headset_pins, ARRAY_SIZE(broxton_headset_pins));
+
+       if (ret)
+               return ret;
+
+       rt298_mic_detect(codec, &broxton_headset);
+       return 0;
+}
+
+static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
+}
+
+static int broxton_ssp5_fixup(struct snd_soc_pcm_runtime *rtd,
+                       struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                       SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+       /* The ADSP will covert the FE rate to 48k, stereo */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP5 to 24 bit */
+       snd_mask_none(fmt);
+       snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+       return 0;
+}
+
+static int broxton_rt298_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT298_SCLK_S_PLL,
+                                       19200000, SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+               return ret;
+       }
+
+       return ret;
+}
+
+static struct snd_soc_ops broxton_rt298_ops = {
+       .hw_params = broxton_rt298_hw_params,
+};
+
+/* broxton digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link broxton_rt298_dais[] = {
+       /* Front End DAI links */
+       [BXT_DPCM_AUDIO_PB]
+       {
+               .name = "Bxt Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:0e.0",
+               .nonatomic = 1,
+               .dynamic = 1,
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dpcm_playback = 1,
+       },
+       [BXT_DPCM_AUDIO_CP]
+       {
+               .name = "Bxt Audio Capture Port",
+               .stream_name = "Audio Record",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:0e.0",
+               .nonatomic = 1,
+               .dynamic = 1,
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dpcm_capture = 1,
+       },
+       [BXT_DPCM_AUDIO_REF_CP]
+       {
+               .name = "Bxt Audio Reference cap",
+               .stream_name = "refcap",
+               .cpu_dai_name = "Reference Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .init = NULL,
+               .dpcm_capture = 1,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [BXT_DPCM_AUDIO_HDMI1_PB]
+       {
+               .name = "Bxt HDMI Port1",
+               .stream_name = "Hdmi1",
+               .cpu_dai_name = "HDMI1 Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .dpcm_playback = 1,
+               .init = NULL,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [BXT_DPCM_AUDIO_HDMI2_PB]
+       {
+               .name = "Bxt HDMI Port2",
+               .stream_name = "Hdmi2",
+               .cpu_dai_name = "HDMI2 Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .dpcm_playback = 1,
+               .init = NULL,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [BXT_DPCM_AUDIO_HDMI3_PB]
+       {
+               .name = "Bxt HDMI Port3",
+               .stream_name = "Hdmi3",
+               .cpu_dai_name = "HDMI3 Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .dpcm_playback = 1,
+               .init = NULL,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       /* Back End DAI links */
+       {
+               /* SSP5 - Codec */
+               .name = "SSP5-Codec",
+               .id = 0,
+               .cpu_dai_name = "SSP5 Pin",
+               .platform_name = "0000:00:0e.0",
+               .no_pcm = 1,
+               .codec_name = "i2c-INT343A:00",
+               .codec_dai_name = "rt298-aif1",
+               .init = broxton_rt298_codec_init,
+               .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
+                                               SND_SOC_DAIFMT_CBS_CFS,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = broxton_ssp5_fixup,
+               .ops = &broxton_rt298_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "dmic01",
+               .id = 1,
+               .cpu_dai_name = "DMIC01 Pin",
+               .codec_name = "dmic-codec",
+               .codec_dai_name = "dmic-hifi",
+               .platform_name = "0000:00:0e.0",
+               .ignore_suspend = 1,
+               .dpcm_capture = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp1",
+               .id = 3,
+               .cpu_dai_name = "iDisp1 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi1",
+               .platform_name = "0000:00:0e.0",
+               .init = broxton_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp2",
+               .id = 4,
+               .cpu_dai_name = "iDisp2 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi2",
+               .platform_name = "0000:00:0e.0",
+               .init = broxton_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp3",
+               .id = 5,
+               .cpu_dai_name = "iDisp3 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi3",
+               .platform_name = "0000:00:0e.0",
+               .init = broxton_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+};
+
+/* broxton audio machine driver for SPT + RT298S */
+static struct snd_soc_card broxton_rt298 = {
+       .name = "broxton-rt298",
+       .owner = THIS_MODULE,
+       .dai_link = broxton_rt298_dais,
+       .num_links = ARRAY_SIZE(broxton_rt298_dais),
+       .controls = broxton_controls,
+       .num_controls = ARRAY_SIZE(broxton_controls),
+       .dapm_widgets = broxton_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(broxton_widgets),
+       .dapm_routes = broxton_rt298_map,
+       .num_dapm_routes = ARRAY_SIZE(broxton_rt298_map),
+       .fully_routed = true,
+};
+
+static int broxton_audio_probe(struct platform_device *pdev)
+{
+       broxton_rt298.dev = &pdev->dev;
+
+       return devm_snd_soc_register_card(&pdev->dev, &broxton_rt298);
+}
+
+static struct platform_driver broxton_audio = {
+       .probe = broxton_audio_probe,
+       .driver = {
+               .name = "bxt_alc298s_i2s",
+       },
+};
+module_platform_driver(broxton_audio)
+
+/* Module information */
+MODULE_AUTHOR("Ramesh Babu <Ramesh.Babu@intel.com>");
+MODULE_AUTHOR("Senthilnathan Veppur <senthilnathanx.veppur@intel.com>");
+MODULE_DESCRIPTION("Intel SST Audio for Broxton");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bxt_alc298s_i2s");
index 032a2e753f0bfbdbee9814af8dd173bf8be51cf4..88efb62439ba3f8b74832729cac15c6365930857 100644 (file)
@@ -304,7 +304,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
                /* back ends */
        {
                .name = "SSP2-Codec",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "ssp2-port",
                .platform_name = "sst-mfld-platform",
                .no_pcm = 1,
index 1c95ccc886c4cdc695b80e12f5960512a9c60b5a..35f591eab3c93111b32508b93afb47b52ea98074 100644 (file)
@@ -267,7 +267,7 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = {
        /* back ends */
        {
                .name = "SSP2-Codec",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "ssp2-port",
                .platform_name = "sst-mfld-platform",
                .no_pcm = 1,
index e609f089593a935984c101cb6e78fe7a0966e314..6260df6bd49cb5ed965ed2ca0b317caa4ee5a26a 100644 (file)
@@ -255,7 +255,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
        /* back ends */
        {
                .name = "SSP2-Codec",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "ssp2-port",
                .platform_name = "sst-mfld-platform",
                .no_pcm = 1,
index 2a6f80843bc9656b8303a4b9f1d67e4f9b49df45..0618a7f1025b95aa91eeb7cbb083c1e975e5c910 100644 (file)
@@ -295,7 +295,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
        /* back ends */
        {
                .name = "SSP2-Codec",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "ssp2-port",
                .platform_name = "sst-mfld-platform",
                .no_pcm = 1,
index 2e5347f8f96c449f9fde65dc614f43439acd155b..df9d254baa1876e900ca7a41d5cf749c8493d2c1 100644 (file)
@@ -273,7 +273,7 @@ static struct snd_soc_dai_link cht_dailink[] = {
        {
                /* SSP2 - Codec */
                .name = "SSP2-Codec",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "ssp2-port",
                .platform_name = "sst-mfld-platform",
                .no_pcm = 1,
index 22558572cb9ca12c8ae9e60d940473db35cb2434..863f1d5e2a2c97b9775ff84a9d43d985db9fb9b5 100644 (file)
@@ -156,7 +156,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
        {
                /* SSP0 - Codec */
                .name = "Codec",
-               .be_id = 0,
+               .id = 0,
                .cpu_dai_name = "snd-soc-dummy-dai",
                .platform_name = "snd-soc-dummy",
                .no_pcm = 1,
index 72176b79a18d6c61c02afabdcfab6bf5b3f4f932..d2808652b9741f2fced8381e356563b63d54bd48 100644 (file)
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
 
+struct skl_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+};
+
+struct skl_nau8825_private {
+       struct list_head hdmi_pcm_list;
+};
+
 enum {
        SKL_DPCM_AUDIO_PB = 0,
        SKL_DPCM_AUDIO_CP,
@@ -192,23 +202,56 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+
+       pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
+       pcm->codec_dai = dai;
 
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+
+       pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
+       pcm->codec_dai = dai;
 
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
 
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+       pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
+       pcm->codec_dai = dai;
+
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
@@ -391,7 +434,6 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .platform_name = "0000:00:1f.3",
                .init = NULL,
                .dpcm_capture = 1,
-               .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
                .ops = &skylaye_refcap_ops,
@@ -456,7 +498,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        {
                /* SSP0 - Codec */
                .name = "SSP0-Codec",
-               .be_id = 0,
+               .id = 0,
                .cpu_dai_name = "SSP0 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
@@ -472,7 +514,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        {
                /* SSP1 - Codec */
                .name = "SSP1-Codec",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "SSP1 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
@@ -489,7 +531,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "dmic01",
-               .be_id = 2,
+               .id = 2,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
@@ -501,7 +543,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "iDisp1",
-               .be_id = 3,
+               .id = 3,
                .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
@@ -512,7 +554,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "iDisp2",
-               .be_id = 4,
+               .id = 4,
                .cpu_dai_name = "iDisp2 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi2",
@@ -523,7 +565,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "iDisp3",
-               .be_id = 5,
+               .id = 5,
                .cpu_dai_name = "iDisp3 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi3",
@@ -534,6 +576,21 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
 };
 
+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;
+
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 /* skylake audio machine driver for SPT + NAU88L25 */
 static struct snd_soc_card skylake_audio_card = {
        .name = "sklnau8825max",
@@ -547,11 +604,21 @@ static struct snd_soc_card skylake_audio_card = {
        .dapm_routes = skylake_map,
        .num_dapm_routes = ARRAY_SIZE(skylake_map),
        .fully_routed = true,
+       .late_probe = skylake_card_late_probe,
 };
 
 static int skylake_audio_probe(struct platform_device *pdev)
 {
+       struct skl_nau8825_private *ctx;
+
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
        skylake_audio_card.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
 
        return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
 }
index 5f1ca99ae9b0d03dc51045af7a3489ee3a20b830..e19aa99c4f7246914fafd1819e561f3b60c7b250 100644 (file)
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
 
+struct skl_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+};
+
+struct skl_nau88125_private {
+       struct list_head hdmi_pcm_list;
+};
 enum {
        SKL_DPCM_AUDIO_PB = 0,
        SKL_DPCM_AUDIO_CP,
@@ -222,24 +231,57 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+
+       pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
+       pcm->codec_dai = dai;
+
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
 
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+       return 0;
 }
 
 static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+
+       pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
+       pcm->codec_dai = dai;
 
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 
 static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
 
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+       pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
+       pcm->codec_dai = dai;
+
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
@@ -440,7 +482,6 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .platform_name = "0000:00:1f.3",
                .init = NULL,
                .dpcm_capture = 1,
-               .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
                .ops = &skylaye_refcap_ops,
@@ -505,7 +546,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        {
                /* SSP0 - Codec */
                .name = "SSP0-Codec",
-               .be_id = 0,
+               .id = 0,
                .cpu_dai_name = "SSP0 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
@@ -523,7 +564,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        {
                /* SSP1 - Codec */
                .name = "SSP1-Codec",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "SSP1 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
@@ -540,7 +581,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "dmic01",
-               .be_id = 2,
+               .id = 2,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
@@ -552,7 +593,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "iDisp1",
-               .be_id = 3,
+               .id = 3,
                .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
@@ -563,7 +604,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "iDisp2",
-               .be_id = 4,
+               .id = 4,
                .cpu_dai_name = "iDisp2 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi2",
@@ -574,7 +615,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "iDisp3",
-               .be_id = 5,
+               .id = 5,
                .cpu_dai_name = "iDisp3 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi3",
@@ -585,6 +626,21 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
 };
 
+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;
+
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 /* skylake audio machine driver for SPT + NAU88L25 */
 static struct snd_soc_card skylake_audio_card = {
        .name = "sklnau8825adi",
@@ -600,11 +656,21 @@ static struct snd_soc_card skylake_audio_card = {
        .codec_conf = ssm4567_codec_conf,
        .num_configs = ARRAY_SIZE(ssm4567_codec_conf),
        .fully_routed = true,
+       .late_probe = skylake_card_late_probe,
 };
 
 static int skylake_audio_probe(struct platform_device *pdev)
 {
+       struct skl_nau88125_private *ctx;
+
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
        skylake_audio_card.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
 
        return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
 }
index 2016397a8e755e5cdeb72b26b270c0203aaec4b9..426b48233fdb1eed38824dc4b949a1dac8ec3907 100644 (file)
 
 static struct snd_soc_jack skylake_headset;
 
+struct skl_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+};
+
+struct skl_rt286_private {
+       struct list_head hdmi_pcm_list;
+};
+
 enum {
        SKL_DPCM_AUDIO_PB = 0,
        SKL_DPCM_AUDIO_CP,
@@ -142,9 +152,20 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
 
 static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
 {
+       struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
 
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id);
+       pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id;
+       pcm->codec_dai = dai;
+
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+
+       return 0;
 }
 
 static unsigned int rates[] = {
@@ -317,7 +338,6 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
                .platform_name = "0000:00:1f.3",
                .init = NULL,
                .dpcm_capture = 1,
-               .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
        },
@@ -375,7 +395,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
        {
                /* SSP0 - Codec */
                .name = "SSP0-Codec",
-               .be_id = 0,
+               .id = 0,
                .cpu_dai_name = "SSP0 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
@@ -393,7 +413,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
        },
        {
                .name = "dmic01",
-               .be_id = 1,
+               .id = 1,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
@@ -405,7 +425,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
        },
        {
                .name = "iDisp1",
-               .be_id = 2,
+               .id = 2,
                .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
@@ -416,7 +436,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
        },
        {
                .name = "iDisp2",
-               .be_id = 3,
+               .id = 3,
                .cpu_dai_name = "iDisp2 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi2",
@@ -427,7 +447,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
        },
        {
                .name = "iDisp3",
-               .be_id = 4,
+               .id = 4,
                .cpu_dai_name = "iDisp3 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi3",
@@ -438,6 +458,21 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
        },
 };
 
+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;
+
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 /* skylake audio machine driver for SPT + RT286S */
 static struct snd_soc_card skylake_rt286 = {
        .name = "skylake-rt286",
@@ -451,11 +486,21 @@ static struct snd_soc_card skylake_rt286 = {
        .dapm_routes = skylake_rt286_map,
        .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
        .fully_routed = true,
+       .late_probe = skylake_card_late_probe,
 };
 
 static int skylake_audio_probe(struct platform_device *pdev)
 {
+       struct skl_rt286_private *ctx;
+
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
+
        skylake_rt286.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&skylake_rt286, ctx);
 
        return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
 }
index 4dcfb7e5ed7099abc8436e4156e2a552c4137f18..8398cb227ba9224bab3344aa908e44c8696f2b67 100644 (file)
  *
  */
 
+#include <linux/kconfig.h>
+#include <linux/stddef.h>
 #include <linux/acpi.h>
 
 /* translation fron HID to I2C name, needed for DAI codec_name */
+#if IS_ENABLED(CONFIG_ACPI)
 const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
+#else
+inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+{
+       return NULL;
+}
+#endif
 
 /* acpi match */
 struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
index 1aa819c7e09b0ff95c36021c7a69c4cf90433634..994256b39b9c5646cd5a26c3f9d3cc0f1da02236 100644 (file)
@@ -445,7 +445,7 @@ static int create_adsp_page_table(struct snd_pcm_substream *substream,
 
        pages = snd_sgbuf_aligned_pages(size);
 
-       dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n",
+       dev_dbg(rtd->dev, "generating page table for %p size 0x%zx pages %d\n",
                dma_area, size, pages);
 
        for (i = 0; i < pages; i++) {
index 914b6dab9beae20fa0421f06d9c21f0f58d3dd64..c28f5d0e1d99fee2d254d65c595638c4c0cb71bb 100644 (file)
@@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
 
 # Skylake IPC Support
 snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
-               skl-sst.o
+               skl-sst.o bxt-sst.o
 
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
new file mode 100644 (file)
index 0000000..965ce40
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ *  bxt-sst.c - DSP library functions for BXT platform
+ *
+ *  Copyright (C) 2015-16 Intel Corp
+ *  Author:Rafal Redzimski <rafal.f.redzimski@intel.com>
+ *        Jeeja KP <jeeja.kp@intel.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-sst-ipc.h"
+
+#define BXT_BASEFW_TIMEOUT     3000
+#define BXT_INIT_TIMEOUT       500
+#define BXT_IPC_PURGE_FW       0x01004000
+
+#define BXT_ROM_INIT           0x5
+#define BXT_ADSP_SRAM0_BASE    0x80000
+
+/* Firmware status window */
+#define BXT_ADSP_FW_STATUS     BXT_ADSP_SRAM0_BASE
+#define BXT_ADSP_ERROR_CODE     (BXT_ADSP_FW_STATUS + 0x4)
+
+#define BXT_ADSP_SRAM1_BASE    0xA0000
+
+static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
+{
+        return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
+}
+
+static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
+                       const void *fwdata, u32 fwsize)
+{
+       int stream_tag, ret, i;
+       u32 reg;
+
+       stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
+       if (stream_tag < 0) {
+               dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n",
+                               stream_tag);
+               return stream_tag;
+       }
+
+       ctx->dsp_ops.stream_tag = stream_tag;
+       memcpy(ctx->dmab.area, fwdata, fwsize);
+
+       /* Purge FW request */
+       sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
+                                        BXT_IPC_PURGE_FW | (stream_tag - 1));
+
+       ret = skl_dsp_enable_core(ctx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
+               ret = -EIO;
+               goto base_fw_load_failed;
+       }
+
+       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,
+                                       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);
+       }
+
+       /* enable Interrupt */
+       skl_ipc_int_enable(ctx);
+       skl_ipc_op_int_enable(ctx);
+
+       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;
+               goto base_fw_load_failed;
+       }
+
+       return ret;
+
+base_fw_load_failed:
+       ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
+       skl_dsp_disable_core(ctx);
+       return ret;
+}
+
+static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
+{
+       int ret;
+
+       ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag);
+       ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK,
+                       BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot");
+
+       ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag);
+       ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag);
+
+       return ret;
+}
+
+static int bxt_load_base_firmware(struct sst_dsp *ctx)
+{
+       const struct firmware *fw = NULL;
+       struct skl_sst *skl = ctx->thread_context;
+       int ret;
+
+       ret = request_firmware(&fw, ctx->fw_name, ctx->dev);
+       if (ret < 0) {
+               dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+               goto sst_load_base_firmware_failed;
+       }
+
+       ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+       /* Retry Enabling core and ROM load. Retry seemed to help */
+       if (ret < 0) {
+               ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
+                       goto sst_load_base_firmware_failed;
+               }
+       }
+
+       ret = sst_transfer_fw_host_dma(ctx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "Transfer firmware failed %d\n", ret);
+               dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+                       sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+                       sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+
+               skl_dsp_disable_core(ctx);
+       } else {
+               dev_dbg(ctx->dev, "Firmware download successful\n");
+               ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
+                                       msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+               if (ret == 0) {
+                       dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n");
+                       skl_dsp_disable_core(ctx);
+                       ret = -EIO;
+               } else {
+                       skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+                       ret = 0;
+               }
+       }
+
+sst_load_base_firmware_failed:
+       release_firmware(fw);
+       return ret;
+}
+
+static int bxt_set_dsp_D0(struct sst_dsp *ctx)
+{
+       struct skl_sst *skl = ctx->thread_context;
+       int ret;
+
+       skl->boot_complete = false;
+
+       ret = skl_dsp_enable_core(ctx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret);
+               return ret;
+       }
+
+       /* enable interrupt */
+       skl_ipc_int_enable(ctx);
+       skl_ipc_op_int_enable(ctx);
+
+       ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
+                                       msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+       if (ret == 0) {
+               dev_err(ctx->dev, "ipc: error DSP boot timeout\n");
+               dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+                       sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+                       sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+               return -EIO;
+       }
+
+       skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+       return 0;
+}
+
+static int bxt_set_dsp_D3(struct sst_dsp *ctx)
+{
+       struct skl_ipc_dxstate_info dx;
+       struct skl_sst *skl = ctx->thread_context;
+       int ret = 0;
+
+       if (!is_skl_dsp_running(ctx))
+               return ret;
+
+       dx.core_mask = SKL_DSP_CORE0_MASK;
+       dx.dx_mask = SKL_IPC_D3_MASK;
+
+       ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
+                               SKL_BASE_FW_MODULE_ID, &dx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret);
+               return ret;
+       }
+
+       ret = skl_dsp_disable_core(ctx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret);
+               ret = -EIO;
+       }
+
+       skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+       return 0;
+}
+
+static struct skl_dsp_fw_ops bxt_fw_ops = {
+       .set_state_D0 = bxt_set_dsp_D0,
+       .set_state_D3 = bxt_set_dsp_D3,
+       .load_fw = bxt_load_base_firmware,
+       .get_fw_errcode = bxt_get_errorcode,
+};
+
+static struct sst_ops skl_ops = {
+       .irq_handler = skl_dsp_sst_interrupt,
+       .write = sst_shim32_write,
+       .read = sst_shim32_read,
+       .ram_read = sst_memcpy_fromio_32,
+       .ram_write = sst_memcpy_toio_32,
+       .free = skl_dsp_free,
+};
+
+static struct sst_dsp_device skl_dev = {
+       .thread = skl_dsp_irq_thread_handler,
+       .ops = &skl_ops,
+};
+
+int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+                       const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
+                       struct skl_sst **dsp)
+{
+       struct skl_sst *skl;
+       struct sst_dsp *sst;
+       int ret;
+
+       skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
+       if (skl == NULL)
+               return -ENOMEM;
+
+       skl->dev = dev;
+       skl_dev.thread_context = skl;
+
+       skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
+       if (!skl->dsp) {
+               dev_err(skl->dev, "skl_dsp_ctx_init failed\n");
+               return -ENODEV;
+       }
+
+       sst = skl->dsp;
+       sst->fw_name = fw_name;
+       sst->dsp_ops = dsp_ops;
+       sst->fw_ops = bxt_fw_ops;
+       sst->addr.lpe = mmio_base;
+       sst->addr.shim = mmio_base;
+
+       sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
+                       SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
+
+       ret = skl_ipc_init(dev, skl);
+       if (ret)
+               return ret;
+
+       skl->boot_complete = false;
+       init_waitqueue_head(&skl->boot_wait);
+
+       ret = sst->fw_ops.load_fw(sst);
+       if (ret < 0) {
+               dev_err(dev, "Load base fw failed: %x", ret);
+               return ret;
+       }
+
+       if (dsp)
+               *dsp = skl;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
+
+
+void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
+{
+       skl_ipc_free(&ctx->ipc);
+       ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
+
+       if (ctx->dsp->addr.lpe)
+               iounmap(ctx->dsp->addr.lpe);
+
+       ctx->dsp->ops->free(ctx->dsp);
+}
+EXPORT_SYMBOL_GPL(bxt_sst_dsp_cleanup);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Broxton IPC driver");
index 79c5089b85d6483efab2ffb25b2959bf8210ab5e..226db84ba20f0286e3b175850f4087b3e4902a59 100644 (file)
@@ -72,6 +72,105 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
        skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
 }
 
+static int skl_dsp_setup_spib(struct device *dev, unsigned int size,
+                               int stream_tag, int enable)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct hdac_stream *stream = snd_hdac_get_stream(bus,
+                       SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
+       struct hdac_ext_stream *estream;
+
+       if (!stream)
+               return -EINVAL;
+
+       estream = stream_to_hdac_ext_stream(stream);
+       /* enable/disable SPIB for this hdac stream */
+       snd_hdac_ext_stream_spbcap_enable(ebus, enable, stream->index);
+
+       /* set the spib value */
+       snd_hdac_ext_stream_set_spib(ebus, estream, size);
+
+       return 0;
+}
+
+static int skl_dsp_prepare(struct device *dev, unsigned int format,
+                       unsigned int size, struct snd_dma_buffer *dmab)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct hdac_ext_stream *estream;
+       struct hdac_stream *stream;
+       struct snd_pcm_substream substream;
+       int ret;
+
+       if (!bus)
+               return -ENODEV;
+
+       memset(&substream, 0, sizeof(substream));
+       substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+
+       estream = snd_hdac_ext_stream_assign(ebus, &substream,
+                                       HDAC_EXT_STREAM_TYPE_HOST);
+       if (!estream)
+               return -ENODEV;
+
+       stream = hdac_stream(estream);
+
+       /* assign decouple host dma channel */
+       ret = snd_hdac_dsp_prepare(stream, format, size, dmab);
+       if (ret < 0)
+               return ret;
+
+       skl_dsp_setup_spib(dev, size, stream->stream_tag, true);
+
+       return stream->stream_tag;
+}
+
+static int skl_dsp_trigger(struct device *dev, bool start, int stream_tag)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct hdac_stream *stream;
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+       if (!bus)
+               return -ENODEV;
+
+       stream = snd_hdac_get_stream(bus,
+               SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
+       if (!stream)
+               return -EINVAL;
+
+       snd_hdac_dsp_trigger(stream, start);
+
+       return 0;
+}
+
+static int skl_dsp_cleanup(struct device *dev,
+               struct snd_dma_buffer *dmab, int stream_tag)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct hdac_stream *stream;
+       struct hdac_ext_stream *estream;
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+
+       if (!bus)
+               return -ENODEV;
+
+       stream = snd_hdac_get_stream(bus,
+               SNDRV_PCM_STREAM_PLAYBACK, stream_tag);
+       if (!stream)
+               return -EINVAL;
+
+       estream = stream_to_hdac_ext_stream(stream);
+       skl_dsp_setup_spib(dev, 0, stream_tag, false);
+       snd_hdac_ext_stream_release(estream, HDAC_EXT_STREAM_TYPE_HOST);
+
+       snd_hdac_dsp_cleanup(stream, dmab);
+
+       return 0;
+}
+
 static struct skl_dsp_loader_ops skl_get_loader_ops(void)
 {
        struct skl_dsp_loader_ops loader_ops;
@@ -84,6 +183,21 @@ static struct skl_dsp_loader_ops skl_get_loader_ops(void)
        return loader_ops;
 };
 
+static struct skl_dsp_loader_ops bxt_get_loader_ops(void)
+{
+       struct skl_dsp_loader_ops loader_ops;
+
+       memset(&loader_ops, 0, sizeof(loader_ops));
+
+       loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
+       loader_ops.free_dma_buf = skl_free_dma_buf;
+       loader_ops.prepare = skl_dsp_prepare;
+       loader_ops.trigger = skl_dsp_trigger;
+       loader_ops.cleanup = skl_dsp_cleanup;
+
+       return loader_ops;
+};
+
 static const struct skl_dsp_ops dsp_ops[] = {
        {
                .id = 0x9d70,
@@ -91,6 +205,12 @@ static const struct skl_dsp_ops dsp_ops[] = {
                .init = skl_sst_dsp_init,
                .cleanup = skl_sst_dsp_cleanup
        },
+       {
+               .id = 0x5a98,
+               .loader_ops = bxt_get_loader_ops,
+               .init = bxt_sst_dsp_init,
+               .cleanup = bxt_sst_dsp_cleanup
+       },
 };
 
 static int skl_get_dsp_ops(int pci_id)
@@ -744,7 +864,7 @@ int skl_init_module(struct skl_sst *ctx,
                return ret;
        }
        mconfig->m_state = SKL_MODULE_INIT_DONE;
-
+       kfree(param_data);
        return ret;
 }
 
index 14d1916ea9f843af75622cd50c93dcf5d426fd68..7d73648e5f9ab22570f67b5153fb74fd36d1985c 100644 (file)
@@ -25,11 +25,12 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
 
 #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
 
-void *skl_nhlt_init(struct device *dev)
+struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
 {
        acpi_handle handle;
        union acpi_object *obj;
        struct nhlt_resource_desc  *nhlt_ptr = NULL;
+       struct nhlt_acpi_table *nhlt_table = NULL;
 
        if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) {
                dev_err(dev, "Requested NHLT device not found\n");
@@ -39,18 +40,20 @@ void *skl_nhlt_init(struct device *dev)
        obj = acpi_evaluate_dsm(handle, OSC_UUID, 1, 1, NULL);
        if (obj && obj->type == ACPI_TYPE_BUFFER) {
                nhlt_ptr = (struct nhlt_resource_desc  *)obj->buffer.pointer;
-
-               return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+               nhlt_table = (struct nhlt_acpi_table *)
+                               memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
                                MEMREMAP_WB);
+               ACPI_FREE(obj);
+               return nhlt_table;
        }
 
        dev_err(dev, "device specific method to extract NHLT blob failed\n");
        return NULL;
 }
 
-void skl_nhlt_free(void *addr)
+void skl_nhlt_free(struct nhlt_acpi_table *nhlt)
 {
-       memunmap(addr);
+       memunmap((void *) nhlt);
 }
 
 static struct nhlt_specific_cfg *skl_get_specific_cfg(
@@ -120,7 +123,7 @@ struct nhlt_specific_cfg
        struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
        struct device *dev = bus->dev;
        struct nhlt_specific_cfg *sp_config;
-       struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+       struct nhlt_acpi_table *nhlt = skl->nhlt;
        u16 bps = (s_fmt == 16) ? 16 : 32;
        u8 j;
 
index dab0900eef26c13f6f9c8ac85d5f6b6a40cdf35b..7c81b31748ffcba2ab7c5d8a8961eb83d4134f55 100644 (file)
@@ -51,7 +51,7 @@ static struct snd_pcm_hardware azx_pcm_hw = {
        .rate_min =             8000,
        .rate_max =             48000,
        .channels_min =         1,
-       .channels_max =         HDA_QUAD,
+       .channels_max =         8,
        .buffer_bytes_max =     AZX_MAX_BUF_SIZE,
        .period_bytes_min =     128,
        .period_bytes_max =     AZX_MAX_BUF_SIZE / 2,
@@ -213,7 +213,7 @@ static int skl_be_prepare(struct snd_pcm_substream *substream,
        struct skl_sst *ctx = skl->skl_sst;
        struct skl_module_cfg *mconfig;
 
-       if ((dai->playback_active > 1) || (dai->capture_active > 1))
+       if (dai->playback_widget->power || dai->capture_widget->power)
                return 0;
 
        mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
@@ -402,23 +402,33 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
        struct skl_module_cfg *mconfig;
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
        struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+       struct snd_soc_dapm_widget *w;
        int ret;
 
        mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
        if (!mconfig)
                return -EIO;
 
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               w = dai->playback_widget;
+       else
+               w = dai->capture_widget;
+
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_RESUME:
-               skl_pcm_prepare(substream, dai);
-               /*
-                * enable DMA Resume enable bit for the stream, set the dpib
-                * & lpib position to resune before starting the DMA
-                */
-               snd_hdac_ext_stream_drsm_enable(ebus, true,
-                                       hdac_stream(stream)->index);
-               snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib);
-               snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
+               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
+                        * DMA
+                        */
+                       snd_hdac_ext_stream_drsm_enable(ebus, true,
+                                               hdac_stream(stream)->index);
+                       snd_hdac_ext_stream_set_dpibr(ebus, stream,
+                                                       stream->dpib);
+                       snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
+               }
 
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
@@ -448,7 +458,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
                        return ret;
 
                ret = skl_decoupled_trigger(substream, cmd);
-               if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
+               if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) {
                        /* save the dpib and lpib positions */
                        stream->dpib = readl(ebus->bus.remap_addr +
                                        AZX_REG_VS_SDXDPIB_XBASE +
@@ -523,7 +533,6 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
        if (!link)
                return -EINVAL;
 
-       snd_hdac_ext_bus_link_power_up(link);
        snd_hdac_ext_link_stream_reset(link_dev);
 
        snd_hdac_ext_link_stream_setup(link_dev, format_val);
@@ -682,7 +691,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        .playback = {
                .stream_name = "HDMI1 Playback",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
                        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
                        SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
@@ -697,7 +706,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        .playback = {
                .stream_name = "HDMI2 Playback",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
                        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
                        SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
@@ -712,7 +721,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        .playback = {
                .stream_name = "HDMI3 Playback",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
                        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
                        SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
@@ -759,13 +768,85 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
 },
+{
+       .name = "SSP2 Pin",
+       .ops = &skl_be_ssp_dai_ops,
+       .playback = {
+               .stream_name = "ssp2 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp2 Rx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "SSP3 Pin",
+       .ops = &skl_be_ssp_dai_ops,
+       .playback = {
+               .stream_name = "ssp3 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp3 Rx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "SSP4 Pin",
+       .ops = &skl_be_ssp_dai_ops,
+       .playback = {
+               .stream_name = "ssp4 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp4 Rx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "SSP5 Pin",
+       .ops = &skl_be_ssp_dai_ops,
+       .playback = {
+               .stream_name = "ssp5 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp5 Rx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
 {
        .name = "iDisp1 Pin",
        .ops = &skl_link_dai_ops,
        .playback = {
                .stream_name = "iDisp1 Tx",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
                        SNDRV_PCM_FMTBIT_S24_LE,
@@ -777,7 +858,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        .playback = {
                .stream_name = "iDisp2 Tx",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
                        SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
@@ -790,7 +871,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        .playback = {
                .stream_name = "iDisp3 Tx",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
                        SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
index 2962ef22fc84bf4d018505bfa8c0cc3b86f46f3c..13c19855ee1ac0cd57473fd2fa2f122b65d58b2f 100644 (file)
@@ -336,8 +336,6 @@ void skl_dsp_free(struct sst_dsp *dsp)
        skl_ipc_int_disable(dsp);
 
        free_irq(dsp->irq, dsp);
-       dsp->cl_dev.ops.cl_cleanup_controller(dsp);
-       skl_cldma_int_disable(dsp);
        skl_ipc_op_int_disable(dsp);
        skl_ipc_int_disable(dsp);
 
index b6e310d49dd682c239dcfcbeacebe875b365de40..deabe7308d3bc779389982b15bc3cea10d4d32b0 100644 (file)
@@ -118,16 +118,25 @@ struct skl_dsp_fw_ops {
        int (*set_state_D0)(struct sst_dsp *ctx);
        int (*set_state_D3)(struct sst_dsp *ctx);
        unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
-       int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, char *mod_name);
+       int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
        int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
 
 };
 
 struct skl_dsp_loader_ops {
+       int stream_tag;
+
        int (*alloc_dma_buf)(struct device *dev,
                struct snd_dma_buffer *dmab, size_t size);
        int (*free_dma_buf)(struct device *dev,
                struct snd_dma_buffer *dmab);
+       int (*prepare)(struct device *dev, unsigned int format,
+                               unsigned int byte_size,
+                               struct snd_dma_buffer *bufp);
+       int (*trigger)(struct device *dev, bool start, int stream_tag);
+
+       int (*cleanup)(struct device *dev, struct snd_dma_buffer *dmab,
+                                int stream_tag);
 };
 
 struct skl_load_module_info {
@@ -160,6 +169,10 @@ int skl_dsp_boot(struct sst_dsp *ctx);
 int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
                const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
                struct skl_sst **dsp);
+int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+               const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
+               struct skl_sst **dsp);
 void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
+void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
 
 #endif /*__SKL_SST_DSP_H__*/
index 348a734f8e24258a1782392a4d324b3b2a6dc794..13ec8d53b526d49e57c50e069fdc5ca076ec5fd1 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/uuid.h>
 #include "../common/sst-dsp.h"
 #include "../common/sst-dsp-priv.h"
 #include "../common/sst-ipc.h"
@@ -304,14 +305,16 @@ static int skl_transfer_module(struct sst_dsp *ctx,
        return ret;
 }
 
-static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, char *guid)
+static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
 {
        struct skl_module_table *module_entry = NULL;
        int ret = 0;
        char mod_name[64]; /* guid str = 32 chars + 4 hyphens */
+       uuid_le *uuid_mod;
 
-       snprintf(mod_name, sizeof(mod_name), "%s%s%s",
-                       "intel/dsp_fw_", guid, ".bin");
+       uuid_mod = (uuid_le *)guid;
+       snprintf(mod_name, sizeof(mod_name), "%s%pUL%s",
+                               "intel/dsp_fw_", uuid_mod, ".bin");
 
        module_entry = skl_module_get_from_id(ctx, mod_id);
        if (module_entry == NULL) {
@@ -451,6 +454,10 @@ void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
        skl_clear_module_table(ctx->dsp);
        skl_ipc_free(&ctx->ipc);
        ctx->dsp->ops->free(ctx->dsp);
+       if (ctx->boot_complete) {
+               ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
+               skl_cldma_int_disable(ctx->dsp);
+       }
 }
 EXPORT_SYMBOL_GPL(skl_sst_dsp_cleanup);
 
index cdb78b7e5a145d8f7a3ca0bd90c39f910643677f..3e036b0349b9771db5c16f0ce0b781fb4b8dca77 100644 (file)
@@ -154,13 +154,32 @@ static void skl_dump_mconfig(struct skl_sst *ctx,
        dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
 }
 
+static void skl_tplg_update_chmap(struct skl_module_fmt *fmt, int chs)
+{
+       int slot_map = 0xFFFFFFFF;
+       int start_slot = 0;
+       int i;
+
+       for (i = 0; i < chs; i++) {
+               /*
+                * For 2 channels with starting slot as 0, slot map will
+                * look like 0xFFFFFF10.
+                */
+               slot_map &= (~(0xF << (4 * i)) | (start_slot << (4 * i)));
+               start_slot++;
+       }
+       fmt->ch_map = slot_map;
+}
+
 static void skl_tplg_update_params(struct skl_module_fmt *fmt,
                        struct skl_pipe_params *params, int fixup)
 {
        if (fixup & SKL_RATE_FIXUP_MASK)
                fmt->s_freq = params->s_freq;
-       if (fixup & SKL_CH_FIXUP_MASK)
+       if (fixup & SKL_CH_FIXUP_MASK) {
                fmt->channels = params->ch;
+               skl_tplg_update_chmap(fmt, fmt->channels);
+       }
        if (fixup & SKL_FMT_FIXUP_MASK) {
                fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
 
@@ -1564,6 +1583,8 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
                return -ENOMEM;
 
        w->priv = mconfig;
+       memcpy(&mconfig->guid, &dfw_config->uuid, 16);
+
        mconfig->id.module_id = dfw_config->module_id;
        mconfig->id.instance_id = dfw_config->instance_id;
        mconfig->mcps = dfw_config->max_mcps;
@@ -1593,10 +1614,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
        mconfig->time_slot = dfw_config->time_slot;
        mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
 
-       if (dfw_config->is_loadable)
-               memcpy(mconfig->guid, dfw_config->uuid,
-                                       ARRAY_SIZE(dfw_config->uuid));
-
        mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
                                                sizeof(*mconfig->m_in_pin),
                                                GFP_KERNEL);
index d2d923002d5cfdaeba501d739e14ecc66fcb470b..e4b399cd7868fd85a4f79e48c01deb770e32f220 100644 (file)
@@ -281,7 +281,7 @@ enum skl_module_state {
 };
 
 struct skl_module_cfg {
-       char guid[SKL_UUID_STR_SZ];
+       u8 guid[16];
        struct skl_module_inst_id id;
        u8 domain;
        bool homogenous_inputs;
index 1db88a63ac1787f6862d62b53a141481dbb60cb8..a32e5e9cc5302f33e9d2bf1b7d60918fa383f4c1 100644 (file)
@@ -181,7 +181,7 @@ struct skl_dfw_pipe {
 } __packed;
 
 struct skl_dfw_module {
-       char uuid[SKL_UUID_STR_SZ];
+       u8 uuid[16];
 
        u16 module_id;
        u16 instance_id;
index 3982f5536f2d82b55837c4358e0b2754ac95e2c4..06d8c263c68f93342931fbcfe447cd986408f2f3 100644 (file)
@@ -229,7 +229,12 @@ static int skl_suspend(struct device *dev)
         * running, we need to save the state for these and continue
         */
        if (skl->supend_active) {
+               /* turn off the links and stop the CORB/RIRB DMA if it is On */
                snd_hdac_ext_bus_link_power_down_all(ebus);
+
+               if (ebus->cmd_dma_state)
+                       snd_hdac_bus_stop_cmd_io(&ebus->bus);
+
                enable_irq_wake(bus->irq);
                pci_save_state(pci);
                pci_disable_device(pci);
@@ -255,6 +260,7 @@ static int skl_resume(struct device *dev)
        struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
        struct skl *skl  = ebus_to_skl(ebus);
        struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct hdac_ext_link *hlink = NULL;
        int ret;
 
        /* Turned OFF in HDMI codec driver after codec reconfiguration */
@@ -276,8 +282,29 @@ static int skl_resume(struct device *dev)
                ret = pci_enable_device(pci);
                snd_hdac_ext_bus_link_power_up_all(ebus);
                disable_irq_wake(bus->irq);
+               /*
+                * turn On the links which are On before active suspend
+                * and start the CORB/RIRB DMA if On before
+                * active suspend.
+                */
+               list_for_each_entry(hlink, &ebus->hlink_list, list) {
+                       if (hlink->ref_count)
+                               snd_hdac_ext_bus_link_power_up(hlink);
+               }
+
+               if (ebus->cmd_dma_state)
+                       snd_hdac_bus_init_cmd_io(&ebus->bus);
        } else {
                ret = _skl_resume(ebus);
+
+               /* turn off the links which are off before suspend */
+               list_for_each_entry(hlink, &ebus->hlink_list, list) {
+                       if (!hlink->ref_count)
+                               snd_hdac_ext_bus_link_power_down(hlink);
+               }
+
+               if (!ebus->cmd_dma_state)
+                       snd_hdac_bus_stop_cmd_io(&ebus->bus);
        }
 
        return ret;
@@ -613,6 +640,7 @@ static int skl_probe(struct pci_dev *pci,
        struct skl *skl;
        struct hdac_ext_bus *ebus = NULL;
        struct hdac_bus *bus = NULL;
+       struct hdac_ext_link *hlink = NULL;
        int err;
 
        /* we use ext core ops, so provide NULL for ops here */
@@ -643,7 +671,7 @@ static int skl_probe(struct pci_dev *pci,
                err = skl_machine_device_register(skl,
                                  (void *)pci_id->driver_data);
                if (err < 0)
-                       goto out_free;
+                       goto out_nhlt_free;
 
                err = skl_init_dsp(skl);
                if (err < 0) {
@@ -679,6 +707,12 @@ static int skl_probe(struct pci_dev *pci,
                }
        }
 
+       /*
+        * we are done probling so decrement link counts
+        */
+       list_for_each_entry(hlink, &ebus->hlink_list, list)
+               snd_hdac_ext_bus_link_put(ebus, hlink);
+
        /*configure PM */
        pm_runtime_put_noidle(bus->dev);
        pm_runtime_allow(bus->dev);
@@ -693,6 +727,8 @@ out_dsp_free:
        skl_free_dsp(skl);
 out_mach_free:
        skl_machine_device_unregister(skl);
+out_nhlt_free:
+       skl_nhlt_free(skl->nhlt);
 out_free:
        skl->init_failed = 1;
        skl_free(ebus);
@@ -743,6 +779,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_free(skl->nhlt);
        skl_free(ebus);
        dev_set_drvdata(&pci->dev, NULL);
 }
index 39e16fa7a92b1a6921d8032ed279e88319061a4e..4b4b3876aea9a9ab6a70e2bce86d822c5c30168b 100644 (file)
@@ -66,7 +66,7 @@ struct skl {
        struct platform_device *dmic_dev;
        struct platform_device *i2s_dev;
 
-       void *nhlt; /* nhlt ptr */
+       struct nhlt_acpi_table *nhlt; /* nhlt ptr */
        struct skl_sst *skl_sst; /* sst skl ctx */
 
        struct skl_dsp_resource resource;
@@ -103,8 +103,8 @@ struct skl_dsp_ops {
 int skl_platform_unregister(struct device *dev);
 int skl_platform_register(struct device *dev);
 
-void *skl_nhlt_init(struct device *dev);
-void skl_nhlt_free(void *addr);
+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);
 
index 2f8e20416bd3aa08bf9178949c010905472a2414..574c6af28c068e164601794ec102c87b33ba40e1 100644 (file)
@@ -34,6 +34,13 @@ struct rk_i2s_dev {
 
        struct regmap *regmap;
 
+/*
+ * Used to indicate the tx/rx status.
+ * I2S controller hopes to start the tx and rx together,
+ * also to stop them when they are both try to stop.
+*/
+       bool tx_start;
+       bool rx_start;
        bool is_master_mode;
 };
 
@@ -75,29 +82,37 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
                                   I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
 
                regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_TXS_START,
-                                  I2S_XFER_TXS_START);
+                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START,
+                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
+
+               i2s->tx_start = true;
        } else {
+               i2s->tx_start = false;
+
                regmap_update_bits(i2s->regmap, I2S_DMACR,
                                   I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
 
-               regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_TXS_START,
-                                  I2S_XFER_TXS_STOP);
+               if (!i2s->rx_start) {
+                       regmap_update_bits(i2s->regmap, I2S_XFER,
+                                          I2S_XFER_TXS_START |
+                                          I2S_XFER_RXS_START,
+                                          I2S_XFER_TXS_STOP |
+                                          I2S_XFER_RXS_STOP);
 
-               regmap_update_bits(i2s->regmap, I2S_CLR,
-                                  I2S_CLR_TXC,
-                                  I2S_CLR_TXC);
+                       regmap_update_bits(i2s->regmap, I2S_CLR,
+                                          I2S_CLR_TXC | I2S_CLR_RXC,
+                                          I2S_CLR_TXC | I2S_CLR_RXC);
 
-               regmap_read(i2s->regmap, I2S_CLR, &val);
-
-               /* Should wait for clear operation to finish */
-               while (val & I2S_CLR_TXC) {
                        regmap_read(i2s->regmap, I2S_CLR, &val);
-                       retry--;
-                       if (!retry) {
-                               dev_warn(i2s->dev, "fail to clear\n");
-                               break;
+
+                       /* Should wait for clear operation to finish */
+                       while (val) {
+                               regmap_read(i2s->regmap, I2S_CLR, &val);
+                               retry--;
+                               if (!retry) {
+                                       dev_warn(i2s->dev, "fail to clear\n");
+                                       break;
+                               }
                        }
                }
        }
@@ -113,29 +128,37 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
                                   I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
 
                regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_RXS_START,
-                                  I2S_XFER_RXS_START);
+                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START,
+                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
+
+               i2s->rx_start = true;
        } else {
+               i2s->rx_start = false;
+
                regmap_update_bits(i2s->regmap, I2S_DMACR,
                                   I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
 
-               regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_RXS_START,
-                                  I2S_XFER_RXS_STOP);
+               if (!i2s->tx_start) {
+                       regmap_update_bits(i2s->regmap, I2S_XFER,
+                                          I2S_XFER_TXS_START |
+                                          I2S_XFER_RXS_START,
+                                          I2S_XFER_TXS_STOP |
+                                          I2S_XFER_RXS_STOP);
 
-               regmap_update_bits(i2s->regmap, I2S_CLR,
-                                  I2S_CLR_RXC,
-                                  I2S_CLR_RXC);
+                       regmap_update_bits(i2s->regmap, I2S_CLR,
+                                          I2S_CLR_TXC | I2S_CLR_RXC,
+                                          I2S_CLR_TXC | I2S_CLR_RXC);
 
-               regmap_read(i2s->regmap, I2S_CLR, &val);
-
-               /* Should wait for clear operation to finish */
-               while (val & I2S_CLR_RXC) {
                        regmap_read(i2s->regmap, I2S_CLR, &val);
-                       retry--;
-                       if (!retry) {
-                               dev_warn(i2s->dev, "fail to clear\n");
-                               break;
+
+                       /* Should wait for clear operation to finish */
+                       while (val) {
+                               regmap_read(i2s->regmap, I2S_CLR, &val);
+                               retry--;
+                               if (!retry) {
+                                       dev_warn(i2s->dev, "fail to clear\n");
+                                       break;
+                               }
                        }
                }
        }
index d2e62b159610bba4dce327a259affc62a92a6cde..16369cad480388c326f2463273e17ebef0324924 100644 (file)
@@ -930,7 +930,18 @@ static struct snd_soc_component *soc_find_component(
        return NULL;
 }
 
-static struct snd_soc_dai *snd_soc_find_dai(
+/**
+ * snd_soc_find_dai - Find a registered DAI
+ *
+ * @dlc: name of the DAI and optional component info to match
+ *
+ * This function will search all regsitered components and their DAIs to
+ * find the DAI of the same name. The component's of_node and name
+ * should also match if being specified.
+ *
+ * Return: pointer of DAI, or NULL if not found.
+ */
+struct snd_soc_dai *snd_soc_find_dai(
        const struct snd_soc_dai_link_component *dlc)
 {
        struct snd_soc_component *component;
@@ -959,6 +970,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
 
        return NULL;
 }
+EXPORT_SYMBOL_GPL(snd_soc_find_dai);
 
 static bool soc_is_dai_link_bound(struct snd_soc_card *card,
                struct snd_soc_dai_link *dai_link)
index 6fd1906af3873aa90b351649c2d0b8afe7152e9d..6cef3977507ae15c11ff074c5e33c0b275cef9c8 100644 (file)
@@ -163,31 +163,42 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
        }
 
        /*
-        * Prepare formats mask for valid/allowed sample types. If the dma does
-        * not have support for the given physical word size, it needs to be
-        * masked out so user space can not use the format which produces
-        * corrupted audio.
-        * In case the dma driver does not implement the slave_caps the default
-        * assumption is that it supports 1, 2 and 4 bytes widths.
+        * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep
+        * hw.formats set to 0, meaning no restrictions are in place.
+        * In this case it's the responsibility of the DAI driver to
+        * provide the supported format information.
         */
-       for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
-               int bits = snd_pcm_format_physical_width(i);
-
-               /* Enable only samples with DMA supported physical widths */
-               switch (bits) {
-               case 8:
-               case 16:
-               case 24:
-               case 32:
-               case 64:
-                       if (addr_widths & (1 << (bits / 8)))
-                               hw.formats |= (1LL << i);
-                       break;
-               default:
-                       /* Unsupported types */
-                       break;
+       if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK))
+               /*
+                * Prepare formats mask for valid/allowed sample types. If the
+                * dma does not have support for the given physical word size,
+                * it needs to be masked out so user space can not use the
+                * format which produces corrupted audio.
+                * In case the dma driver does not implement the slave_caps the
+                * default assumption is that it supports 1, 2 and 4 bytes
+                * widths.
+                */
+               for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
+                       int bits = snd_pcm_format_physical_width(i);
+
+                       /*
+                        * Enable only samples with DMA supported physical
+                        * widths
+                        */
+                       switch (bits) {
+                       case 8:
+                       case 16:
+                       case 24:
+                       case 32:
+                       case 64:
+                               if (addr_widths & (1 << (bits / 8)))
+                                       hw.formats |= (1LL << i);
+                               break;
+                       default:
+                               /* Unsupported types */
+                               break;
+                       }
                }
-       }
 
        return snd_soc_set_runtime_hwparams(substream, &hw);
 }