Merge tag 'sound-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 May 2016 20:41:32 +0000 (13:41 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 May 2016 20:41:32 +0000 (13:41 -0700)
Pull sound updates from Takashi Iwai:
 "This time was again a relatively calm development cycle; most of
  updates are about drivers, and no radical changes are seen in any core
  code.  Here are some highlights:

  ALSA core:
   - Continued hardening of ALSA hrtimer
   - A few leak fixes in timer interface
   - Fix poll error handling in PCM and compress
   - Add error propagation in compress API
   - Removal of dead rtctimer driver

  HD-audio:
   - Native ELD notify support for i915 HDMI
   - Realtek ALC234 & co support
   - Code refactoring to standardize chmap support
   - Continued development for SKL HDMI core support

  Firewire:
   - Apply delayed card registration to all drivers
   - Improved / stabilized the handling of PCM stream start / stop
   - Add tracepoints to dump a part of isochronous packet data
   - Fixed incoming/outgoing packet parameter usages
   - Add support for M-Audio profire series

  USB-audio:
   - Fixes for UAC2 clock source
   - SS+ support
   - Workaround for oft-seen repeated sample rate read errors

  ASoC:
   - Further slow progress on the topology code
   - Substantial updates and improvements for the da7219, es8328,
     fsl-ssi, Intel and rcar drivers.
   - Compress error handling in WM ADSP driver"

* tag 'sound-4.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (177 commits)
  ALSA: firewire-lib: change a member of event structure to suppress sparse wanings to bool type
  sound: oss: Use setup_timer and mod_timer.
  ASoC: hdac_hdmi: Remove the unused 'timeout' variable
  ASoC: fsl_ssi: Fix channel slipping on capture (or playback) restart in full duplex.
  ASoC: fsl_ssi: Fix channel slipping in Playback at startup
  ASoC: fsl_ssi: Fix samples being dropped at Playback startup
  ASoC: fsl_ssi: Save a dev reference for dev_err() purpose.
  ASoC: fsl_ssi: The IPG/5 limitation concerns the bitclk, not the sysclk.
  ASoC: fsl_ssi: Real hardware channels max number is 32
  ASoC: pcm5102a: Add support for PCM5102A codec
  ASoC: hdac_hdmi: add link management
  ASoC: Intel: Skylake: add link management
  ALSA: hdac: add link pm and ref counting
  ALSA: au88x0: Fix zero clear of stream->resources
  ASoC: rt298: Add DMI match for Broxton-P reference platform
  ASoC: rt298: fix null deref on acpi driver data
  ASoC: dapm: deprecate MICBIAS widget type
  ALSA: firewire-lib: drop skip argument from helper functions to queue a packet
  ALSA: firewire-lib: add context information to tracepoints
  ALSA: firewire-lib: permit to flush queued packets only in process context for better PCM period granularity
  ...

144 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]
Documentation/sound/alsa/HD-Audio.txt
Documentation/sound/alsa/compress_offload.txt
Documentation/sound/alsa/soc/dapm.txt
Documentation/sound/alsa/soc/overview.txt
Documentation/sound/alsa/timestamping.txt
MAINTAINERS
include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
include/sound/dmaengine_pcm.h
include/sound/hda_chmap.h
include/sound/hda_i915.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
include/uapi/sound/asound.h
sound/core/Kconfig
sound/core/Makefile
sound/core/compress_offload.c
sound/core/hrtimer.c
sound/core/pcm_dmaengine.c
sound/core/pcm_iec958.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/core/rtctimer.c [deleted file]
sound/core/seq/seq.c
sound/core/timer.c
sound/firewire/Kconfig
sound/firewire/Makefile
sound/firewire/amdtp-stream-trace.h [new file with mode: 0644]
sound/firewire/amdtp-stream.c
sound/firewire/amdtp-stream.h
sound/firewire/bebob/bebob.c
sound/firewire/bebob/bebob.h
sound/firewire/bebob/bebob_stream.c
sound/firewire/dice/dice.c
sound/firewire/digi00x/amdtp-dot.c
sound/firewire/digi00x/digi00x-transaction.c
sound/firewire/digi00x/digi00x.c
sound/firewire/digi00x/digi00x.h
sound/firewire/fireworks/fireworks.c
sound/firewire/fireworks/fireworks.h
sound/firewire/fireworks/fireworks_stream.c
sound/firewire/lib.c
sound/firewire/lib.h
sound/firewire/oxfw/oxfw-stream.c
sound/firewire/oxfw/oxfw.c
sound/firewire/oxfw/oxfw.h
sound/firewire/tascam/tascam-stream.c
sound/firewire/tascam/tascam.c
sound/firewire/tascam/tascam.h
sound/hda/ext/hdac_ext_bus.c
sound/hda/ext/hdac_ext_controller.c
sound/hda/hdac_controller.c
sound/hda/hdac_i915.c
sound/hda/hdmi_chmap.c
sound/hda/local.h
sound/isa/wavefront/wavefront_synth.c
sound/oss/waveartist.c
sound/pci/au88x0/au88x0_core.c
sound/pci/au88x0/au88x0_pcm.c
sound/pci/ctxfi/cttimer.c
sound/pci/ens1370.c
sound/pci/hda/Kconfig
sound/pci/hda/hda_generic.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/intel8x0.c
sound/pci/lx6464es/lx_core.c
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
sound/usb/card.c
sound/usb/clock.c
sound/usb/helper.c
sound/usb/midi.c
sound/usb/mixer.c
sound/usb/usbaudio.h

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 e7193aac669c3a7afc356276c167de3f887fc027..d4510ebf2e8c5bf35cc54885c7ea96d54e2e67c3 100644 (file)
@@ -655,17 +655,6 @@ development branches in general while the development for the current
 and next kernels are found in for-linus and for-next branches,
 respectively.
 
-If you are using the latest Linus tree, it'd be better to pull the
-above GIT tree onto it.  If you are using the older kernels, an easy
-way to try the latest ALSA code is to build from the snapshot
-tarball.  There are daily tarballs and the latest snapshot tarball.
-All can be built just like normal alsa-driver release packages, that
-is, installed via the usual spells: configure, make and make
-install(-modules).  See INSTALL in the package.  The snapshot tarballs
-are found at:
-
-- ftp://ftp.suse.com/pub/people/tiwai/snapshot/
-
 
 Sending a Bug Report
 ~~~~~~~~~~~~~~~~~~~~
@@ -699,7 +688,12 @@ problems.
 alsa-info
 ~~~~~~~~~
 The script `alsa-info.sh` is a very useful tool to gather the audio
-device information.  You can fetch the latest version from:
+device information.  It's included in alsa-utils package.  The latest
+version can be found on git repository:
+
+- git://git.alsa-project.org/alsa-utils.git
+
+The script can be fetched directly from the following URL, too:
 
 - http://www.alsa-project.org/alsa-info.sh
 
@@ -836,15 +830,11 @@ can get a proc-file dump at the current state, get a list of control
 (mixer) elements, set/get the control element value, simulate the PCM
 operation, the jack plugging simulation, etc.
 
-The package is found in:
-
-- ftp://ftp.suse.com/pub/people/tiwai/misc/
-
-A git repository is available:
+The program is found in the git repository below:
 
 - git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
 
-See README file in the tarball for more details about hda-emu
+See README file in the repository for more details about hda-emu
 program.
 
 
index 630c492c3dc2374d621b5c70d190b9100100402a..8ba556a131c37857e8056a91e0c929bf4564a00d 100644 (file)
@@ -149,7 +149,7 @@ Gapless Playback
 ================
 When playing thru an album, the decoders have the ability to skip the encoder
 delay and padding and directly move from one track content to another. The end
-user can perceive this as gapless playback as we dont have silence while
+user can perceive this as gapless playback as we don't have silence while
 switching from one track to another
 
 Also, there might be low-intensity noises due to encoding. Perfect gapless is
@@ -184,7 +184,7 @@ Sequence flow for gapless would be:
 - Fill data of the first track
 - Trigger start
 - User-space finished sending all,
-- Indicaite next track data by sending set_next_track
+- Indicate next track data by sending set_next_track
 - Set metadata of the next track
 - then call partial_drain to flush most of buffer in DSP
 - Fill data of the next track
index 6faab4880006d4c2721846623a07eb6de6661299..c45bd79f291eaddd0f2dba42d56c9a5f0d8b466b 100644 (file)
@@ -132,7 +132,7 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
 SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
        ARRAY_SIZE(wm8731_output_mixer_controls)),
 
-If you dont want the mixer elements prefixed with the name of the mixer widget,
+If you don't want the mixer elements prefixed with the name of the mixer widget,
 you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
 as for SND_SOC_DAPM_MIXER.
 
index ff88f52eec9849e254796b1c56d77a19236ea6a4..f3f28b7ae2420f575773a0703f80f1fa6289c75a 100644 (file)
@@ -63,7 +63,7 @@ multiple re-usable component drivers :-
     and any audio DSP drivers for that platform.
 
   * Machine class driver: The machine driver class acts as the glue that
-    decribes and binds the other component drivers together to form an ALSA
+    describes and binds the other component drivers together to form an ALSA
     "sound card device". It handles any machine specific controls and
     machine level audio events (e.g. turning on an amp at start of playback).
 
index 0b191a23f534d1bc5a0d54d6f7dfa6ae0ee090fd..1b6473f393a8220909fcbb0eb9a1e829a7e6854d 100644 (file)
@@ -129,7 +129,7 @@ will be required to issue multiple queries and perform an
 interpolation of the results
 
 In some hardware-specific configuration, the system timestamp is
-latched by a low-level audio subsytem, and the information provided
+latched by a low-level audio subsystem, and the information provided
 back to the driver. Due to potential delays in the communication with
 the hardware, there is a risk of misalignment with the avail and delay
 information. To make sure applications are not confused, a
index f825014a082a26e7b92b5550a21d0bc5971f0b77..0d4f9a1c7ce4d994f024b0ea445a82c2b26afaf4 100644 (file)
@@ -4732,6 +4732,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 5b08e3c5325f0a7d7bbc011ad157f96b6d7effc5..c8e0164c54236f9e32edf33b7e6c961a26540e2f 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 e20d219a030412527a57f5f2882d461412607ff3..babd445c75051fbaa9c18b337d897c75d676e237 100644 (file)
@@ -36,6 +36,8 @@ struct hdac_chmap_ops {
        int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
                        int channels, unsigned char *chmap);
 
+       int (*get_spk_alloc)(struct hdac_device *hdac, int pcm_idx);
+
        void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
                                        unsigned char *chmap);
        void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
index f5842bcd9c94ca633a2673d7a4bdceac76423364..796cabf6be5ee9da39a4a5347979b169c21bd633 100644 (file)
@@ -10,8 +10,8 @@
 int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable);
 int snd_hdac_display_power(struct hdac_bus *bus, bool enable);
 void snd_hdac_i915_set_bclk(struct hdac_bus *bus);
-int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate);
-int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate);
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
                           bool *audio_enabled, char *buffer, int max_bytes);
 int snd_hdac_i915_init(struct hdac_bus *bus);
 int snd_hdac_i915_exit(struct hdac_bus *bus);
@@ -28,12 +28,12 @@ static inline int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
 static inline void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
 {
 }
-static inline int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid,
-                                          int rate)
+static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec,
+                                          hda_nid_t nid, int rate)
 {
        return 0;
 }
-static inline int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
+static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
                                         bool *audio_enabled, char *buffer,
                                         int max_bytes)
 {
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 67bf49d8c94407ca6fe5547e7ce07ef992bab3fc..609cadb8739dbd143b6136d26cb7af2d7a0fc08a 100644 (file)
@@ -672,7 +672,7 @@ enum {
 
 /* global timers (device member) */
 #define SNDRV_TIMER_GLOBAL_SYSTEM      0
-#define SNDRV_TIMER_GLOBAL_RTC         1
+#define SNDRV_TIMER_GLOBAL_RTC         1       /* unused */
 #define SNDRV_TIMER_GLOBAL_HPET                2
 #define SNDRV_TIMER_GLOBAL_HRTIMER     3
 
index 6d12ca9bcb807d3a79a20f342eb15093ee8db9c9..9749f9e8b45c525d1560ddf467bf6a9cdcdffa72 100644 (file)
@@ -141,35 +141,6 @@ config SND_SEQ_HRTIMER_DEFAULT
          Say Y here to use the HR-timer backend as the default sequencer
          timer.
 
-config SND_RTCTIMER
-       tristate "RTC Timer support"
-       depends on RTC
-       select SND_TIMER
-       help
-         Say Y here to enable RTC timer support for ALSA.  ALSA uses
-         the RTC timer as a precise timing source and maps the RTC
-         timer to ALSA's timer interface.  The ALSA sequencer code also
-         can use this timing source.
-
-         To compile this driver as a module, choose M here: the module
-         will be called snd-rtctimer.
-
-         Note that this option is exclusive with the new RTC drivers
-         (CONFIG_RTC_CLASS) since this requires the old API.
-
-config SND_SEQ_RTCTIMER_DEFAULT
-       bool "Use RTC as default sequencer timer"
-       depends on SND_RTCTIMER && SND_SEQUENCER
-       depends on !SND_SEQ_HRTIMER_DEFAULT
-       default y
-       help
-         Say Y here to use the RTC timer as the default sequencer
-         timer.  This is strongly recommended because it ensures
-         precise MIDI timing even when the system timer runs at less
-         than 1000 Hz.
-
-         If in doubt, say Y.
-
 config SND_DYNAMIC_MINORS
        bool "Dynamic device file minor numbers"
        help
index 48ab4b8f82790809838c3a87535587636ea69ed7..e85d9dd12c2d7c379ce7234ab70d319c772736f7 100644 (file)
@@ -37,7 +37,6 @@ obj-$(CONFIG_SND)             += snd.o
 obj-$(CONFIG_SND_HWDEP)                += snd-hwdep.o
 obj-$(CONFIG_SND_TIMER)                += snd-timer.o
 obj-$(CONFIG_SND_HRTIMER)      += snd-hrtimer.o
-obj-$(CONFIG_SND_RTCTIMER)     += snd-rtctimer.o
 obj-$(CONFIG_SND_PCM)          += snd-pcm.o
 obj-$(CONFIG_SND_DMAENGINE_PCM)        += snd-pcm-dmaengine.o
 obj-$(CONFIG_SND_RAWMIDI)      += snd-rawmidi.o
index a9933c07a6bf542e38b54ec8ed70204fbc429d55..9b3334be9df23c8f48a8768ee8b723318252873f 100644 (file)
@@ -288,9 +288,12 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
        stream = &data->stream;
        mutex_lock(&stream->device->lock);
        /* write is allowed when stream is running or has been steup */
-       if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
-           stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
-                       stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
+       switch (stream->runtime->state) {
+       case SNDRV_PCM_STATE_SETUP:
+       case SNDRV_PCM_STATE_PREPARED:
+       case SNDRV_PCM_STATE_RUNNING:
+               break;
+       default:
                mutex_unlock(&stream->device->lock);
                return -EBADFD;
        }
@@ -391,14 +394,13 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
        int retval = 0;
 
        if (snd_BUG_ON(!data))
-               return -EFAULT;
+               return POLLERR;
+
        stream = &data->stream;
-       if (snd_BUG_ON(!stream))
-               return -EFAULT;
 
        mutex_lock(&stream->device->lock);
        if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
-               retval = -EBADFD;
+               retval = snd_compr_get_poll(stream) | POLLERR;
                goto out;
        }
        poll_wait(f, &stream->runtime->sleep, wait);
@@ -421,10 +423,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
                        retval = snd_compr_get_poll(stream);
                break;
        default:
-               if (stream->direction == SND_COMPRESS_PLAYBACK)
-                       retval = POLLOUT | POLLWRNORM | POLLERR;
-               else
-                       retval = POLLIN | POLLRDNORM | POLLERR;
+               retval = snd_compr_get_poll(stream) | POLLERR;
                break;
        }
 out:
@@ -802,9 +801,9 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
 
        if (snd_BUG_ON(!data))
                return -EFAULT;
+
        stream = &data->stream;
-       if (snd_BUG_ON(!stream))
-               return -EFAULT;
+
        mutex_lock(&stream->device->lock);
        switch (_IOC_NR(cmd)) {
        case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
index 656d9a9032dc2165d1f30f4ef193250a07bec555..e2f27022b363c482ba2614a2579f87f2b19192e3 100644 (file)
@@ -38,37 +38,53 @@ static unsigned int resolution;
 struct snd_hrtimer {
        struct snd_timer *timer;
        struct hrtimer hrt;
-       atomic_t running;
+       bool in_callback;
 };
 
 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
 {
        struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
        struct snd_timer *t = stime->timer;
-       unsigned long oruns;
-
-       if (!atomic_read(&stime->running))
-               return HRTIMER_NORESTART;
-
-       oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
-       snd_timer_interrupt(stime->timer, t->sticks * oruns);
+       ktime_t delta;
+       unsigned long ticks;
+       enum hrtimer_restart ret = HRTIMER_NORESTART;
+
+       spin_lock(&t->lock);
+       if (!t->running)
+               goto out; /* fast path */
+       stime->in_callback = true;
+       ticks = t->sticks;
+       spin_unlock(&t->lock);
+
+       /* calculate the drift */
+       delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
+       if (delta.tv64 > 0)
+               ticks += ktime_divns(delta, ticks * resolution);
+
+       snd_timer_interrupt(stime->timer, ticks);
+
+       spin_lock(&t->lock);
+       if (t->running) {
+               hrtimer_add_expires_ns(hrt, t->sticks * resolution);
+               ret = HRTIMER_RESTART;
+       }
 
-       if (!atomic_read(&stime->running))
-               return HRTIMER_NORESTART;
-       return HRTIMER_RESTART;
+       stime->in_callback = false;
+ out:
+       spin_unlock(&t->lock);
+       return ret;
 }
 
 static int snd_hrtimer_open(struct snd_timer *t)
 {
        struct snd_hrtimer *stime;
 
-       stime = kmalloc(sizeof(*stime), GFP_KERNEL);
+       stime = kzalloc(sizeof(*stime), GFP_KERNEL);
        if (!stime)
                return -ENOMEM;
        hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        stime->timer = t;
        stime->hrt.function = snd_hrtimer_callback;
-       atomic_set(&stime->running, 0);
        t->private_data = stime;
        return 0;
 }
@@ -78,6 +94,11 @@ static int snd_hrtimer_close(struct snd_timer *t)
        struct snd_hrtimer *stime = t->private_data;
 
        if (stime) {
+               spin_lock_irq(&t->lock);
+               t->running = 0; /* just to be sure */
+               stime->in_callback = 1; /* skip start/stop */
+               spin_unlock_irq(&t->lock);
+
                hrtimer_cancel(&stime->hrt);
                kfree(stime);
                t->private_data = NULL;
@@ -89,18 +110,19 @@ static int snd_hrtimer_start(struct snd_timer *t)
 {
        struct snd_hrtimer *stime = t->private_data;
 
-       atomic_set(&stime->running, 0);
-       hrtimer_try_to_cancel(&stime->hrt);
+       if (stime->in_callback)
+               return 0;
        hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
                      HRTIMER_MODE_REL);
-       atomic_set(&stime->running, 1);
        return 0;
 }
 
 static int snd_hrtimer_stop(struct snd_timer *t)
 {
        struct snd_hrtimer *stime = t->private_data;
-       atomic_set(&stime->running, 0);
+
+       if (stime->in_callback)
+               return 0;
        hrtimer_try_to_cancel(&stime->hrt);
        return 0;
 }
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 3a9b66c6e09c38933b63a4af6ee20e34efa09118..bb1261591a1f300e17f8cb27e36071e5b3fb28a8 100644 (file)
@@ -1886,8 +1886,8 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
                snd_timer_interrupt(substream->timer, 1);
 #endif
  _end:
-       snd_pcm_stream_unlock_irqrestore(substream, flags);
        kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
+       snd_pcm_stream_unlock_irqrestore(substream, flags);
 }
 
 EXPORT_SYMBOL(snd_pcm_period_elapsed);
@@ -2595,6 +2595,8 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
        };
        int err;
 
+       if (WARN_ON(pcm->streams[stream].chmap_kctl))
+               return -EBUSY;
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
index 9106d8e2300eab3e566f9a3807c469ee02aa7bcd..c61fd50f771f9ecd55c8e472d152321b37255e71 100644 (file)
@@ -3161,7 +3161,7 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
 
        substream = pcm_file->substream;
        if (PCM_RUNTIME_CHECK(substream))
-               return -ENXIO;
+               return POLLOUT | POLLWRNORM | POLLERR;
        runtime = substream->runtime;
 
        poll_wait(file, &runtime->sleep, wait);
@@ -3200,7 +3200,7 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
 
        substream = pcm_file->substream;
        if (PCM_RUNTIME_CHECK(substream))
-               return -ENXIO;
+               return POLLIN | POLLRDNORM | POLLERR;
        runtime = substream->runtime;
 
        poll_wait(file, &runtime->sleep, wait);
diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c
deleted file mode 100644 (file)
index f3420d1..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- *  RTC based high-frequency timer
- *
- *  Copyright (C) 2000 Takashi Iwai
- *     based on rtctimer.c by Steve Ratcliffe
- *
- *   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; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   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.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/log2.h>
-#include <sound/core.h>
-#include <sound/timer.h>
-
-#if IS_ENABLED(CONFIG_RTC)
-
-#include <linux/mc146818rtc.h>
-
-#define RTC_FREQ       1024            /* default frequency */
-#define NANO_SEC       1000000000L     /* 10^9 in sec */
-
-/*
- * prototypes
- */
-static int rtctimer_open(struct snd_timer *t);
-static int rtctimer_close(struct snd_timer *t);
-static int rtctimer_start(struct snd_timer *t);
-static int rtctimer_stop(struct snd_timer *t);
-
-
-/*
- * The hardware dependent description for this timer.
- */
-static struct snd_timer_hardware rtc_hw = {
-       .flags =        SNDRV_TIMER_HW_AUTO |
-                       SNDRV_TIMER_HW_FIRST |
-                       SNDRV_TIMER_HW_TASKLET,
-       .ticks =        100000000L,             /* FIXME: XXX */
-       .open =         rtctimer_open,
-       .close =        rtctimer_close,
-       .start =        rtctimer_start,
-       .stop =         rtctimer_stop,
-};
-
-static int rtctimer_freq = RTC_FREQ;           /* frequency */
-static struct snd_timer *rtctimer;
-static struct tasklet_struct rtc_tasklet;
-static rtc_task_t rtc_task;
-
-
-static int
-rtctimer_open(struct snd_timer *t)
-{
-       int err;
-
-       err = rtc_register(&rtc_task);
-       if (err < 0)
-               return err;
-       t->private_data = &rtc_task;
-       return 0;
-}
-
-static int
-rtctimer_close(struct snd_timer *t)
-{
-       rtc_task_t *rtc = t->private_data;
-       if (rtc) {
-               rtc_unregister(rtc);
-               tasklet_kill(&rtc_tasklet);
-               t->private_data = NULL;
-       }
-       return 0;
-}
-
-static int
-rtctimer_start(struct snd_timer *timer)
-{
-       rtc_task_t *rtc = timer->private_data;
-       if (snd_BUG_ON(!rtc))
-               return -EINVAL;
-       rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
-       rtc_control(rtc, RTC_PIE_ON, 0);
-       return 0;
-}
-
-static int
-rtctimer_stop(struct snd_timer *timer)
-{
-       rtc_task_t *rtc = timer->private_data;
-       if (snd_BUG_ON(!rtc))
-               return -EINVAL;
-       rtc_control(rtc, RTC_PIE_OFF, 0);
-       return 0;
-}
-
-static void rtctimer_tasklet(unsigned long data)
-{
-       snd_timer_interrupt((struct snd_timer *)data, 1);
-}
-
-/*
- * interrupt
- */
-static void rtctimer_interrupt(void *private_data)
-{
-       tasklet_schedule(private_data);
-}
-
-
-/*
- *  ENTRY functions
- */
-static int __init rtctimer_init(void)
-{
-       int err;
-       struct snd_timer *timer;
-
-       if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
-           !is_power_of_2(rtctimer_freq)) {
-               pr_err("ALSA: rtctimer: invalid frequency %d\n", rtctimer_freq);
-               return -EINVAL;
-       }
-
-       /* Create a new timer and set up the fields */
-       err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
-       if (err < 0)
-               return err;
-
-       timer->module = THIS_MODULE;
-       strcpy(timer->name, "RTC timer");
-       timer->hw = rtc_hw;
-       timer->hw.resolution = NANO_SEC / rtctimer_freq;
-
-       tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
-
-       /* set up RTC callback */
-       rtc_task.func = rtctimer_interrupt;
-       rtc_task.private_data = &rtc_tasklet;
-
-       err = snd_timer_global_register(timer);
-       if (err < 0) {
-               snd_timer_global_free(timer);
-               return err;
-       }
-       rtctimer = timer; /* remember this */
-
-       return 0;
-}
-
-static void __exit rtctimer_exit(void)
-{
-       if (rtctimer) {
-               snd_timer_global_free(rtctimer);
-               rtctimer = NULL;
-       }
-}
-
-
-/*
- * exported stuff
- */
-module_init(rtctimer_init)
-module_exit(rtctimer_exit)
-
-module_param(rtctimer_freq, int, 0444);
-MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
-
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
-
-#endif /* IS_ENABLED(CONFIG_RTC) */
index 7e0aabb808a66007d3719be8ca7885a611164ed3..639544b4fb044c3560196f451d5990c778f7a8e2 100644 (file)
@@ -47,8 +47,6 @@ int seq_default_timer_card = -1;
 int seq_default_timer_device =
 #ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
        SNDRV_TIMER_GLOBAL_HRTIMER
-#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
-       SNDRV_TIMER_GLOBAL_RTC
 #else
        SNDRV_TIMER_GLOBAL_SYSTEM
 #endif
index 6469bedda2f3c033a30007e910caf4c500aeaca7..e722022d325d7771d0d516c08ad830d814eeebdc 100644 (file)
@@ -37,8 +37,6 @@
 
 #if IS_ENABLED(CONFIG_SND_HRTIMER)
 #define DEFAULT_TIMER_LIMIT 4
-#elif IS_ENABLED(CONFIG_SND_RTCTIMER)
-#define DEFAULT_TIMER_LIMIT 2
 #else
 #define DEFAULT_TIMER_LIMIT 1
 #endif
@@ -1225,6 +1223,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
                tu->tstamp = *tstamp;
        if ((tu->filter & (1 << event)) == 0 || !tu->tread)
                return;
+       memset(&r1, 0, sizeof(r1));
        r1.event = event;
        r1.tstamp = *tstamp;
        r1.val = resolution;
@@ -1267,6 +1266,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
        }
        if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
            tu->last_resolution != resolution) {
+               memset(&r1, 0, sizeof(r1));
                r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
                r1.tstamp = tstamp;
                r1.val = resolution;
@@ -1739,6 +1739,7 @@ static int snd_timer_user_params(struct file *file,
        if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
                if (tu->tread) {
                        struct snd_timer_tread tread;
+                       memset(&tread, 0, sizeof(tread));
                        tread.event = SNDRV_TIMER_EVENT_EARLY;
                        tread.tstamp.tv_sec = 0;
                        tread.tstamp.tv_nsec = 0;
index 2a779c2f63abc7978842890b815c700b5ab50236..ab894ed1ff6717a8acde6581fef9842dbb8d5c35 100644 (file)
@@ -134,6 +134,7 @@ config SND_FIREWIRE_TASCAM
         Say Y here to include support for TASCAM.
          * FW-1884
          * FW-1082
+         * FW-1804
 
         To compile this driver as a module, choose M here: the module
         will be called snd-firewire-tascam.
index 003c09029786c592750b13b2c28c86c88b587b6f..0ee1fb115d8833bb076b74079de1b1222d8d98e3 100644 (file)
@@ -1,3 +1,6 @@
+# To find a header included by define_trace.h.
+CFLAGS_amdtp-stream.o  := -I$(src)
+
 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
                         fcp.o cmp.o amdtp-stream.o amdtp-am824.o
 snd-isight-objs := isight.o
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
new file mode 100644 (file)
index 0000000..9c04faf
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2016 Takashi Sakamoto
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM           snd_firewire_lib
+
+#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _AMDTP_STREAM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(in_packet,
+       TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index),
+       TP_ARGS(s, cycles, cip_header, payload_quadlets, index),
+       TP_STRUCT__entry(
+               __field(unsigned int, second)
+               __field(unsigned int, cycle)
+               __field(int, channel)
+               __field(int, src)
+               __field(int, dest)
+               __field(u32, cip_header0)
+               __field(u32, cip_header1)
+               __field(unsigned int, payload_quadlets)
+               __field(unsigned int, packet_index)
+               __field(unsigned int, irq)
+               __field(unsigned int, index)
+       ),
+       TP_fast_assign(
+               __entry->second = cycles / CYCLES_PER_SECOND;
+               __entry->cycle = cycles % CYCLES_PER_SECOND;
+               __entry->channel = s->context->channel;
+               __entry->src = fw_parent_device(s->unit)->node_id;
+               __entry->dest = fw_parent_device(s->unit)->card->node_id;
+               __entry->cip_header0 = cip_header[0];
+               __entry->cip_header1 = cip_header[1];
+               __entry->payload_quadlets = payload_quadlets;
+               __entry->packet_index = s->packet_index;
+               __entry->irq = !!in_interrupt();
+               __entry->index = index;
+       ),
+       TP_printk(
+               "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
+               __entry->second,
+               __entry->cycle,
+               __entry->src,
+               __entry->dest,
+               __entry->channel,
+               __entry->cip_header0,
+               __entry->cip_header1,
+               __entry->payload_quadlets,
+               __entry->packet_index,
+               __entry->irq,
+               __entry->index)
+);
+
+TRACE_EVENT(out_packet,
+       TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
+       TP_ARGS(s, cycles, cip_header, payload_length, index),
+       TP_STRUCT__entry(
+               __field(unsigned int, second)
+               __field(unsigned int, cycle)
+               __field(int, channel)
+               __field(int, src)
+               __field(int, dest)
+               __field(u32, cip_header0)
+               __field(u32, cip_header1)
+               __field(unsigned int, payload_quadlets)
+               __field(unsigned int, packet_index)
+               __field(unsigned int, irq)
+               __field(unsigned int, index)
+       ),
+       TP_fast_assign(
+               __entry->second = cycles / CYCLES_PER_SECOND;
+               __entry->cycle = cycles % CYCLES_PER_SECOND;
+               __entry->channel = s->context->channel;
+               __entry->src = fw_parent_device(s->unit)->card->node_id;
+               __entry->dest = fw_parent_device(s->unit)->node_id;
+               __entry->cip_header0 = be32_to_cpu(cip_header[0]);
+               __entry->cip_header1 = be32_to_cpu(cip_header[1]);
+               __entry->payload_quadlets = payload_length / 4;
+               __entry->packet_index = s->packet_index;
+               __entry->irq = !!in_interrupt();
+               __entry->index = index;
+       ),
+       TP_printk(
+               "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
+               __entry->second,
+               __entry->cycle,
+               __entry->src,
+               __entry->dest,
+               __entry->channel,
+               __entry->cip_header0,
+               __entry->cip_header1,
+               __entry->payload_quadlets,
+               __entry->packet_index,
+               __entry->irq,
+               __entry->index)
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH     .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE     amdtp-stream-trace
+#include <trace/define_trace.h>
index ed2902609a4c6cd0fd854e6ee29f6eaa866499c0..00060c4a9deb4ec239f34dc857ceebf75615d65c 100644 (file)
 #define CYCLES_PER_SECOND      8000
 #define TICKS_PER_SECOND       (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
 
+/* Always support Linux tracing subsystem. */
+#define CREATE_TRACE_POINTS
+#include "amdtp-stream-trace.h"
+
 #define TRANSFER_DELAY_TICKS   0x2e00 /* 479.17 microseconds */
 
 /* isochronous header parameters */
@@ -87,7 +91,6 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
 
        init_waitqueue_head(&s->callback_wait);
        s->callbacked = false;
-       s->sync_slave = NULL;
 
        s->fmt = fmt;
        s->process_data_blocks = process_data_blocks;
@@ -102,6 +105,10 @@ EXPORT_SYMBOL(amdtp_stream_init);
  */
 void amdtp_stream_destroy(struct amdtp_stream *s)
 {
+       /* Not initialized. */
+       if (s->protocol == NULL)
+               return;
+
        WARN_ON(amdtp_stream_running(s));
        kfree(s->protocol);
        mutex_destroy(&s->mutex);
@@ -244,7 +251,6 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
        tasklet_kill(&s->period_tasklet);
        s->pcm_buffer_pointer = 0;
        s->pcm_period_pointer = 0;
-       s->pointer_flush = true;
 }
 EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
 
@@ -349,7 +355,6 @@ static void update_pcm_pointers(struct amdtp_stream *s,
        s->pcm_period_pointer += frames;
        if (s->pcm_period_pointer >= pcm->runtime->period_size) {
                s->pcm_period_pointer -= pcm->runtime->period_size;
-               s->pointer_flush = false;
                tasklet_hi_schedule(&s->period_tasklet);
        }
 }
@@ -363,9 +368,8 @@ static void pcm_period_tasklet(unsigned long data)
                snd_pcm_period_elapsed(pcm);
 }
 
-static int queue_packet(struct amdtp_stream *s,
-                       unsigned int header_length,
-                       unsigned int payload_length, bool skip)
+static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
+                       unsigned int payload_length)
 {
        struct fw_iso_packet p = {0};
        int err = 0;
@@ -376,8 +380,10 @@ static int queue_packet(struct amdtp_stream *s,
        p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
        p.tag = TAG_CIP;
        p.header_length = header_length;
-       p.payload_length = (!skip) ? payload_length : 0;
-       p.skip = skip;
+       if (payload_length > 0)
+               p.payload_length = payload_length;
+       else
+               p.skip = true;
        err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
                                   s->buffer.packets[s->packet_index].offset);
        if (err < 0) {
@@ -392,27 +398,30 @@ end:
 }
 
 static inline int queue_out_packet(struct amdtp_stream *s,
-                                  unsigned int payload_length, bool skip)
+                                  unsigned int payload_length)
 {
-       return queue_packet(s, OUT_PACKET_HEADER_SIZE,
-                           payload_length, skip);
+       return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length);
 }
 
 static inline int queue_in_packet(struct amdtp_stream *s)
 {
        return queue_packet(s, IN_PACKET_HEADER_SIZE,
-                           amdtp_stream_get_max_payload(s), false);
+                           amdtp_stream_get_max_payload(s));
 }
 
-static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
-                            unsigned int syt)
+static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
+                            unsigned int index)
 {
        __be32 *buffer;
+       unsigned int syt;
+       unsigned int data_blocks;
        unsigned int payload_length;
        unsigned int pcm_frames;
        struct snd_pcm_substream *pcm;
 
        buffer = s->buffer.packets[s->packet_index].buffer;
+       syt = calculate_syt(s, cycle);
+       data_blocks = calculate_data_blocks(s, syt);
        pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
 
        buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
@@ -424,9 +433,11 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
                                (syt & CIP_SYT_MASK));
 
        s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
-
        payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
-       if (queue_out_packet(s, payload_length, false) < 0)
+
+       trace_out_packet(s, cycle, buffer, payload_length, index);
+
+       if (queue_out_packet(s, payload_length) < 0)
                return -EIO;
 
        pcm = ACCESS_ONCE(s->pcm);
@@ -438,19 +449,24 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
 }
 
 static int handle_in_packet(struct amdtp_stream *s,
-                           unsigned int payload_quadlets, __be32 *buffer,
-                           unsigned int *data_blocks, unsigned int syt)
+                           unsigned int payload_quadlets, unsigned int cycle,
+                           unsigned int index)
 {
+       __be32 *buffer;
        u32 cip_header[2];
-       unsigned int fmt, fdf;
+       unsigned int fmt, fdf, syt;
        unsigned int data_block_quadlets, data_block_counter, dbc_interval;
+       unsigned int data_blocks;
        struct snd_pcm_substream *pcm;
        unsigned int pcm_frames;
        bool lost;
 
+       buffer = s->buffer.packets[s->packet_index].buffer;
        cip_header[0] = be32_to_cpu(buffer[0]);
        cip_header[1] = be32_to_cpu(buffer[1]);
 
+       trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
+
        /*
         * This module supports 'Two-quadlet CIP header with SYT field'.
         * For convenience, also check FMT field is AM824 or not.
@@ -460,7 +476,7 @@ static int handle_in_packet(struct amdtp_stream *s,
                dev_info_ratelimited(&s->unit->device,
                                "Invalid CIP header for AMDTP: %08X:%08X\n",
                                cip_header[0], cip_header[1]);
-               *data_blocks = 0;
+               data_blocks = 0;
                pcm_frames = 0;
                goto end;
        }
@@ -471,7 +487,7 @@ static int handle_in_packet(struct amdtp_stream *s,
                dev_info_ratelimited(&s->unit->device,
                                     "Detect unexpected protocol: %08x %08x\n",
                                     cip_header[0], cip_header[1]);
-               *data_blocks = 0;
+               data_blocks = 0;
                pcm_frames = 0;
                goto end;
        }
@@ -480,7 +496,7 @@ static int handle_in_packet(struct amdtp_stream *s,
        fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
        if (payload_quadlets < 3 ||
            (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
-               *data_blocks = 0;
+               data_blocks = 0;
        } else {
                data_block_quadlets =
                        (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
@@ -494,12 +510,12 @@ static int handle_in_packet(struct amdtp_stream *s,
                if (s->flags & CIP_WRONG_DBS)
                        data_block_quadlets = s->data_block_quadlets;
 
-               *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+               data_blocks = (payload_quadlets - 2) / data_block_quadlets;
        }
 
        /* Check data block counter continuity */
        data_block_counter = cip_header[0] & CIP_DBC_MASK;
-       if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+       if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
            s->data_block_counter != UINT_MAX)
                data_block_counter = s->data_block_counter;
 
@@ -510,10 +526,10 @@ static int handle_in_packet(struct amdtp_stream *s,
        } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
                lost = data_block_counter != s->data_block_counter;
        } else {
-               if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
+               if (data_blocks > 0 && s->tx_dbc_interval > 0)
                        dbc_interval = s->tx_dbc_interval;
                else
-                       dbc_interval = *data_blocks;
+                       dbc_interval = data_blocks;
 
                lost = data_block_counter !=
                       ((s->data_block_counter + dbc_interval) & 0xff);
@@ -526,13 +542,14 @@ static int handle_in_packet(struct amdtp_stream *s,
                return -EIO;
        }
 
-       pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
+       syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+       pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
 
        if (s->flags & CIP_DBC_IS_END_EVENT)
                s->data_block_counter = data_block_counter;
        else
                s->data_block_counter =
-                               (data_block_counter + *data_blocks) & 0xff;
+                               (data_block_counter + data_blocks) & 0xff;
 end:
        if (queue_in_packet(s) < 0)
                return -EIO;
@@ -544,29 +561,50 @@ end:
        return 0;
 }
 
-static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+/*
+ * In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
+ * the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
+ * it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
+ */
+static inline u32 compute_cycle_count(u32 tstamp)
+{
+       return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
+}
+
+static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
+{
+       cycle += addend;
+       if (cycle >= 8 * CYCLES_PER_SECOND)
+               cycle -= 8 * CYCLES_PER_SECOND;
+       return cycle;
+}
+
+static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
+{
+       if (cycle < subtrahend)
+               cycle += 8 * CYCLES_PER_SECOND;
+       return cycle - subtrahend;
+}
+
+static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
                                size_t header_length, void *header,
                                void *private_data)
 {
        struct amdtp_stream *s = private_data;
-       unsigned int i, syt, packets = header_length / 4;
-       unsigned int data_blocks;
+       unsigned int i, packets = header_length / 4;
+       u32 cycle;
 
        if (s->packet_index < 0)
                return;
 
-       /*
-        * Compute the cycle of the last queued packet.
-        * (We need only the four lowest bits for the SYT, so we can ignore
-        * that bits 0-11 must wrap around at 3072.)
-        */
-       cycle += QUEUE_LENGTH - packets;
+       cycle = compute_cycle_count(tstamp);
 
-       for (i = 0; i < packets; ++i) {
-               syt = calculate_syt(s, ++cycle);
-               data_blocks = calculate_data_blocks(s, syt);
+       /* Align to actual cycle count for the last packet. */
+       cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
 
-               if (handle_out_packet(s, data_blocks, syt) < 0) {
+       for (i = 0; i < packets; ++i) {
+               cycle = increment_cycle_count(cycle, 1);
+               if (handle_out_packet(s, cycle, i) < 0) {
                        s->packet_index = -1;
                        amdtp_stream_pcm_abort(s);
                        return;
@@ -576,15 +614,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
        fw_iso_context_queue_flush(s->context);
 }
 
-static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
                               size_t header_length, void *header,
                               void *private_data)
 {
        struct amdtp_stream *s = private_data;
-       unsigned int p, syt, packets;
+       unsigned int i, packets;
        unsigned int payload_quadlets, max_payload_quadlets;
-       unsigned int data_blocks;
-       __be32 *buffer, *headers = header;
+       __be32 *headers = header;
+       u32 cycle;
 
        if (s->packet_index < 0)
                return;
@@ -592,70 +630,44 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
        /* The number of packets in buffer */
        packets = header_length / IN_PACKET_HEADER_SIZE;
 
+       cycle = compute_cycle_count(tstamp);
+
+       /* Align to actual cycle count for the last packet. */
+       cycle = decrement_cycle_count(cycle, packets);
+
        /* For buffer-over-run prevention. */
        max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
 
-       for (p = 0; p < packets; p++) {
-               buffer = s->buffer.packets[s->packet_index].buffer;
+       for (i = 0; i < packets; i++) {
+               cycle = increment_cycle_count(cycle, 1);
 
                /* The number of quadlets in this packet */
                payload_quadlets =
-                       (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+                       (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
                if (payload_quadlets > max_payload_quadlets) {
                        dev_err(&s->unit->device,
                                "Detect jumbo payload: %02x %02x\n",
                                payload_quadlets, max_payload_quadlets);
-                       s->packet_index = -1;
                        break;
                }
 
-               syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
-               if (handle_in_packet(s, payload_quadlets, buffer,
-                                               &data_blocks, syt) < 0) {
-                       s->packet_index = -1;
+               if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
                        break;
-               }
-
-               /* Process sync slave stream */
-               if (s->sync_slave && s->sync_slave->callbacked) {
-                       if (handle_out_packet(s->sync_slave,
-                                             data_blocks, syt) < 0) {
-                               s->packet_index = -1;
-                               break;
-                       }
-               }
        }
 
-       /* Queueing error or detecting discontinuity */
-       if (s->packet_index < 0) {
+       /* Queueing error or detecting invalid payload. */
+       if (i < packets) {
+               s->packet_index = -1;
                amdtp_stream_pcm_abort(s);
-
-               /* Abort sync slave. */
-               if (s->sync_slave) {
-                       s->sync_slave->packet_index = -1;
-                       amdtp_stream_pcm_abort(s->sync_slave);
-               }
                return;
        }
 
-       /* when sync to device, flush the packets for slave stream */
-       if (s->sync_slave && s->sync_slave->callbacked)
-               fw_iso_context_queue_flush(s->sync_slave->context);
-
        fw_iso_context_queue_flush(s->context);
 }
 
-/* processing is done by master callback */
-static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
-                                 size_t header_length, void *header,
-                                 void *private_data)
-{
-       return;
-}
-
 /* this is executed one time */
 static void amdtp_stream_first_callback(struct fw_iso_context *context,
-                                       u32 cycle, size_t header_length,
+                                       u32 tstamp, size_t header_length,
                                        void *header, void *private_data)
 {
        struct amdtp_stream *s = private_data;
@@ -669,12 +681,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
 
        if (s->direction == AMDTP_IN_STREAM)
                context->callback.sc = in_stream_callback;
-       else if (s->flags & CIP_SYNC_TO_DEVICE)
-               context->callback.sc = slave_stream_callback;
        else
                context->callback.sc = out_stream_callback;
 
-       context->callback.sc(context, cycle, header_length, header, s);
+       context->callback.sc(context, tstamp, header_length, header, s);
 }
 
 /**
@@ -713,8 +723,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
                goto err_unlock;
        }
 
-       if (s->direction == AMDTP_IN_STREAM &&
-           s->flags & CIP_SKIP_INIT_DBC_CHECK)
+       if (s->direction == AMDTP_IN_STREAM)
                s->data_block_counter = UINT_MAX;
        else
                s->data_block_counter = 0;
@@ -755,7 +764,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
                if (s->direction == AMDTP_IN_STREAM)
                        err = queue_in_packet(s);
                else
-                       err = queue_out_packet(s, 0, true);
+                       err = queue_out_packet(s, 0);
                if (err < 0)
                        goto err_context;
        } while (s->packet_index > 0);
@@ -794,11 +803,24 @@ EXPORT_SYMBOL(amdtp_stream_start);
  */
 unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
 {
-       /* this optimization is allowed to be racy */
-       if (s->pointer_flush && amdtp_stream_running(s))
+       /*
+        * This function is called in software IRQ context of period_tasklet or
+        * process context.
+        *
+        * When the software IRQ context was scheduled by software IRQ context
+        * of IR/IT contexts, queued packets were already handled. Therefore,
+        * no need to flush the queue in buffer anymore.
+        *
+        * When the process context reach here, some packets will be already
+        * queued in the buffer. These packets should be handled immediately
+        * to keep better granularity of PCM pointer.
+        *
+        * Later, the process context will sometimes schedules software IRQ
+        * context of the period_tasklet. Then, no need to flush the queue by
+        * the same reason as described for IR/IT contexts.
+        */
+       if (!in_interrupt() && amdtp_stream_running(s))
                fw_iso_context_flush_completions(s->context);
-       else
-               s->pointer_flush = true;
 
        return ACCESS_ONCE(s->pcm_buffer_pointer);
 }
index 8775704a3665ef9989e242a8bc152c24d6afb825..c1bc7fad056e82c62ee81e8f6a615f0a5d5fa85b 100644 (file)
@@ -17,8 +17,6 @@
  * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
  *     SYT_INTERVAL samples, with these two types alternating so that
  *     the overall sample rate comes out right.
- * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
- *     generated by in packets. Defaultly this driver generates timestamp.
  * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
  * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
  *     corresponds to the end of event in the packet. Out of IEC 61883.
@@ -26,8 +24,6 @@
  *     The value of data_block_quadlets is used instead of reported value.
  * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream.  Packets with zero in dbc is
  *     skipped for detecting discontinuity.
- * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
- *     packet is not continuous from an initial value.
  * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
  *     packet is wrong but the others are correct.
  * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
 enum cip_flags {
        CIP_NONBLOCKING         = 0x00,
        CIP_BLOCKING            = 0x01,
-       CIP_SYNC_TO_DEVICE      = 0x02,
-       CIP_EMPTY_WITH_TAG0     = 0x04,
-       CIP_DBC_IS_END_EVENT    = 0x08,
-       CIP_WRONG_DBS           = 0x10,
-       CIP_SKIP_DBC_ZERO_CHECK = 0x20,
-       CIP_SKIP_INIT_DBC_CHECK = 0x40,
-       CIP_EMPTY_HAS_WRONG_DBC = 0x80,
-       CIP_JUMBO_PAYLOAD       = 0x100,
+       CIP_EMPTY_WITH_TAG0     = 0x02,
+       CIP_DBC_IS_END_EVENT    = 0x04,
+       CIP_WRONG_DBS           = 0x08,
+       CIP_SKIP_DBC_ZERO_CHECK = 0x10,
+       CIP_EMPTY_HAS_WRONG_DBC = 0x20,
+       CIP_JUMBO_PAYLOAD       = 0x40,
 };
 
 /**
@@ -132,12 +126,10 @@ struct amdtp_stream {
        struct tasklet_struct period_tasklet;
        unsigned int pcm_buffer_pointer;
        unsigned int pcm_period_pointer;
-       bool pointer_flush;
 
        /* To wait for first packet. */
        bool callbacked;
        wait_queue_head_t callback_wait;
-       struct amdtp_stream *sync_slave;
 
        /* For backends to process data blocks. */
        void *protocol;
@@ -223,23 +215,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
        return sfc & 1;
 }
 
-static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
-                                        struct amdtp_stream *master,
-                                        struct amdtp_stream *slave)
-{
-       if (sync_mode == CIP_SYNC_TO_DEVICE) {
-               master->flags |= CIP_SYNC_TO_DEVICE;
-               slave->flags |= CIP_SYNC_TO_DEVICE;
-               master->sync_slave = slave;
-       } else {
-               master->flags &= ~CIP_SYNC_TO_DEVICE;
-               slave->flags &= ~CIP_SYNC_TO_DEVICE;
-               master->sync_slave = NULL;
-       }
-
-       slave->sync_slave = NULL;
-}
-
 /**
  * amdtp_stream_wait_callback - sleep till callbacked or timeout
  * @s: the AMDTP stream
index 3e4e0756e3febfeb90547f5f9085f13f6d948451..f7e2cbd2a3132b0cc52ff2ea3433477ce9a2cc04 100644 (file)
@@ -67,7 +67,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
 #define MODEL_MAUDIO_PROJECTMIX                0x00010091
 
 static int
-name_device(struct snd_bebob *bebob, unsigned int vendor_id)
+name_device(struct snd_bebob *bebob)
 {
        struct fw_device *fw_dev = fw_parent_device(bebob->unit);
        char vendor[24] = {0};
@@ -126,6 +126,17 @@ end:
        return err;
 }
 
+static void bebob_free(struct snd_bebob *bebob)
+{
+       snd_bebob_stream_destroy_duplex(bebob);
+       fw_unit_put(bebob->unit);
+
+       kfree(bebob->maudio_special_quirk);
+
+       mutex_destroy(&bebob->mutex);
+       kfree(bebob);
+}
+
 /*
  * This module releases the FireWire unit data after all ALSA character devices
  * are released by applications. This is for releasing stream data or finishing
@@ -137,18 +148,11 @@ bebob_card_free(struct snd_card *card)
 {
        struct snd_bebob *bebob = card->private_data;
 
-       snd_bebob_stream_destroy_duplex(bebob);
-       fw_unit_put(bebob->unit);
-
-       kfree(bebob->maudio_special_quirk);
-
-       if (bebob->card_index >= 0) {
-               mutex_lock(&devices_mutex);
-               clear_bit(bebob->card_index, devices_used);
-               mutex_unlock(&devices_mutex);
-       }
+       mutex_lock(&devices_mutex);
+       clear_bit(bebob->card_index, devices_used);
+       mutex_unlock(&devices_mutex);
 
-       mutex_destroy(&bebob->mutex);
+       bebob_free(card->private_data);
 }
 
 static const struct snd_bebob_spec *
@@ -176,16 +180,17 @@ check_audiophile_booted(struct fw_unit *unit)
        return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
 }
 
-static int
-bebob_probe(struct fw_unit *unit,
-           const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
 {
-       struct snd_card *card;
-       struct snd_bebob *bebob;
-       const struct snd_bebob_spec *spec;
+       struct snd_bebob *bebob =
+                       container_of(work, struct snd_bebob, dwork.work);
        unsigned int card_index;
        int err;
 
+       if (bebob->registered)
+               return;
+
        mutex_lock(&devices_mutex);
 
        for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
@@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit,
                        break;
        }
        if (card_index >= SNDRV_CARDS) {
-               err = -ENOENT;
-               goto end;
+               mutex_unlock(&devices_mutex);
+               return;
        }
 
-       if ((entry->vendor_id == VEN_FOCUSRITE) &&
-           (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
-               spec = get_saffire_spec(unit);
-       else if ((entry->vendor_id == VEN_MAUDIO1) &&
-                (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
-                !check_audiophile_booted(unit))
-               spec = NULL;
-       else
-               spec = (const struct snd_bebob_spec *)entry->driver_data;
-
-       if (spec == NULL) {
-               if ((entry->vendor_id == VEN_MAUDIO1) ||
-                   (entry->vendor_id == VEN_MAUDIO2))
-                       err = snd_bebob_maudio_load_firmware(unit);
-               else
-                       err = -ENOSYS;
-               goto end;
+       err = snd_card_new(&bebob->unit->device, index[card_index],
+                          id[card_index], THIS_MODULE, 0, &bebob->card);
+       if (err < 0) {
+               mutex_unlock(&devices_mutex);
+               return;
        }
 
-       err = snd_card_new(&unit->device, index[card_index], id[card_index],
-                          THIS_MODULE, sizeof(struct snd_bebob), &card);
+       err = name_device(bebob);
        if (err < 0)
-               goto end;
-       bebob = card->private_data;
-       bebob->card_index = card_index;
-       set_bit(card_index, devices_used);
-       card->private_free = bebob_card_free;
-
-       bebob->card = card;
-       bebob->unit = fw_unit_get(unit);
-       bebob->spec = spec;
-       mutex_init(&bebob->mutex);
-       spin_lock_init(&bebob->lock);
-       init_waitqueue_head(&bebob->hwdep_wait);
+               goto error;
 
-       err = name_device(bebob, entry->vendor_id);
+       if (bebob->spec == &maudio_special_spec) {
+               if (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
+                       err = snd_bebob_maudio_special_discover(bebob, true);
+               else
+                       err = snd_bebob_maudio_special_discover(bebob, false);
+       } else {
+               err = snd_bebob_stream_discover(bebob);
+       }
        if (err < 0)
                goto error;
 
-       if ((entry->vendor_id == VEN_MAUDIO1) &&
-           (entry->model_id == MODEL_MAUDIO_FW1814))
-               err = snd_bebob_maudio_special_discover(bebob, true);
-       else if ((entry->vendor_id == VEN_MAUDIO1) &&
-                (entry->model_id == MODEL_MAUDIO_PROJECTMIX))
-               err = snd_bebob_maudio_special_discover(bebob, false);
-       else
-               err = snd_bebob_stream_discover(bebob);
+       err = snd_bebob_stream_init_duplex(bebob);
        if (err < 0)
                goto error;
 
        snd_bebob_proc_init(bebob);
 
-       if ((bebob->midi_input_ports > 0) ||
-           (bebob->midi_output_ports > 0)) {
+       if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
                err = snd_bebob_create_midi_devices(bebob);
                if (err < 0)
                        goto error;
@@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
-       err = snd_bebob_stream_init_duplex(bebob);
+       err = snd_card_register(bebob->card);
        if (err < 0)
                goto error;
 
-       if (!bebob->maudio_special_quirk) {
-               err = snd_card_register(card);
-               if (err < 0) {
-                       snd_bebob_stream_destroy_duplex(bebob);
-                       goto error;
-               }
+       set_bit(card_index, devices_used);
+       mutex_unlock(&devices_mutex);
+
+       /*
+        * After registered, bebob instance can be released corresponding to
+        * releasing the sound card instance.
+        */
+       bebob->card->private_free = bebob_card_free;
+       bebob->card->private_data = bebob;
+       bebob->registered = true;
+
+       return;
+error:
+       mutex_unlock(&devices_mutex);
+       snd_bebob_stream_destroy_duplex(bebob);
+       snd_card_free(bebob->card);
+       dev_info(&bebob->unit->device,
+                "Sound card registration failed: %d\n", err);
+}
+
+static int
+bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+       struct snd_bebob *bebob;
+       const struct snd_bebob_spec *spec;
+
+       if (entry->vendor_id == VEN_FOCUSRITE &&
+           entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
+               spec = get_saffire_spec(unit);
+       else if (entry->vendor_id == VEN_MAUDIO1 &&
+                entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
+                !check_audiophile_booted(unit))
+               spec = NULL;
+       else
+               spec = (const struct snd_bebob_spec *)entry->driver_data;
+
+       if (spec == NULL) {
+               if (entry->vendor_id == VEN_MAUDIO1 ||
+                   entry->vendor_id == VEN_MAUDIO2)
+                       return snd_bebob_maudio_load_firmware(unit);
+               else
+                       return -ENODEV;
+       }
+
+       /* Allocate this independent of sound card instance. */
+       bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL);
+       if (bebob == NULL)
+               return -ENOMEM;
+
+       bebob->unit = fw_unit_get(unit);
+       bebob->entry = entry;
+       bebob->spec = spec;
+       dev_set_drvdata(&unit->device, bebob);
+
+       mutex_init(&bebob->mutex);
+       spin_lock_init(&bebob->lock);
+       init_waitqueue_head(&bebob->hwdep_wait);
+
+       /* Allocate and register this sound card later. */
+       INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration);
+
+       if (entry->vendor_id != VEN_MAUDIO1 ||
+           (entry->model_id != MODEL_MAUDIO_FW1814 &&
+            entry->model_id != MODEL_MAUDIO_PROJECTMIX)) {
+               snd_fw_schedule_registration(unit, &bebob->dwork);
        } else {
                /*
                 * This is a workaround. This bus reset seems to have an effect
@@ -285,19 +324,11 @@ bebob_probe(struct fw_unit *unit,
                 * signals from dbus and starts I/Os. To avoid I/Os till the
                 * future bus reset, registration is done in next update().
                 */
-               bebob->deferred_registration = true;
                fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
                                      false, true);
        }
 
-       dev_set_drvdata(&unit->device, bebob);
-end:
-       mutex_unlock(&devices_mutex);
-       return err;
-error:
-       mutex_unlock(&devices_mutex);
-       snd_card_free(card);
-       return err;
+       return 0;
 }
 
 /*
@@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit)
        if (bebob == NULL)
                return;
 
-       fcp_bus_reset(bebob->unit);
-
-       if (bebob->deferred_registration) {
-               if (snd_card_register(bebob->card) < 0) {
-                       snd_bebob_stream_destroy_duplex(bebob);
-                       snd_card_free(bebob->card);
-               }
-               bebob->deferred_registration = false;
-       }
+       /* Postpone a workqueue for deferred registration. */
+       if (!bebob->registered)
+               snd_fw_schedule_registration(unit, &bebob->dwork);
+       else
+               fcp_bus_reset(bebob->unit);
 }
 
 static void bebob_remove(struct fw_unit *unit)
@@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit)
        if (bebob == NULL)
                return;
 
-       /* No need to wait for releasing card object in this context. */
-       snd_card_free_when_closed(bebob->card);
+       /*
+        * Confirm to stop the work for registration before the sound card is
+        * going to be released. The work is not scheduled again because bus
+        * reset handler is not called anymore.
+        */
+       cancel_delayed_work_sync(&bebob->dwork);
+
+       if (bebob->registered) {
+               /* No need to wait for releasing card object in this context. */
+               snd_card_free_when_closed(bebob->card);
+       } else {
+               /* Don't forget this case. */
+               bebob_free(bebob);
+       }
 }
 
 static const struct snd_bebob_rate_spec normal_rate_spec = {
index b50bb33d9d46616b1186143b03495b309967f770..e7f1bb925b12f9e19d76a1c4e563a522473a8436 100644 (file)
@@ -83,6 +83,10 @@ struct snd_bebob {
        struct mutex mutex;
        spinlock_t lock;
 
+       bool registered;
+       struct delayed_work dwork;
+
+       const struct ieee1394_device_id *entry;
        const struct snd_bebob_spec *spec;
 
        unsigned int midi_input_ports;
@@ -90,7 +94,6 @@ struct snd_bebob {
 
        bool connected;
 
-       struct amdtp_stream *master;
        struct amdtp_stream tx_stream;
        struct amdtp_stream rx_stream;
        struct cmp_connection out_conn;
@@ -111,7 +114,6 @@ struct snd_bebob {
 
        /* for M-Audio special devices */
        void *maudio_special_quirk;
-       bool deferred_registration;
 
        /* For BeBoB version quirk. */
        unsigned int version;
index 77cbb02bff342271555398bbd582ab5a4a578bed..4d3034a68bdfbd587163566484eea57804260eb7 100644 (file)
@@ -483,30 +483,6 @@ destroy_both_connections(struct snd_bebob *bebob)
        cmp_connection_destroy(&bebob->out_conn);
 }
 
-static int
-get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
-{
-       enum snd_bebob_clock_type src;
-       int err;
-
-       err = snd_bebob_stream_get_clock_src(bebob, &src);
-       if (err < 0)
-               return err;
-
-       switch (src) {
-       case SND_BEBOB_CLOCK_TYPE_INTERNAL:
-       case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
-               *sync_mode = CIP_SYNC_TO_DEVICE;
-               break;
-       default:
-       case SND_BEBOB_CLOCK_TYPE_SYT:
-               *sync_mode = 0;
-               break;
-       }
-
-       return 0;
-}
-
 static int
 start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
             unsigned int rate)
@@ -550,8 +526,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
                goto end;
        }
 
-       bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
-
        /*
         * BeBoB v3 transfers packets with these qurks:
         *  - In the beginning of streaming, the value of dbc is incremented
@@ -584,8 +558,6 @@ end:
 int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
 {
        const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
-       struct amdtp_stream *master, *slave;
-       enum cip_flags sync_mode;
        unsigned int curr_rate;
        int err = 0;
 
@@ -593,22 +565,11 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
        if (bebob->substreams_counter == 0)
                goto end;
 
-       err = get_sync_mode(bebob, &sync_mode);
-       if (err < 0)
-               goto end;
-       if (sync_mode == CIP_SYNC_TO_DEVICE) {
-               master = &bebob->tx_stream;
-               slave  = &bebob->rx_stream;
-       } else {
-               master = &bebob->rx_stream;
-               slave  = &bebob->tx_stream;
-       }
-
        /*
         * Considering JACK/FFADO streaming:
         * TODO: This can be removed hwdep functionality becomes popular.
         */
-       err = check_connection_used_by_others(bebob, master);
+       err = check_connection_used_by_others(bebob, &bebob->rx_stream);
        if (err < 0)
                goto end;
 
@@ -618,11 +579,12 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
         * At bus reset, connections should not be broken here. So streams need
         * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
         */
-       if (amdtp_streaming_error(master))
-               amdtp_stream_stop(master);
-       if (amdtp_streaming_error(slave))
-               amdtp_stream_stop(slave);
-       if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
+       if (amdtp_streaming_error(&bebob->rx_stream))
+               amdtp_stream_stop(&bebob->rx_stream);
+       if (amdtp_streaming_error(&bebob->tx_stream))
+               amdtp_stream_stop(&bebob->tx_stream);
+       if (!amdtp_stream_running(&bebob->rx_stream) &&
+           !amdtp_stream_running(&bebob->tx_stream))
                break_both_connections(bebob);
 
        /* stop streams if rate is different */
@@ -635,16 +597,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
        if (rate == 0)
                rate = curr_rate;
        if (rate != curr_rate) {
-               amdtp_stream_stop(master);
-               amdtp_stream_stop(slave);
+               amdtp_stream_stop(&bebob->rx_stream);
+               amdtp_stream_stop(&bebob->tx_stream);
                break_both_connections(bebob);
        }
 
        /* master should be always running */
-       if (!amdtp_stream_running(master)) {
-               amdtp_stream_set_sync(sync_mode, master, slave);
-               bebob->master = master;
-
+       if (!amdtp_stream_running(&bebob->rx_stream)) {
                /*
                 * NOTE:
                 * If establishing connections at first, Yamaha GO46
@@ -666,7 +625,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
                if (err < 0)
                        goto end;
 
-               err = start_stream(bebob, master, rate);
+               err = start_stream(bebob, &bebob->rx_stream, rate);
                if (err < 0) {
                        dev_err(&bebob->unit->device,
                                "fail to run AMDTP master stream:%d\n", err);
@@ -685,15 +644,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
                                dev_err(&bebob->unit->device,
                                        "fail to ensure sampling rate: %d\n",
                                        err);
-                               amdtp_stream_stop(master);
+                               amdtp_stream_stop(&bebob->rx_stream);
                                break_both_connections(bebob);
                                goto end;
                        }
                }
 
                /* wait first callback */
-               if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
-                       amdtp_stream_stop(master);
+               if (!amdtp_stream_wait_callback(&bebob->rx_stream,
+                                               CALLBACK_TIMEOUT)) {
+                       amdtp_stream_stop(&bebob->rx_stream);
                        break_both_connections(bebob);
                        err = -ETIMEDOUT;
                        goto end;
@@ -701,20 +661,21 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
        }
 
        /* start slave if needed */
-       if (!amdtp_stream_running(slave)) {
-               err = start_stream(bebob, slave, rate);
+       if (!amdtp_stream_running(&bebob->tx_stream)) {
+               err = start_stream(bebob, &bebob->tx_stream, rate);
                if (err < 0) {
                        dev_err(&bebob->unit->device,
                                "fail to run AMDTP slave stream:%d\n", err);
-                       amdtp_stream_stop(master);
+                       amdtp_stream_stop(&bebob->rx_stream);
                        break_both_connections(bebob);
                        goto end;
                }
 
                /* wait first callback */
-               if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
-                       amdtp_stream_stop(slave);
-                       amdtp_stream_stop(master);
+               if (!amdtp_stream_wait_callback(&bebob->tx_stream,
+                                               CALLBACK_TIMEOUT)) {
+                       amdtp_stream_stop(&bebob->tx_stream);
+                       amdtp_stream_stop(&bebob->rx_stream);
                        break_both_connections(bebob);
                        err = -ETIMEDOUT;
                }
@@ -725,22 +686,12 @@ end:
 
 void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
 {
-       struct amdtp_stream *master, *slave;
-
-       if (bebob->master == &bebob->rx_stream) {
-               slave  = &bebob->tx_stream;
-               master = &bebob->rx_stream;
-       } else {
-               slave  = &bebob->rx_stream;
-               master = &bebob->tx_stream;
-       }
-
        if (bebob->substreams_counter == 0) {
-               amdtp_stream_pcm_abort(master);
-               amdtp_stream_stop(master);
+               amdtp_stream_pcm_abort(&bebob->rx_stream);
+               amdtp_stream_stop(&bebob->rx_stream);
 
-               amdtp_stream_pcm_abort(slave);
-               amdtp_stream_stop(slave);
+               amdtp_stream_pcm_abort(&bebob->tx_stream);
+               amdtp_stream_stop(&bebob->tx_stream);
 
                break_both_connections(bebob);
        }
index 8b64aef31a864523d428e6fa26b8c6a5f45db154..25e9f77275c4dd6ef6a39da8e9f77294f86f876c 100644 (file)
@@ -20,8 +20,6 @@ MODULE_LICENSE("GPL v2");
 #define WEISS_CATEGORY_ID      0x00
 #define LOUD_CATEGORY_ID       0x10
 
-#define PROBE_DELAY_MS         (2 * MSEC_PER_SEC)
-
 /*
  * Some models support several isochronous channels, while these streams are not
  * always available. In this case, add the model name to this list.
@@ -201,6 +199,10 @@ static void do_registration(struct work_struct *work)
 
        dice_card_strings(dice);
 
+       err = snd_dice_stream_init_duplex(dice);
+       if (err < 0)
+               goto error;
+
        snd_dice_create_proc(dice);
 
        err = snd_dice_create_pcm(dice);
@@ -229,28 +231,14 @@ static void do_registration(struct work_struct *work)
 
        return;
 error:
+       snd_dice_stream_destroy_duplex(dice);
        snd_dice_transaction_destroy(dice);
+       snd_dice_stream_destroy_duplex(dice);
        snd_card_free(dice->card);
        dev_info(&dice->unit->device,
                 "Sound card registration failed: %d\n", err);
 }
 
-static void schedule_registration(struct snd_dice *dice)
-{
-       struct fw_card *fw_card = fw_parent_device(dice->unit)->card;
-       u64 now, delay;
-
-       now = get_jiffies_64();
-       delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS);
-
-       if (time_after64(delay, now))
-               delay -= now;
-       else
-               delay = 0;
-
-       mod_delayed_work(system_wq, &dice->dwork, delay);
-}
-
 static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 {
        struct snd_dice *dice;
@@ -273,15 +261,9 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
        init_completion(&dice->clock_accepted);
        init_waitqueue_head(&dice->hwdep_wait);
 
-       err = snd_dice_stream_init_duplex(dice);
-       if (err < 0) {
-               dice_free(dice);
-               return err;
-       }
-
        /* Allocate and register this sound card later. */
        INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
-       schedule_registration(dice);
+       snd_fw_schedule_registration(unit, &dice->dwork);
 
        return 0;
 }
@@ -312,7 +294,7 @@ static void dice_bus_reset(struct fw_unit *unit)
 
        /* Postpone a workqueue for deferred registration. */
        if (!dice->registered)
-               schedule_registration(dice);
+               snd_fw_schedule_registration(unit, &dice->dwork);
 
        /* The handler address register becomes initialized. */
        snd_dice_transaction_reinit(dice);
@@ -335,6 +317,13 @@ static const struct ieee1394_device_id dice_id_table[] = {
                .match_flags = IEEE1394_MATCH_VERSION,
                .version     = DICE_INTERFACE,
        },
+       /* M-Audio Profire 610/2626 has a different value in version field. */
+       {
+               .match_flags    = IEEE1394_MATCH_VENDOR_ID |
+                                 IEEE1394_MATCH_SPECIFIER_ID,
+               .vendor_id      = 0x000d6c,
+               .specifier_id   = 0x000d6c,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
index 0ac92aba5bc1c9a4c01b1764a6f16a098e3bbae1..b3cffd01a19f711f3572920a33fa75098f39ffde 100644 (file)
@@ -421,7 +421,7 @@ int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
 
        /* Use different mode between incoming/outgoing. */
        if (dir == AMDTP_IN_STREAM) {
-               flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+               flags = CIP_NONBLOCKING;
                process_data_blocks = process_tx_data_blocks;
        } else {
                flags = CIP_BLOCKING;
index 554324d8c602189e58efc9aebeac1707c3f5cf97..735d35640807c581ea43a6f4152348a549c0427d 100644 (file)
@@ -126,12 +126,17 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
        return err;
 error:
        fw_core_remove_address_handler(&dg00x->async_handler);
-       dg00x->async_handler.address_callback = NULL;
+       dg00x->async_handler.callback_data = NULL;
        return err;
 }
 
 void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
 {
+       if (dg00x->async_handler.callback_data == NULL)
+               return;
+
        snd_fw_async_midi_port_destroy(&dg00x->out_control);
        fw_core_remove_address_handler(&dg00x->async_handler);
+
+       dg00x->async_handler.callback_data = NULL;
 }
index 1f33b7a1fca4c3695cc663789d7eeeff135c2a83..cc4776c6ded313673ef1c767101f2ed7e0c4369f 100644 (file)
@@ -40,10 +40,8 @@ static int name_card(struct snd_dg00x *dg00x)
        return 0;
 }
 
-static void dg00x_card_free(struct snd_card *card)
+static void dg00x_free(struct snd_dg00x *dg00x)
 {
-       struct snd_dg00x *dg00x = card->private_data;
-
        snd_dg00x_stream_destroy_duplex(dg00x);
        snd_dg00x_transaction_unregister(dg00x);
 
@@ -52,28 +50,24 @@ static void dg00x_card_free(struct snd_card *card)
        mutex_destroy(&dg00x->mutex);
 }
 
-static int snd_dg00x_probe(struct fw_unit *unit,
-                          const struct ieee1394_device_id *entry)
+static void dg00x_card_free(struct snd_card *card)
 {
-       struct snd_card *card;
-       struct snd_dg00x *dg00x;
-       int err;
+       dg00x_free(card->private_data);
+}
 
-       /* create card */
-       err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
-                          sizeof(struct snd_dg00x), &card);
-       if (err < 0)
-               return err;
-       card->private_free = dg00x_card_free;
+static void do_registration(struct work_struct *work)
+{
+       struct snd_dg00x *dg00x =
+                       container_of(work, struct snd_dg00x, dwork.work);
+       int err;
 
-       /* initialize myself */
-       dg00x = card->private_data;
-       dg00x->card = card;
-       dg00x->unit = fw_unit_get(unit);
+       if (dg00x->registered)
+               return;
 
-       mutex_init(&dg00x->mutex);
-       spin_lock_init(&dg00x->lock);
-       init_waitqueue_head(&dg00x->hwdep_wait);
+       err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
+                          &dg00x->card);
+       if (err < 0)
+               return;
 
        err = name_card(dg00x);
        if (err < 0)
@@ -101,35 +95,86 @@ static int snd_dg00x_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
-       err = snd_card_register(card);
+       err = snd_card_register(dg00x->card);
        if (err < 0)
                goto error;
 
-       dev_set_drvdata(&unit->device, dg00x);
+       dg00x->card->private_free = dg00x_card_free;
+       dg00x->card->private_data = dg00x;
+       dg00x->registered = true;
 
-       return err;
+       return;
 error:
-       snd_card_free(card);
-       return err;
+       snd_dg00x_transaction_unregister(dg00x);
+       snd_dg00x_stream_destroy_duplex(dg00x);
+       snd_card_free(dg00x->card);
+       dev_info(&dg00x->unit->device,
+                "Sound card registration failed: %d\n", err);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+                          const struct ieee1394_device_id *entry)
+{
+       struct snd_dg00x *dg00x;
+
+       /* Allocate this independent of sound card instance. */
+       dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL);
+       if (dg00x == NULL)
+               return -ENOMEM;
+
+       dg00x->unit = fw_unit_get(unit);
+       dev_set_drvdata(&unit->device, dg00x);
+
+       mutex_init(&dg00x->mutex);
+       spin_lock_init(&dg00x->lock);
+       init_waitqueue_head(&dg00x->hwdep_wait);
+
+       /* Allocate and register this sound card later. */
+       INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
+       snd_fw_schedule_registration(unit, &dg00x->dwork);
+
+       return 0;
 }
 
 static void snd_dg00x_update(struct fw_unit *unit)
 {
        struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
 
+       /* Postpone a workqueue for deferred registration. */
+       if (!dg00x->registered)
+               snd_fw_schedule_registration(unit, &dg00x->dwork);
+
        snd_dg00x_transaction_reregister(dg00x);
 
-       mutex_lock(&dg00x->mutex);
-       snd_dg00x_stream_update_duplex(dg00x);
-       mutex_unlock(&dg00x->mutex);
+       /*
+        * After registration, userspace can start packet streaming, then this
+        * code block works fine.
+        */
+       if (dg00x->registered) {
+               mutex_lock(&dg00x->mutex);
+               snd_dg00x_stream_update_duplex(dg00x);
+               mutex_unlock(&dg00x->mutex);
+       }
 }
 
 static void snd_dg00x_remove(struct fw_unit *unit)
 {
        struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
 
-       /* No need to wait for releasing card object in this context. */
-       snd_card_free_when_closed(dg00x->card);
+       /*
+        * Confirm to stop the work for registration before the sound card is
+        * going to be released. The work is not scheduled again because bus
+        * reset handler is not called anymore.
+        */
+       cancel_delayed_work_sync(&dg00x->dwork);
+
+       if (dg00x->registered) {
+               /* No need to wait for releasing card object in this context. */
+               snd_card_free_when_closed(dg00x->card);
+       } else {
+               /* Don't forget this case. */
+               dg00x_free(dg00x);
+       }
 }
 
 static const struct ieee1394_device_id snd_dg00x_id_table[] = {
index 907e739936777c359bf6c93973d7faba09ac68df..2cd465c0caae84e1f1e21849f58c12194e2b9f5f 100644 (file)
@@ -37,6 +37,9 @@ struct snd_dg00x {
        struct mutex mutex;
        spinlock_t lock;
 
+       bool registered;
+       struct delayed_work dwork;
+
        struct amdtp_stream tx_stream;
        struct fw_iso_resources tx_resources;
 
index 8f27b67503c880bcf5d1c1e4a9e84ba2ad4bd006..71a0613d3da040c49ef633f4877f98d8db219a99 100644 (file)
@@ -168,11 +168,34 @@ get_hardware_info(struct snd_efw *efw)
               sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
        memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
               sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
+
+       /* AudioFire8 (since 2009) and AudioFirePre8 */
+       if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9)
+               efw->is_af9 = true;
+       /* These models uses the same firmware. */
+       if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 ||
+           hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 ||
+           hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 ||
+           hwinfo->type == MODEL_GIBSON_RIP ||
+           hwinfo->type == MODEL_GIBSON_GOLDTOP)
+               efw->is_fireworks3 = true;
 end:
        kfree(hwinfo);
        return err;
 }
 
+static void efw_free(struct snd_efw *efw)
+{
+       snd_efw_stream_destroy_duplex(efw);
+       snd_efw_transaction_remove_instance(efw);
+       fw_unit_put(efw->unit);
+
+       kfree(efw->resp_buf);
+
+       mutex_destroy(&efw->mutex);
+       kfree(efw);
+}
+
 /*
  * This module releases the FireWire unit data after all ALSA character devices
  * are released by applications. This is for releasing stream data or finishing
@@ -184,28 +207,24 @@ efw_card_free(struct snd_card *card)
 {
        struct snd_efw *efw = card->private_data;
 
-       snd_efw_stream_destroy_duplex(efw);
-       snd_efw_transaction_remove_instance(efw);
-       fw_unit_put(efw->unit);
-
-       kfree(efw->resp_buf);
-
        if (efw->card_index >= 0) {
                mutex_lock(&devices_mutex);
                clear_bit(efw->card_index, devices_used);
                mutex_unlock(&devices_mutex);
        }
 
-       mutex_destroy(&efw->mutex);
+       efw_free(card->private_data);
 }
 
-static int
-efw_probe(struct fw_unit *unit,
-         const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
 {
-       struct snd_card *card;
-       struct snd_efw *efw;
-       int card_index, err;
+       struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
+       unsigned int card_index;
+       int err;
+
+       if (efw->registered)
+               return;
 
        mutex_lock(&devices_mutex);
 
@@ -215,24 +234,16 @@ efw_probe(struct fw_unit *unit,
                        break;
        }
        if (card_index >= SNDRV_CARDS) {
-               err = -ENOENT;
-               goto end;
+               mutex_unlock(&devices_mutex);
+               return;
        }
 
-       err = snd_card_new(&unit->device, index[card_index], id[card_index],
-                          THIS_MODULE, sizeof(struct snd_efw), &card);
-       if (err < 0)
-               goto end;
-       efw = card->private_data;
-       efw->card_index = card_index;
-       set_bit(card_index, devices_used);
-       card->private_free = efw_card_free;
-
-       efw->card = card;
-       efw->unit = fw_unit_get(unit);
-       mutex_init(&efw->mutex);
-       spin_lock_init(&efw->lock);
-       init_waitqueue_head(&efw->hwdep_wait);
+       err = snd_card_new(&efw->unit->device, index[card_index],
+                          id[card_index], THIS_MODULE, 0, &efw->card);
+       if (err < 0) {
+               mutex_unlock(&devices_mutex);
+               return;
+       }
 
        /* prepare response buffer */
        snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
@@ -248,16 +259,10 @@ efw_probe(struct fw_unit *unit,
        err = get_hardware_info(efw);
        if (err < 0)
                goto error;
-       /* AudioFire8 (since 2009) and AudioFirePre8 */
-       if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
-               efw->is_af9 = true;
-       /* These models uses the same firmware. */
-       if (entry->model_id == MODEL_ECHO_AUDIOFIRE_2 ||
-           entry->model_id == MODEL_ECHO_AUDIOFIRE_4 ||
-           entry->model_id == MODEL_ECHO_AUDIOFIRE_9 ||
-           entry->model_id == MODEL_GIBSON_RIP ||
-           entry->model_id == MODEL_GIBSON_GOLDTOP)
-               efw->is_fireworks3 = true;
+
+       err = snd_efw_stream_init_duplex(efw);
+       if (err < 0)
+               goto error;
 
        snd_efw_proc_init(efw);
 
@@ -275,44 +280,93 @@ efw_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
-       err = snd_efw_stream_init_duplex(efw);
+       err = snd_card_register(efw->card);
        if (err < 0)
                goto error;
 
-       err = snd_card_register(card);
-       if (err < 0) {
-               snd_efw_stream_destroy_duplex(efw);
-               goto error;
-       }
-
-       dev_set_drvdata(&unit->device, efw);
-end:
+       set_bit(card_index, devices_used);
        mutex_unlock(&devices_mutex);
-       return err;
+
+       /*
+        * After registered, efw instance can be released corresponding to
+        * releasing the sound card instance.
+        */
+       efw->card->private_free = efw_card_free;
+       efw->card->private_data = efw;
+       efw->registered = true;
+
+       return;
 error:
-       snd_efw_transaction_remove_instance(efw);
        mutex_unlock(&devices_mutex);
-       snd_card_free(card);
-       return err;
+       snd_efw_transaction_remove_instance(efw);
+       snd_efw_stream_destroy_duplex(efw);
+       snd_card_free(efw->card);
+       dev_info(&efw->unit->device,
+                "Sound card registration failed: %d\n", err);
+}
+
+static int
+efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+       struct snd_efw *efw;
+
+       efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL);
+       if (efw == NULL)
+               return -ENOMEM;
+
+       efw->unit = fw_unit_get(unit);
+       dev_set_drvdata(&unit->device, efw);
+
+       mutex_init(&efw->mutex);
+       spin_lock_init(&efw->lock);
+       init_waitqueue_head(&efw->hwdep_wait);
+
+       /* Allocate and register this sound card later. */
+       INIT_DEFERRABLE_WORK(&efw->dwork, do_registration);
+       snd_fw_schedule_registration(unit, &efw->dwork);
+
+       return 0;
 }
 
 static void efw_update(struct fw_unit *unit)
 {
        struct snd_efw *efw = dev_get_drvdata(&unit->device);
 
+       /* Postpone a workqueue for deferred registration. */
+       if (!efw->registered)
+               snd_fw_schedule_registration(unit, &efw->dwork);
+
        snd_efw_transaction_bus_reset(efw->unit);
 
-       mutex_lock(&efw->mutex);
-       snd_efw_stream_update_duplex(efw);
-       mutex_unlock(&efw->mutex);
+       /*
+        * After registration, userspace can start packet streaming, then this
+        * code block works fine.
+        */
+       if (efw->registered) {
+               mutex_lock(&efw->mutex);
+               snd_efw_stream_update_duplex(efw);
+               mutex_unlock(&efw->mutex);
+       }
 }
 
 static void efw_remove(struct fw_unit *unit)
 {
        struct snd_efw *efw = dev_get_drvdata(&unit->device);
 
-       /* No need to wait for releasing card object in this context. */
-       snd_card_free_when_closed(efw->card);
+       /*
+        * Confirm to stop the work for registration before the sound card is
+        * going to be released. The work is not scheduled again because bus
+        * reset handler is not called anymore.
+        */
+       cancel_delayed_work_sync(&efw->dwork);
+
+       if (efw->registered) {
+               /* No need to wait for releasing card object in this context. */
+               snd_card_free_when_closed(efw->card);
+       } else {
+               /* Don't forget this case. */
+               efw_free(efw);
+       }
 }
 
 static const struct ieee1394_device_id efw_id_table[] = {
index 96c4e0c6a9bd81d5f1136bc2673471ca5c23fd93..03ed35237e2bb89af027b18fddbc334f5e4620b7 100644 (file)
@@ -65,6 +65,9 @@ struct snd_efw {
        struct mutex mutex;
        spinlock_t lock;
 
+       bool registered;
+       struct delayed_work dwork;
+
        /* for transaction */
        u32 seqnum;
        bool resp_addr_changable;
@@ -81,7 +84,6 @@ struct snd_efw {
        unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
        unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
 
-       struct amdtp_stream *master;
        struct amdtp_stream tx_stream;
        struct amdtp_stream rx_stream;
        struct cmp_connection out_conn;
index 425db8d88235ad755b054f3701fc9b0c14072709..ee47924aef0df676223975a08df37ad055cc1f51 100644 (file)
@@ -120,23 +120,6 @@ destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
        cmp_connection_destroy(&efw->out_conn);
 }
 
-static int
-get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
-{
-       enum snd_efw_clock_source clock_source;
-       int err;
-
-       err = snd_efw_command_get_clock_source(efw, &clock_source);
-       if (err < 0)
-               return err;
-
-       if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
-               return -ENOSYS;
-
-       *sync_mode = CIP_SYNC_TO_DEVICE;
-       return 0;
-}
-
 static int
 check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
 {
@@ -208,9 +191,6 @@ end:
 
 int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
 {
-       struct amdtp_stream *master, *slave;
-       unsigned int slave_substreams;
-       enum cip_flags sync_mode;
        unsigned int curr_rate;
        int err = 0;
 
@@ -218,32 +198,19 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
        if (efw->playback_substreams == 0 && efw->capture_substreams  == 0)
                goto end;
 
-       err = get_sync_mode(efw, &sync_mode);
-       if (err < 0)
-               goto end;
-       if (sync_mode == CIP_SYNC_TO_DEVICE) {
-               master = &efw->tx_stream;
-               slave  = &efw->rx_stream;
-               slave_substreams  = efw->playback_substreams;
-       } else {
-               master = &efw->rx_stream;
-               slave  = &efw->tx_stream;
-               slave_substreams = efw->capture_substreams;
-       }
-
        /*
         * Considering JACK/FFADO streaming:
         * TODO: This can be removed hwdep functionality becomes popular.
         */
-       err = check_connection_used_by_others(efw, master);
+       err = check_connection_used_by_others(efw, &efw->rx_stream);
        if (err < 0)
                goto end;
 
        /* packet queueing error */
-       if (amdtp_streaming_error(slave))
-               stop_stream(efw, slave);
-       if (amdtp_streaming_error(master))
-               stop_stream(efw, master);
+       if (amdtp_streaming_error(&efw->tx_stream))
+               stop_stream(efw, &efw->tx_stream);
+       if (amdtp_streaming_error(&efw->rx_stream))
+               stop_stream(efw, &efw->rx_stream);
 
        /* stop streams if rate is different */
        err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
@@ -252,20 +219,17 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
        if (rate == 0)
                rate = curr_rate;
        if (rate != curr_rate) {
-               stop_stream(efw, slave);
-               stop_stream(efw, master);
+               stop_stream(efw, &efw->tx_stream);
+               stop_stream(efw, &efw->rx_stream);
        }
 
        /* master should be always running */
-       if (!amdtp_stream_running(master)) {
-               amdtp_stream_set_sync(sync_mode, master, slave);
-               efw->master = master;
-
+       if (!amdtp_stream_running(&efw->rx_stream)) {
                err = snd_efw_command_set_sampling_rate(efw, rate);
                if (err < 0)
                        goto end;
 
-               err = start_stream(efw, master, rate);
+               err = start_stream(efw, &efw->rx_stream, rate);
                if (err < 0) {
                        dev_err(&efw->unit->device,
                                "fail to start AMDTP master stream:%d\n", err);
@@ -274,12 +238,13 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
        }
 
        /* start slave if needed */
-       if (slave_substreams > 0 && !amdtp_stream_running(slave)) {
-               err = start_stream(efw, slave, rate);
+       if (efw->capture_substreams > 0 &&
+           !amdtp_stream_running(&efw->tx_stream)) {
+               err = start_stream(efw, &efw->tx_stream, rate);
                if (err < 0) {
                        dev_err(&efw->unit->device,
                                "fail to start AMDTP slave stream:%d\n", err);
-                       stop_stream(efw, master);
+                       stop_stream(efw, &efw->rx_stream);
                }
        }
 end:
@@ -288,26 +253,11 @@ end:
 
 void snd_efw_stream_stop_duplex(struct snd_efw *efw)
 {
-       struct amdtp_stream *master, *slave;
-       unsigned int master_substreams, slave_substreams;
-
-       if (efw->master == &efw->rx_stream) {
-               slave  = &efw->tx_stream;
-               master = &efw->rx_stream;
-               slave_substreams  = efw->capture_substreams;
-               master_substreams = efw->playback_substreams;
-       } else {
-               slave  = &efw->rx_stream;
-               master = &efw->tx_stream;
-               slave_substreams  = efw->playback_substreams;
-               master_substreams = efw->capture_substreams;
-       }
-
-       if (slave_substreams == 0) {
-               stop_stream(efw, slave);
+       if (efw->capture_substreams == 0) {
+               stop_stream(efw, &efw->tx_stream);
 
-               if (master_substreams == 0)
-                       stop_stream(efw, master);
+               if (efw->playback_substreams == 0)
+                       stop_stream(efw, &efw->rx_stream);
        }
 }
 
index f80aafa44c89499ff92b0b3af0bc761ca49c0567..ca4dfcf4317579158c1cc6178a40fcf7eeda1052 100644 (file)
@@ -67,6 +67,38 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
 }
 EXPORT_SYMBOL(snd_fw_transaction);
 
+#define PROBE_DELAY_MS         (2 * MSEC_PER_SEC)
+
+/**
+ * snd_fw_schedule_registration - schedule work for sound card registration
+ * @unit: an instance for unit on IEEE 1394 bus
+ * @dwork: delayed work with callback function
+ *
+ * This function is not designed for general purposes. When new unit is
+ * connected to IEEE 1394 bus, the bus is under bus-reset state because of
+ * topological change. In this state, units tend to fail both of asynchronous
+ * and isochronous communication. To avoid this problem, this function is used
+ * to postpone sound card registration after the state. The callers must
+ * set up instance of delayed work in advance.
+ */
+void snd_fw_schedule_registration(struct fw_unit *unit,
+                                 struct delayed_work *dwork)
+{
+       u64 now, delay;
+
+       now = get_jiffies_64();
+       delay = fw_parent_device(unit)->card->reset_jiffies
+                                       + msecs_to_jiffies(PROBE_DELAY_MS);
+
+       if (time_after64(delay, now))
+               delay -= now;
+       else
+               delay = 0;
+
+       mod_delayed_work(system_wq, dwork, delay);
+}
+EXPORT_SYMBOL(snd_fw_schedule_registration);
+
 static void async_midi_port_callback(struct fw_card *card, int rcode,
                                     void *data, size_t length,
                                     void *callback_data)
index f3f6f84c48d69747cc0fa3bd815d986c79ca0d78..f6769312ebfccbe473ac291e81b386b48b45488f 100644 (file)
@@ -22,6 +22,9 @@ static inline bool rcode_is_permanent_error(int rcode)
        return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
 }
 
+void snd_fw_schedule_registration(struct fw_unit *unit,
+                                 struct delayed_work *dwork);
+
 struct snd_fw_async_midi_port;
 typedef int (*snd_fw_async_midi_port_fill)(
                                struct snd_rawmidi_substream *substream,
index 7cb5743c073bbed3f88d2452b6b6a7047f7619f3..d9361f3521338bce3befa1ddaa422a4e935680b4 100644 (file)
@@ -242,8 +242,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
         * blocks than IEC 61883-6 defines.
         */
        if (stream == &oxfw->tx_stream) {
-               oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
-                                        CIP_JUMBO_PAYLOAD;
+               oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
                if (oxfw->wrong_dbs)
                        oxfw->tx_stream.flags |= CIP_WRONG_DBS;
        }
index abedc2207261cac52e6b848a7123cf746780941e..e629b88f7d933481a70cb72b6c504856c9a57d98 100644 (file)
@@ -118,15 +118,8 @@ end:
        return err;
 }
 
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
-static void oxfw_card_free(struct snd_card *card)
+static void oxfw_free(struct snd_oxfw *oxfw)
 {
-       struct snd_oxfw *oxfw = card->private_data;
        unsigned int i;
 
        snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
@@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card)
        mutex_destroy(&oxfw->mutex);
 }
 
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
+static void oxfw_card_free(struct snd_card *card)
+{
+       oxfw_free(card->private_data);
+}
+
 static int detect_quirks(struct snd_oxfw *oxfw)
 {
        struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
@@ -205,41 +209,39 @@ static int detect_quirks(struct snd_oxfw *oxfw)
        return 0;
 }
 
-static int oxfw_probe(struct fw_unit *unit,
-                     const struct ieee1394_device_id *entry)
+static void do_registration(struct work_struct *work)
 {
-       struct snd_card *card;
-       struct snd_oxfw *oxfw;
+       struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
        int err;
 
-       if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
-               return -ENODEV;
+       if (oxfw->registered)
+               return;
 
-       err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
-                          sizeof(*oxfw), &card);
+       err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
+                          &oxfw->card);
        if (err < 0)
-               return err;
+               return;
 
-       card->private_free = oxfw_card_free;
-       oxfw = card->private_data;
-       oxfw->card = card;
-       mutex_init(&oxfw->mutex);
-       oxfw->unit = fw_unit_get(unit);
-       oxfw->entry = entry;
-       spin_lock_init(&oxfw->lock);
-       init_waitqueue_head(&oxfw->hwdep_wait);
+       err = name_card(oxfw);
+       if (err < 0)
+               goto error;
 
-       err = snd_oxfw_stream_discover(oxfw);
+       err = detect_quirks(oxfw);
        if (err < 0)
                goto error;
 
-       err = name_card(oxfw);
+       err = snd_oxfw_stream_discover(oxfw);
        if (err < 0)
                goto error;
 
-       err = detect_quirks(oxfw);
+       err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
        if (err < 0)
                goto error;
+       if (oxfw->has_output) {
+               err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
+               if (err < 0)
+                       goto error;
+       }
 
        err = snd_oxfw_create_pcm(oxfw);
        if (err < 0)
@@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
-       err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
+       err = snd_card_register(oxfw->card);
        if (err < 0)
                goto error;
-       if (oxfw->has_output) {
-               err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
-               if (err < 0)
-                       goto error;
-       }
 
-       err = snd_card_register(card);
-       if (err < 0) {
-               snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
-               if (oxfw->has_output)
-                       snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
-               goto error;
-       }
+       /*
+        * After registered, oxfw instance can be released corresponding to
+        * releasing the sound card instance.
+        */
+       oxfw->card->private_free = oxfw_card_free;
+       oxfw->card->private_data = oxfw;
+       oxfw->registered = true;
+
+       return;
+error:
+       snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+       if (oxfw->has_output)
+               snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+       snd_card_free(oxfw->card);
+       dev_info(&oxfw->unit->device,
+                "Sound card registration failed: %d\n", err);
+}
+
+static int oxfw_probe(struct fw_unit *unit,
+                     const struct ieee1394_device_id *entry)
+{
+       struct snd_oxfw *oxfw;
+
+       if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
+               return -ENODEV;
+
+       /* Allocate this independent of sound card instance. */
+       oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL);
+       if (oxfw == NULL)
+               return -ENOMEM;
+
+       oxfw->entry = entry;
+       oxfw->unit = fw_unit_get(unit);
        dev_set_drvdata(&unit->device, oxfw);
 
+       mutex_init(&oxfw->mutex);
+       spin_lock_init(&oxfw->lock);
+       init_waitqueue_head(&oxfw->hwdep_wait);
+
+       /* Allocate and register this sound card later. */
+       INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
+       snd_fw_schedule_registration(unit, &oxfw->dwork);
+
        return 0;
-error:
-       snd_card_free(card);
-       return err;
 }
 
 static void oxfw_bus_reset(struct fw_unit *unit)
 {
        struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
+       if (!oxfw->registered)
+               snd_fw_schedule_registration(unit, &oxfw->dwork);
+
        fcp_bus_reset(oxfw->unit);
 
-       mutex_lock(&oxfw->mutex);
+       if (oxfw->registered) {
+               mutex_lock(&oxfw->mutex);
 
-       snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
-       if (oxfw->has_output)
-               snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
+               snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
+               if (oxfw->has_output)
+                       snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
 
-       mutex_unlock(&oxfw->mutex);
+               mutex_unlock(&oxfw->mutex);
 
-       if (oxfw->entry->vendor_id == OUI_STANTON)
-               snd_oxfw_scs1x_update(oxfw);
+               if (oxfw->entry->vendor_id == OUI_STANTON)
+                       snd_oxfw_scs1x_update(oxfw);
+       }
 }
 
 static void oxfw_remove(struct fw_unit *unit)
 {
        struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
 
-       /* No need to wait for releasing card object in this context. */
-       snd_card_free_when_closed(oxfw->card);
+       /*
+        * Confirm to stop the work for registration before the sound card is
+        * going to be released. The work is not scheduled again because bus
+        * reset handler is not called anymore.
+        */
+       cancel_delayed_work_sync(&oxfw->dwork);
+
+       if (oxfw->registered) {
+               /* No need to wait for releasing card object in this context. */
+               snd_card_free_when_closed(oxfw->card);
+       } else {
+               /* Don't forget this case. */
+               oxfw_free(oxfw);
+       }
 }
 
 static const struct compat_info griffin_firewave = {
index 9beecc2147676090be4ccec9c1c4488fb63f883e..2047dcb27625161d8851658a2d4ad44e78da7263 100644 (file)
 struct snd_oxfw {
        struct snd_card *card;
        struct fw_unit *unit;
-       const struct device_info *device_info;
        struct mutex mutex;
        spinlock_t lock;
 
+       bool registered;
+       struct delayed_work dwork;
+
        bool wrong_dbs;
        bool has_output;
        u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
index 0e6dd5c61f5388afc39cdaca33bff884ec5ad748..4ad3bd7fd4453e3a64fc4cd95001165510292559 100644 (file)
@@ -381,19 +381,17 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
        if (err < 0)
                return err;
        if (curr_rate != rate ||
-           amdtp_streaming_error(&tscm->tx_stream) ||
-           amdtp_streaming_error(&tscm->rx_stream)) {
+           amdtp_streaming_error(&tscm->rx_stream) ||
+           amdtp_streaming_error(&tscm->tx_stream)) {
                finish_session(tscm);
 
-               amdtp_stream_stop(&tscm->tx_stream);
                amdtp_stream_stop(&tscm->rx_stream);
+               amdtp_stream_stop(&tscm->tx_stream);
 
                release_resources(tscm);
        }
 
-       if (!amdtp_stream_running(&tscm->tx_stream)) {
-               amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
-                                     &tscm->tx_stream, &tscm->rx_stream);
+       if (!amdtp_stream_running(&tscm->rx_stream)) {
                err = keep_resources(tscm, rate);
                if (err < 0)
                        goto error;
@@ -406,27 +404,27 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
                if (err < 0)
                        goto error;
 
-               err = amdtp_stream_start(&tscm->tx_stream,
-                               tscm->tx_resources.channel,
+               err = amdtp_stream_start(&tscm->rx_stream,
+                               tscm->rx_resources.channel,
                                fw_parent_device(tscm->unit)->max_speed);
                if (err < 0)
                        goto error;
 
-               if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+               if (!amdtp_stream_wait_callback(&tscm->rx_stream,
                                                CALLBACK_TIMEOUT)) {
                        err = -ETIMEDOUT;
                        goto error;
                }
        }
 
-       if (!amdtp_stream_running(&tscm->rx_stream)) {
-               err = amdtp_stream_start(&tscm->rx_stream,
-                               tscm->rx_resources.channel,
+       if (!amdtp_stream_running(&tscm->tx_stream)) {
+               err = amdtp_stream_start(&tscm->tx_stream,
+                               tscm->tx_resources.channel,
                                fw_parent_device(tscm->unit)->max_speed);
                if (err < 0)
                        goto error;
 
-               if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+               if (!amdtp_stream_wait_callback(&tscm->tx_stream,
                                                CALLBACK_TIMEOUT)) {
                        err = -ETIMEDOUT;
                        goto error;
@@ -435,8 +433,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
 
        return 0;
 error:
-       amdtp_stream_stop(&tscm->tx_stream);
        amdtp_stream_stop(&tscm->rx_stream);
+       amdtp_stream_stop(&tscm->tx_stream);
 
        finish_session(tscm);
        release_resources(tscm);
index e281c338e562d59e861303789d82c319b9a2ae9c..9dc93a7eb9da420fb871d7b28007b67b4d37f261 100644 (file)
@@ -85,10 +85,8 @@ static int identify_model(struct snd_tscm *tscm)
        return 0;
 }
 
-static void tscm_card_free(struct snd_card *card)
+static void tscm_free(struct snd_tscm *tscm)
 {
-       struct snd_tscm *tscm = card->private_data;
-
        snd_tscm_transaction_unregister(tscm);
        snd_tscm_stream_destroy_duplex(tscm);
 
@@ -97,44 +95,36 @@ static void tscm_card_free(struct snd_card *card)
        mutex_destroy(&tscm->mutex);
 }
 
-static int snd_tscm_probe(struct fw_unit *unit,
-                          const struct ieee1394_device_id *entry)
+static void tscm_card_free(struct snd_card *card)
 {
-       struct snd_card *card;
-       struct snd_tscm *tscm;
+       tscm_free(card->private_data);
+}
+
+static void do_registration(struct work_struct *work)
+{
+       struct snd_tscm *tscm = container_of(work, struct snd_tscm, dwork.work);
        int err;
 
-       /* create card */
-       err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
-                          sizeof(struct snd_tscm), &card);
+       err = snd_card_new(&tscm->unit->device, -1, NULL, THIS_MODULE, 0,
+                          &tscm->card);
        if (err < 0)
-               return err;
-       card->private_free = tscm_card_free;
-
-       /* initialize myself */
-       tscm = card->private_data;
-       tscm->card = card;
-       tscm->unit = fw_unit_get(unit);
-
-       mutex_init(&tscm->mutex);
-       spin_lock_init(&tscm->lock);
-       init_waitqueue_head(&tscm->hwdep_wait);
+               return;
 
        err = identify_model(tscm);
        if (err < 0)
                goto error;
 
-       snd_tscm_proc_init(tscm);
-
-       err = snd_tscm_stream_init_duplex(tscm);
+       err = snd_tscm_transaction_register(tscm);
        if (err < 0)
                goto error;
 
-       err = snd_tscm_create_pcm_devices(tscm);
+       err = snd_tscm_stream_init_duplex(tscm);
        if (err < 0)
                goto error;
 
-       err = snd_tscm_transaction_register(tscm);
+       snd_tscm_proc_init(tscm);
+
+       err = snd_tscm_create_pcm_devices(tscm);
        if (err < 0)
                goto error;
 
@@ -146,35 +136,91 @@ static int snd_tscm_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
-       err = snd_card_register(card);
+       err = snd_card_register(tscm->card);
        if (err < 0)
                goto error;
 
-       dev_set_drvdata(&unit->device, tscm);
+       /*
+        * After registered, tscm instance can be released corresponding to
+        * releasing the sound card instance.
+        */
+       tscm->card->private_free = tscm_card_free;
+       tscm->card->private_data = tscm;
+       tscm->registered = true;
 
-       return err;
+       return;
 error:
-       snd_card_free(card);
-       return err;
+       snd_tscm_transaction_unregister(tscm);
+       snd_tscm_stream_destroy_duplex(tscm);
+       snd_card_free(tscm->card);
+       dev_info(&tscm->unit->device,
+                "Sound card registration failed: %d\n", err);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+                          const struct ieee1394_device_id *entry)
+{
+       struct snd_tscm *tscm;
+
+       /* Allocate this independent of sound card instance. */
+       tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL);
+       if (tscm == NULL)
+               return -ENOMEM;
+
+       /* initialize myself */
+       tscm->unit = fw_unit_get(unit);
+       dev_set_drvdata(&unit->device, tscm);
+
+       mutex_init(&tscm->mutex);
+       spin_lock_init(&tscm->lock);
+       init_waitqueue_head(&tscm->hwdep_wait);
+
+       /* Allocate and register this sound card later. */
+       INIT_DEFERRABLE_WORK(&tscm->dwork, do_registration);
+       snd_fw_schedule_registration(unit, &tscm->dwork);
+
+       return 0;
 }
 
 static void snd_tscm_update(struct fw_unit *unit)
 {
        struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
 
+       /* Postpone a workqueue for deferred registration. */
+       if (!tscm->registered)
+               snd_fw_schedule_registration(unit, &tscm->dwork);
+
        snd_tscm_transaction_reregister(tscm);
 
-       mutex_lock(&tscm->mutex);
-       snd_tscm_stream_update_duplex(tscm);
-       mutex_unlock(&tscm->mutex);
+       /*
+        * After registration, userspace can start packet streaming, then this
+        * code block works fine.
+        */
+       if (tscm->registered) {
+               mutex_lock(&tscm->mutex);
+               snd_tscm_stream_update_duplex(tscm);
+               mutex_unlock(&tscm->mutex);
+       }
 }
 
 static void snd_tscm_remove(struct fw_unit *unit)
 {
        struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
 
-       /* No need to wait for releasing card object in this context. */
-       snd_card_free_when_closed(tscm->card);
+       /*
+        * Confirm to stop the work for registration before the sound card is
+        * going to be released. The work is not scheduled again because bus
+        * reset handler is not called anymore.
+        */
+       cancel_delayed_work_sync(&tscm->dwork);
+
+       if (tscm->registered) {
+               /* No need to wait for releasing card object in this context. */
+               snd_card_free_when_closed(tscm->card);
+       } else {
+               /* Don't forget this case. */
+               tscm_free(tscm);
+       }
 }
 
 static const struct ieee1394_device_id snd_tscm_id_table[] = {
index 30ab77e924f7f68f9b939a230109152722ad9057..1f61011579a7fc72f44b6cae84d2e2d039969ddf 100644 (file)
@@ -51,6 +51,8 @@ struct snd_tscm {
        struct mutex mutex;
        spinlock_t lock;
 
+       bool registered;
+       struct delayed_work dwork;
        const struct snd_tscm_spec *spec;
 
        struct fw_iso_resources tx_resources;
index 2433f7c81472848be51b9af420ec198b523871b1..31b510c5ca0bdf3cfef06bdbc47700e4f8c3ca73 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);
@@ -144,6 +147,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
        if (!edev)
                return -ENOMEM;
        hdev = &edev->hdac;
+       edev->ebus = ebus;
 
        snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr);
 
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 8c486235c905253ea0609183d813362a294e42f9..9fee464e5d49772f53db5d4f5a6485ecaa028b10 100644 (file)
@@ -80,6 +80,22 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
 
+/* wait for cmd dmas till they are stopped */
+static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
+{
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(100);
+       while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
+               && time_before(jiffies, timeout))
+               udelay(10);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+       while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
+               && time_before(jiffies, timeout))
+               udelay(10);
+}
+
 /**
  * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
  * @bus: HD-audio core bus
@@ -90,6 +106,7 @@ void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
        /* disable ringbuffer DMAs */
        snd_hdac_chip_writeb(bus, RIRBCTL, 0);
        snd_hdac_chip_writeb(bus, CORBCTL, 0);
+       hdac_wait_for_cmd_dmas(bus);
        /* disable unsolicited responses */
        snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
        spin_unlock_irq(&bus->reg_lock);
index 607bbeaebddf784aadb062c7b3934b4fa506c1e0..c9af022676c2f1071585d6ec3a7975ef19ed55be 100644 (file)
@@ -158,22 +158,40 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
 
-/* There is a fixed mapping between audio pin node and display port
- * on current Intel platforms:
+/* There is a fixed mapping between audio pin node and display port.
+ * on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
  * Pin Widget 5 - PORT B (port = 1 in i915 driver)
  * Pin Widget 6 - PORT C (port = 2 in i915 driver)
  * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ *
+ * on VLV, ILK:
+ * Pin Widget 4 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 5 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 6 - PORT D (port = 3 in i915 driver)
  */
-static int pin2port(hda_nid_t pin_nid)
+static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
 {
-       if (WARN_ON(pin_nid < 5 || pin_nid > 7))
+       int base_nid;
+
+       switch (codec->vendor_id) {
+       case 0x80860054: /* ILK */
+       case 0x80862804: /* ILK */
+       case 0x80862882: /* VLV */
+               base_nid = 3;
+               break;
+       default:
+               base_nid = 4;
+               break;
+       }
+
+       if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3))
                return -1;
-       return pin_nid - 4;
+       return pin_nid - base_nid;
 }
 
 /**
  * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
- * @bus: HDA core bus
+ * @codec: HDA codec
  * @nid: the pin widget NID
  * @rate: the sample rate to set
  *
@@ -183,14 +201,15 @@ static int pin2port(hda_nid_t pin_nid)
  * This function sets N/CTS value based on the given sample rate.
  * Returns zero for success, or a negative error code.
  */
-int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate)
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate)
 {
+       struct hdac_bus *bus = codec->bus;
        struct i915_audio_component *acomp = bus->audio_component;
        int port;
 
        if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
                return -ENODEV;
-       port = pin2port(nid);
+       port = pin2port(codec, nid);
        if (port < 0)
                return -EINVAL;
        return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
@@ -199,7 +218,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
 
 /**
  * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
- * @bus: HDA core bus
+ * @codec: HDA codec
  * @nid: the pin widget NID
  * @audio_enabled: the pointer to store the current audio state
  * @buffer: the buffer pointer to store ELD bytes
@@ -217,16 +236,17 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
  * thus it may be over @max_bytes.  If it's over @max_bytes, it implies
  * that only a part of ELD bytes have been fetched.
  */
-int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
                           bool *audio_enabled, char *buffer, int max_bytes)
 {
+       struct hdac_bus *bus = codec->bus;
        struct i915_audio_component *acomp = bus->audio_component;
        int port;
 
        if (!acomp || !acomp->ops || !acomp->ops->get_eld)
                return -ENODEV;
 
-       port = pin2port(nid);
+       port = pin2port(codec, nid);
        if (port < 0)
                return -EINVAL;
        return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
@@ -338,6 +358,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
        struct i915_audio_component *acomp;
        int ret;
 
+       if (WARN_ON(hdac_acomp))
+               return -EBUSY;
+
        if (!i915_gfx_present())
                return -ENODEV;
 
@@ -371,6 +394,7 @@ out_master_del:
 out_err:
        kfree(acomp);
        bus->audio_component = NULL;
+       hdac_acomp = NULL;
        dev_info(dev, "failed to add i915 component master (%d)\n", ret);
 
        return ret;
@@ -404,6 +428,7 @@ int snd_hdac_i915_exit(struct hdac_bus *bus)
 
        kfree(acomp);
        bus->audio_component = NULL;
+       hdac_acomp = NULL;
 
        return 0;
 }
index d7ec862638286d0272206dc77b647491ca19c7ae..c6c75e7e0981affde1d06b508f551b7be25733a0 100644 (file)
@@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
        WARN_ON(count != channels);
 }
 
+static int spk_mask_from_spk_alloc(int spk_alloc)
+{
+       int i;
+       int spk_mask = eld_speaker_allocation_bits[0];
+
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       return spk_mask;
+}
+
 static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                              unsigned int size, unsigned int __user *tlv)
 {
        struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
        struct hdac_chmap *chmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
        unsigned int __user *dst;
        int chs, count = 0;
+       unsigned long max_chs;
+       int type;
+       int spk_alloc, spk_mask;
 
        if (size < 8)
                return -ENOMEM;
@@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                return -EFAULT;
        size -= 8;
        dst = tlv + 2;
-       for (chs = 2; chs <= chmap->channels_max; chs++) {
+
+       spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
+       spk_mask = spk_mask_from_spk_alloc(spk_alloc);
+
+       max_chs = hweight_long(spk_mask);
+
+       for (chs = 2; chs <= max_chs; chs++) {
                int i;
                struct hdac_cea_channel_speaker_allocation *cap;
 
                cap = channel_allocations;
                for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
                        int chs_bytes = chs * 4;
-                       int type = chmap->ops.chmap_cea_alloc_validate_get_type(
-                                                               chmap, cap, chs);
                        unsigned int tlv_chmap[8];
 
-                       if (type < 0)
+                       if (cap->channels != chs)
+                               continue;
+
+                       if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
                                continue;
+
+                       type = chmap->ops.chmap_cea_alloc_validate_get_type(
+                                                       chmap, cap, chs);
+                       if (type < 0)
+                               return -ENODEV;
                        if (size < 8)
                                return -ENOMEM;
+
                        if (put_user(type, dst) ||
                            put_user(chs_bytes, dst + 1))
                                return -EFAULT;
+
                        dst += 2;
                        size -= 8;
                        count += 8;
+
                        if (size < chs_bytes)
                                return -ENOMEM;
+
                        size -= chs_bytes;
                        count += chs_bytes;
                        chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
                                                tlv_chmap, chs);
+
                        if (copy_to_user(dst, tlv_chmap, chs_bytes))
                                return -EFAULT;
                        dst += chs;
                }
        }
+
        if (put_user(count, tlv + 1))
                return -EFAULT;
+
        return 0;
 }
 
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 69f76ff5693d4ec864a8dea68e374929bed66974..718d5e3b7806f01da8782ed9955c9d5dd22db57a 100644 (file)
@@ -785,6 +785,9 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
        DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
                                      header->number);
 
+       if (header->number >= ARRAY_SIZE(dev->patch_status))
+               return -EINVAL;
+
        dev->patch_status[header->number] |= WF_SLOT_FILLED;
 
        bptr = buf;
@@ -809,6 +812,9 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
        DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
                header->number);
 
+       if (header->number >= ARRAY_SIZE(dev->prog_status))
+               return -EINVAL;
+
        dev->prog_status[header->number] = WF_SLOT_USED;
 
        /* XXX need to zero existing SLOT_USED bit for program_status[i]
@@ -898,6 +904,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
                header->number = x;
        }
 
+       if (header->number >= WF_MAX_SAMPLE)
+               return -EINVAL;
+
        if (header->size) {
 
                /* XXX it's a debatable point whether or not RDONLY semantics
index b36ea47527e87f567cd5c97ee7b9d3297676f092..0b8d0de872732e618a55f6b26e9bec9935956d60 100644 (file)
@@ -1414,11 +1414,9 @@ attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *m
        else {
 #ifdef CONFIG_ARCH_NETWINDER
                if (machine_is_netwinder()) {
-                       init_timer(&vnc_timer);
-                       vnc_timer.function = vnc_slider_tick;
-                       vnc_timer.expires  = jiffies;
-                       vnc_timer.data     = nr_waveartist_devs;
-                       add_timer(&vnc_timer);
+                       setup_timer(&vnc_timer, vnc_slider_tick,
+                                   nr_waveartist_devs);
+                       mod_timer(&vnc_timer, jiffies);
 
                        vnc_configure_mixer(devc, 0);
 
index 4667c3232b7f1d48d15d637bb73019ba40675958..4a054d72011246db38fbdba68c25f3da0c0ce372 100644 (file)
@@ -2151,8 +2151,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
                                                           stream->resources, en,
                                                           VORTEX_RESOURCE_SRC)) < 0) {
                                        memset(stream->resources, 0,
-                                              sizeof(unsigned char) *
-                                              VORTEX_RESOURCE_LAST);
+                                              sizeof(stream->resources));
                                        return -EBUSY;
                                }
                                if (stream->type != VORTEX_PCM_A3D) {
@@ -2162,7 +2161,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
                                                                   VORTEX_RESOURCE_MIXIN)) < 0) {
                                                memset(stream->resources,
                                                       0,
-                                                      sizeof(unsigned char) * VORTEX_RESOURCE_LAST);
+                                                      sizeof(stream->resources));
                                                return -EBUSY;
                                        }
                                }
@@ -2175,8 +2174,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
                                                   stream->resources, en,
                                                   VORTEX_RESOURCE_A3D)) < 0) {
                                memset(stream->resources, 0,
-                                      sizeof(unsigned char) *
-                                      VORTEX_RESOURCE_LAST);
+                                      sizeof(stream->resources));
                                dev_err(vortex->card->dev,
                                        "out of A3D sources. Sorry\n");
                                return -EBUSY;
@@ -2290,8 +2288,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
                                                   VORTEX_RESOURCE_MIXOUT))
                            < 0) {
                                memset(stream->resources, 0,
-                                      sizeof(unsigned char) *
-                                      VORTEX_RESOURCE_LAST);
+                                      sizeof(stream->resources));
                                return -EBUSY;
                        }
                        if ((src[i] =
@@ -2299,8 +2296,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
                                                   stream->resources, en,
                                                   VORTEX_RESOURCE_SRC)) < 0) {
                                memset(stream->resources, 0,
-                                      sizeof(unsigned char) *
-                                      VORTEX_RESOURCE_LAST);
+                                      sizeof(stream->resources));
                                return -EBUSY;
                        }
                }
index a6d6d8d0867add5446fd6a0126cca7b7041d8d3b..df5741a78fd23089eff63ef26d213f7341d8b60a 100644 (file)
@@ -432,7 +432,10 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
 #endif
        //printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
        spin_unlock(&chip->lock);
-       return (bytes_to_frames(substream->runtime, current_ptr));
+       current_ptr = bytes_to_frames(substream->runtime, current_ptr);
+       if (current_ptr >= substream->runtime->buffer_size)
+               current_ptr = 0;
+       return current_ptr;
 }
 
 /* operators */
index a5d460453d7bbf1895bf7e0085675ec7e36b9d60..8f945341720bb594d54694bd5f99be855dcd339a 100644 (file)
@@ -49,7 +49,7 @@ struct ct_timer {
        spinlock_t lock;                /* global timer lock (for xfitimer) */
        spinlock_t list_lock;           /* lock for instance list */
        struct ct_atc *atc;
-       struct ct_timer_ops *ops;
+       const struct ct_timer_ops *ops;
        struct list_head instance_head;
        struct list_head running_head;
        unsigned int wc;                /* current wallclock */
@@ -128,7 +128,7 @@ static void ct_systimer_prepare(struct ct_timer_instance *ti)
 
 #define ct_systimer_free       ct_systimer_prepare
 
-static struct ct_timer_ops ct_systimer_ops = {
+static const struct ct_timer_ops ct_systimer_ops = {
        .init = ct_systimer_init,
        .free_instance = ct_systimer_free,
        .prepare = ct_systimer_prepare,
@@ -322,7 +322,7 @@ static void ct_xfitimer_free_global(struct ct_timer *atimer)
        ct_xfitimer_irq_stop(atimer);
 }
 
-static struct ct_timer_ops ct_xfitimer_ops = {
+static const struct ct_timer_ops ct_xfitimer_ops = {
        .prepare = ct_xfitimer_prepare,
        .start = ct_xfitimer_start,
        .stop = ct_xfitimer_stop,
index 0dc44ebb00329e3266d6bad2e20b424588c9a321..626cd2167d29032e7369ed94c447445acbb9451d 100644 (file)
@@ -1548,7 +1548,7 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
        int val = 0;
        
        spin_lock_irq(&ensoniq->reg_lock);
-       if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4)
+       if (ensoniq->ctrl & ES_1371_GPIO_OUT(4))
                val = 1;
        ucontrol->value.integer.value[0] = val;
        spin_unlock_irq(&ensoniq->reg_lock);
index bb02c2d48fd508d2892d39b5ec58e976831d59b2..7f3b5ed819957df6aacb5ab483e55e50d9b4d49d 100644 (file)
@@ -50,9 +50,13 @@ config SND_HDA_RECONFIG
        bool "Allow dynamic codec reconfiguration"
        help
          Say Y here to enable the HD-audio codec re-configuration feature.
-         This adds the sysfs interfaces to allow user to clear the whole
-         codec configuration, change the codec setup, add extra verbs,
-         and re-configure the codec dynamically.
+         It allows user to clear the whole codec configuration, change the
+         codec setup, add extra verbs, and re-configure the codec dynamically.
+
+         Note that this item alone doesn't provide the sysfs interface, but
+         enables the feature just for the patch loader below.
+         If you need the traditional sysfs entries for the manual interaction,
+         turn on CONFIG_SND_HDA_HWDEP as well.
 
 config SND_HDA_INPUT_BEEP
        bool "Support digital beep via input layer"
index dfaf1a93fb8a3b8aba4fee3f3090a48b08ed2734..320445f3bf736d51e3dcfc884cdf5f7aef0b3855 100644 (file)
@@ -5434,6 +5434,7 @@ static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
        spec->cur_adc_stream_tag = stream_tag;
        spec->cur_adc_format = format;
        snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+       call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
        return 0;
 }
 
@@ -5444,6 +5445,7 @@ static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
        struct hda_gen_spec *spec = codec->spec;
        snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
        spec->cur_adc = 0;
+       call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP);
        return 0;
 }
 
index a010d704e0e20b066d912349088cfe0a27de0c31..d0d5ad8beac565b8e5bee7bb61f6e692081083ec 100644 (file)
@@ -114,6 +114,9 @@ struct hdmi_ops {
        int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
                            hda_nid_t pin_nid, u32 stream_tag, int format);
 
+       void (*pin_cvt_fixup)(struct hda_codec *codec,
+                             struct hdmi_spec_per_pin *per_pin,
+                             hda_nid_t cvt_nid);
 };
 
 struct hdmi_pcm {
@@ -684,7 +687,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
        if (!channels)
                return;
 
-       if (is_haswell_plus(codec))
+       /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
+       if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
                snd_hda_codec_write(codec, pin_nid, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
                                            AMP_OUT_UNMUTE);
@@ -864,9 +868,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
        struct hdmi_spec *spec = codec->spec;
        int err;
 
-       if (is_haswell_plus(codec))
-               haswell_verify_D0(codec, cvt_nid, pin_nid);
-
        err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
 
        if (err) {
@@ -884,7 +885,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
  * of the pin.
  */
 static int hdmi_choose_cvt(struct hda_codec *codec,
-                       int pin_idx, int *cvt_id, int *mux_id)
+                          int pin_idx, int *cvt_id)
 {
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin;
@@ -925,8 +926,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 
        if (cvt_id)
                *cvt_id = cvt_idx;
-       if (mux_id)
-               *mux_id = mux_idx;
 
        return 0;
 }
@@ -1019,9 +1018,6 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
        int mux_idx;
        struct hdmi_spec *spec = codec->spec;
 
-       if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
-               return;
-
        /* On Intel platform, the mapping of converter nid to
         * mux index of the pins are always the same.
         * The pin nid may be 0, this means all pins will not
@@ -1032,6 +1028,17 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
                intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
 }
 
+/* skeleton caller of pin_cvt_fixup ops */
+static void pin_cvt_fixup(struct hda_codec *codec,
+                         struct hdmi_spec_per_pin *per_pin,
+                         hda_nid_t cvt_nid)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       if (spec->ops.pin_cvt_fixup)
+               spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid);
+}
+
 /* called in hdmi_pcm_open when no pin is assigned to the PCM
  * in dyn_pcm_assign mode.
  */
@@ -1049,7 +1056,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
        if (pcm_idx < 0)
                return -EINVAL;
 
-       err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+       err = hdmi_choose_cvt(codec, -1, &cvt_idx);
        if (err)
                return err;
 
@@ -1057,7 +1064,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
        per_cvt->assigned = 1;
        hinfo->nid = per_cvt->cvt_nid;
 
-       intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+       pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid);
 
        set_bit(pcm_idx, &spec->pcm_in_use);
        /* todo: setup spdif ctls assign */
@@ -1089,7 +1096,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 {
        struct hdmi_spec *spec = codec->spec;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int pin_idx, cvt_idx, pcm_idx, mux_idx = 0;
+       int pin_idx, cvt_idx, pcm_idx;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
        struct hdmi_spec_per_cvt *per_cvt = NULL;
@@ -1118,7 +1125,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
                }
        }
 
-       err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
+       err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx);
        if (err < 0) {
                mutex_unlock(&spec->pcm_lock);
                return err;
@@ -1135,11 +1142,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 
        snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
                            AC_VERB_SET_CONNECT_SEL,
-                           mux_idx);
+                           per_pin->mux_idx);
 
        /* configure unused pins to choose other converters */
-       if (is_haswell_plus(codec) || is_valleyview_plus(codec))
-               intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
+       pin_cvt_fixup(codec, per_pin, 0);
 
        snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
 
@@ -1372,12 +1378,7 @@ static void update_eld(struct hda_codec *codec,
         *   and this can make HW reset converter selection on a pin.
         */
        if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
-               if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
-                       intel_verify_pin_cvt_connect(codec, per_pin);
-                       intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
-                                                    per_pin->mux_idx);
-               }
-
+               pin_cvt_fixup(codec, per_pin, 0);
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
        }
 
@@ -1484,7 +1485,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
 
        mutex_lock(&per_pin->lock);
        eld->monitor_present = false;
-       size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
+       size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
                                      &eld->monitor_present, eld->eld_buffer,
                                      ELD_MAX_SIZE);
        if (size > 0) {
@@ -1711,7 +1712,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                 * skip pin setup and return 0 to make audio playback
                 * be ongoing
                 */
-               intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+               pin_cvt_fixup(codec, NULL, cvt_nid);
                snd_hda_codec_setup_stream(codec, cvt_nid,
                                        stream_tag, 0, format);
                mutex_unlock(&spec->pcm_lock);
@@ -1724,23 +1725,21 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        }
        per_pin = get_pin(spec, pin_idx);
        pin_nid = per_pin->pin_nid;
-       if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
-               /* Verify pin:cvt selections to avoid silent audio after S3.
-                * After S3, the audio driver restores pin:cvt selections
-                * but this can happen before gfx is ready and such selection
-                * is overlooked by HW. Thus multiple pins can share a same
-                * default convertor and mute control will affect each other,
-                * which can cause a resumed audio playback become silent
-                * after S3.
-                */
-               intel_verify_pin_cvt_connect(codec, per_pin);
-               intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
-       }
+
+       /* Verify pin:cvt selections to avoid silent audio after S3.
+        * After S3, the audio driver restores pin:cvt selections
+        * but this can happen before gfx is ready and such selection
+        * is overlooked by HW. Thus multiple pins can share a same
+        * default convertor and mute control will affect each other,
+        * which can cause a resumed audio playback become silent
+        * after S3.
+        */
+       pin_cvt_fixup(codec, per_pin, 0);
 
        /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
        /* Todo: add DP1.2 MST audio support later */
        if (codec_has_acomp(codec))
-               snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
+               snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate);
 
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
        mutex_lock(&per_pin->lock);
@@ -1837,6 +1836,18 @@ static const struct hda_pcm_ops generic_ops = {
        .cleanup = generic_hdmi_playback_pcm_cleanup,
 };
 
+static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
+{
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+       if (!per_pin)
+               return 0;
+
+       return per_pin->sink_eld.info.spk_alloc;
+}
+
 static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
                                        unsigned char *chmap)
 {
@@ -2075,6 +2086,20 @@ static void hdmi_array_free(struct hdmi_spec *spec)
        snd_array_free(&spec->cvts);
 }
 
+static void generic_spec_free(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       if (spec) {
+               if (spec->i915_bound)
+                       snd_hdac_i915_exit(&codec->bus->core);
+               hdmi_array_free(spec);
+               kfree(spec);
+               codec->spec = NULL;
+       }
+       codec->dp_mst = false;
+}
+
 static void generic_hdmi_free(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -2099,10 +2124,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
                        spec->pcm_rec[pcm_idx].jack = NULL;
        }
 
-       if (spec->i915_bound)
-               snd_hdac_i915_exit(&codec->bus->core);
-       hdmi_array_free(spec);
-       kfree(spec);
+       generic_spec_free(codec);
 }
 
 #ifdef CONFIG_PM
@@ -2140,6 +2162,55 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
        .setup_stream                           = hdmi_setup_stream,
 };
 
+/* allocate codec->spec and assign/initialize generic parser ops */
+static int alloc_generic_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+
+       spec->ops = generic_standard_hdmi_ops;
+       mutex_init(&spec->pcm_lock);
+       snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+       spec->chmap.ops.get_chmap = hdmi_get_chmap;
+       spec->chmap.ops.set_chmap = hdmi_set_chmap;
+       spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+       spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc,
+
+       codec->spec = spec;
+       hdmi_array_init(spec, 4);
+
+       codec->patch_ops = generic_hdmi_patch_ops;
+
+       return 0;
+}
+
+/* generic HDMI parser */
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+       int err;
+
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
+       }
+
+       generic_hdmi_init_per_pins(codec);
+       return 0;
+}
+
+/*
+ * Intel codec parsers and helpers
+ */
+
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
                                             hda_nid_t nid)
 {
@@ -2217,12 +2288,23 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 static void intel_pin_eld_notify(void *audio_ptr, int port)
 {
        struct hda_codec *codec = audio_ptr;
-       int pin_nid = port + 0x04;
+       int pin_nid;
 
        /* we assume only from port-B to port-D */
        if (port < 1 || port > 3)
                return;
 
+       switch (codec->core.vendor_id) {
+       case 0x80860054: /* ILK */
+       case 0x80862804: /* ILK */
+       case 0x80862882: /* VLV */
+               pin_nid = port + 0x03;
+               break;
+       default:
+               pin_nid = port + 0x04;
+               break;
+       }
+
        /* skip notification during system suspend (but not in runtime PM);
         * the state will be updated at resume
         */
@@ -2236,93 +2318,159 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
        check_presence_and_report(codec, pin_nid);
 }
 
-static int patch_generic_hdmi(struct hda_codec *codec)
+/* register i915 component pin_eld_notify callback */
+static void register_i915_notifier(struct hda_codec *codec)
 {
-       struct hdmi_spec *spec;
+       struct hdmi_spec *spec = codec->spec;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
+       spec->use_acomp_notifier = true;
+       spec->i915_audio_ops.audio_ptr = codec;
+       /* intel_audio_codec_enable() or intel_audio_codec_disable()
+        * will call pin_eld_notify with using audio_ptr pointer
+        * We need make sure audio_ptr is really setup
+        */
+       wmb();
+       spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
+       snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+}
 
-       spec->ops = generic_standard_hdmi_ops;
-       mutex_init(&spec->pcm_lock);
-       snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+/* setup_stream ops override for HSW+ */
+static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
+                                hda_nid_t pin_nid, u32 stream_tag, int format)
+{
+       haswell_verify_D0(codec, cvt_nid, pin_nid);
+       return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+}
 
-       spec->chmap.ops.get_chmap = hdmi_get_chmap;
-       spec->chmap.ops.set_chmap = hdmi_set_chmap;
-       spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+/* pin_cvt_fixup ops override for HSW+ and VLV+ */
+static void i915_pin_cvt_fixup(struct hda_codec *codec,
+                              struct hdmi_spec_per_pin *per_pin,
+                              hda_nid_t cvt_nid)
+{
+       if (per_pin) {
+               intel_verify_pin_cvt_connect(codec, per_pin);
+               intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
+                                            per_pin->mux_idx);
+       } else {
+               intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+       }
+}
 
-       codec->spec = spec;
-       hdmi_array_init(spec, 4);
+/* Intel Haswell and onwards; audio component with eld notifier */
+static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
 
-#ifdef CONFIG_SND_HDA_I915
-       /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */
-       if ((codec->core.vendor_id >> 16) == 0x8086 &&
-           is_haswell_plus(codec)) {
-#if 0
-               /* on-demand binding leads to an unbalanced refcount when
-                * both i915 and hda drivers are probed concurrently;
-                * disabled temporarily for now
-                */
-               if (!codec->bus->core.audio_component)
-                       if (!snd_hdac_i915_init(&codec->bus->core))
-                               spec->i915_bound = true;
-#endif
-               /* use i915 audio component notifier for hotplug */
-               if (codec->bus->core.audio_component)
-                       spec->use_acomp_notifier = true;
+       /* HSW+ requires i915 binding */
+       if (!codec->bus->core.audio_component) {
+               codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+               return -ENODEV;
        }
-#endif
 
-       if (is_haswell_plus(codec)) {
-               intel_haswell_enable_all_pins(codec, true);
-               intel_haswell_fixup_enable_dp12(codec);
-       }
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+       spec = codec->spec;
 
-       /* For Valleyview/Cherryview, only the display codec is in the display
-        * power well and can use link_power ops to request/release the power.
-        * For Haswell/Broadwell, the controller is also in the power well and
+       intel_haswell_enable_all_pins(codec, true);
+       intel_haswell_fixup_enable_dp12(codec);
+
+       /* For Haswell/Broadwell, the controller is also in the power well and
         * can cover the codec power request, and so need not set this flag.
-        * For previous platforms, there is no such power well feature.
         */
-       if (is_valleyview_plus(codec) || is_skylake(codec) ||
-                       is_broxton(codec))
+       if (!is_haswell(codec) && !is_broadwell(codec))
                codec->core.link_power_control = 1;
 
-       if (hdmi_parse_codec(codec) < 0) {
-               if (spec->i915_bound)
-                       snd_hdac_i915_exit(&codec->bus->core);
-               codec->spec = NULL;
-               kfree(spec);
-               return -EINVAL;
+       codec->patch_ops.set_power_state = haswell_set_power_state;
+       codec->dp_mst = true;
+       codec->depop_delay = 0;
+       codec->auto_runtime_pm = 1;
+
+       spec->ops.setup_stream = i915_hsw_setup_stream;
+       spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
        }
-       codec->patch_ops = generic_hdmi_patch_ops;
-       if (is_haswell_plus(codec)) {
-               codec->patch_ops.set_power_state = haswell_set_power_state;
-               codec->dp_mst = true;
+
+       generic_hdmi_init_per_pins(codec);
+       register_i915_notifier(codec);
+       return 0;
+}
+
+/* Intel Baytrail and Braswell; with eld notifier */
+static int patch_i915_byt_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
+
+       /* requires i915 binding */
+       if (!codec->bus->core.audio_component) {
+               codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+               return -ENODEV;
        }
 
-       /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
-       if (is_haswell_plus(codec) || is_valleyview_plus(codec))
-               codec->auto_runtime_pm = 1;
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+       spec = codec->spec;
 
-       generic_hdmi_init_per_pins(codec);
+       /* For Valleyview/Cherryview, only the display codec is in the display
+        * power well and can use link_power ops to request/release the power.
+        */
+       codec->core.link_power_control = 1;
 
+       codec->depop_delay = 0;
+       codec->auto_runtime_pm = 1;
 
-       if (codec_has_acomp(codec)) {
-               codec->depop_delay = 0;
-               spec->i915_audio_ops.audio_ptr = codec;
-               /* intel_audio_codec_enable() or intel_audio_codec_disable()
-                * will call pin_eld_notify with using audio_ptr pointer
-                * We need make sure audio_ptr is really setup
-                */
-               wmb();
-               spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
-               snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+       spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
        }
 
-       WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
+       generic_hdmi_init_per_pins(codec);
+       register_i915_notifier(codec);
+       return 0;
+}
+
+/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */
+static int patch_i915_cpt_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
+
+       /* no i915 component should have been bound before this */
+       if (WARN_ON(codec->bus->core.audio_component))
+               return -EBUSY;
+
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+       spec = codec->spec;
+
+       /* Try to bind with i915 now */
+       err = snd_hdac_i915_init(&codec->bus->core);
+       if (err < 0)
+               goto error;
+       spec->i915_bound = true;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0)
+               goto error;
+
+       generic_hdmi_init_per_pins(codec);
+       register_i915_notifier(codec);
        return 0;
+
+ error:
+       generic_spec_free(codec);
+       return err;
 }
 
 /*
@@ -3492,21 +3640,21 @@ HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP",    patch_via_hdmi),
 HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP",   patch_via_hdmi),
 HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP",    patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI",   patch_i915_cpt_hdmi),
 HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI",   patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI",    patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI",  patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI",   patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI",        patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI",   patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI",        patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI",   patch_i915_hsw_hdmi),
 HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_i915_byt_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_i915_byt_hdmi),
 HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI",  patch_generic_hdmi),
 /* special ID for generic HDMI */
 HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
index 4918ffa5ba6829e102519b3a7af700b049d93ae2..002f153bc65948e2a5bd46c484acbfba42d5b875 100644 (file)
@@ -342,6 +342,11 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
        case 0x10ec0293:
                alc_update_coef_idx(codec, 0xa, 1<<13, 0);
                break;
+       case 0x10ec0234:
+       case 0x10ec0274:
+       case 0x10ec0294:
+               alc_update_coef_idx(codec, 0x10, 1<<15, 0);
+               break;
        case 0x10ec0662:
                if ((coef & 0x00f0) == 0x0030)
                        alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
@@ -2647,6 +2652,7 @@ enum {
        ALC269_TYPE_ALC255,
        ALC269_TYPE_ALC256,
        ALC269_TYPE_ALC225,
+       ALC269_TYPE_ALC294,
 };
 
 /*
@@ -2677,6 +2683,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        case ALC269_TYPE_ALC255:
        case ALC269_TYPE_ALC256:
        case ALC269_TYPE_ALC225:
+       case ALC269_TYPE_ALC294:
                ssids = alc269_ssids;
                break;
        default:
@@ -6028,6 +6035,11 @@ static int patch_alc269(struct hda_codec *codec)
        case 0x10ec0225:
                spec->codec_variant = ALC269_TYPE_ALC225;
                break;
+       case 0x10ec0234:
+       case 0x10ec0274:
+       case 0x10ec0294:
+               spec->codec_variant = ALC269_TYPE_ALC294;
+               break;
        }
 
        if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
@@ -6942,6 +6954,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
        HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
@@ -6952,6 +6965,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
        HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
@@ -6964,6 +6978,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
        HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269),
        HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
        HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
        HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
index 8151318a69a2fb65fa53f44f07233792338a2011..9720a30dbfff31fb412b385370647927e6ecef3a 100644 (file)
 #include <asm/pgtable.h>
 #include <asm/cacheflush.h>
 
-#ifdef CONFIG_KVM_GUEST
-#include <linux/kvm_para.h>
-#else
-#define kvm_para_available() (0)
-#endif
-
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
 MODULE_LICENSE("GPL");
@@ -2972,25 +2966,17 @@ static int snd_intel8x0_inside_vm(struct pci_dev *pci)
                goto fini;
        }
 
-       /* detect KVM and Parallels virtual environments */
-       result = kvm_para_available();
-#ifdef X86_FEATURE_HYPERVISOR
-       result = result || boot_cpu_has(X86_FEATURE_HYPERVISOR);
-#endif
-       if (!result)
-               goto fini;
-
        /* check for known (emulated) devices */
+       result = 0;
        if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
            pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
                /* KVM emulated sound, PCI SSID: 1af4:1100 */
                msg = "enable KVM";
+               result = 1;
        } else if (pci->subsystem_vendor == 0x1ab8) {
                /* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */
                msg = "enable Parallels VM";
-       } else {
-               msg = "disable (unknown or VT-d) VM";
-               result = 0;
+               result = 1;
        }
 
 fini:
index f3d62020ef66c95119461a8836a650f30071f39d..a80684bdc30d6ee3cdad09e6e99be4cef902b7c3 100644 (file)
@@ -644,7 +644,7 @@ static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
                if (err < 0)
                        return err;
 
-               if (current_state == state)
+               if (!err && current_state == state)
                        return 0;
 
                mdelay(1);
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 ac60b04540fd127526db3da069e097c79d039b7d..cdcced9f32b6220eeeff12b22a4b43821375e3d1 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 3f2c1ea3a83ea5eec0eacc87a0ae74779fdda802..d7ef292c402d385c6522b321df632996e64cd755 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);
 }
index 3fc63583a5372dbec8d09e7ba7cf7399abb54cd1..69860da473ea8b9d7ee853e9dfcdcc804690983f 100644 (file)
@@ -350,6 +350,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
        case USB_SPEED_HIGH:
        case USB_SPEED_WIRELESS:
        case USB_SPEED_SUPER:
+       case USB_SPEED_SUPER_PLUS:
                break;
        default:
                dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
@@ -450,6 +451,9 @@ static int snd_usb_audio_create(struct usb_interface *intf,
        case USB_SPEED_SUPER:
                strlcat(card->longname, ", super speed", sizeof(card->longname));
                break;
+       case USB_SPEED_SUPER_PLUS:
+               strlcat(card->longname, ", super speed plus", sizeof(card->longname));
+               break;
        default:
                break;
        }
index 7ccbcaf6a1476423e0d2becf0bf91f3463a47b46..26dd5f20f1494320735b47f60be79eab70b6e111 100644 (file)
@@ -309,6 +309,9 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
         * support reading */
        if (snd_usb_get_sample_rate_quirk(chip))
                return 0;
+       /* the firmware is likely buggy, don't repeat to fail too many times */
+       if (chip->sample_rate_read_error > 2)
+               return 0;
 
        if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
                                   USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
@@ -316,6 +319,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
                                   data, sizeof(data))) < 0) {
                dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
                        iface, fmt->altsetting, ep);
+               chip->sample_rate_read_error++;
                return 0; /* some devices don't support reading */
        }
 
index 51ed1ac825fdca80744a1351de727bdc4fddbfbe..7712e2b84183891336469d65b861cba0beb145e3 100644 (file)
@@ -120,6 +120,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
        case USB_SPEED_HIGH:
        case USB_SPEED_WIRELESS:
        case USB_SPEED_SUPER:
+       case USB_SPEED_SUPER_PLUS:
                if (get_endpoint(alts, 0)->bInterval >= 1 &&
                    get_endpoint(alts, 0)->bInterval <= 4)
                        return get_endpoint(alts, 0)->bInterval - 1;
index 47de8af42f16d031b8075dd10ebff204109fc391..7ba92921bf283b3ffc8c0987917afc7fe830c383 100644 (file)
@@ -911,6 +911,7 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
        switch (snd_usb_get_speed(ep->umidi->dev)) {
        case USB_SPEED_HIGH:
        case USB_SPEED_SUPER:
+       case USB_SPEED_SUPER_PLUS:
                count = 1;
                break;
        default:
index 4f85757009b3e44e2246cc21f4387d0948056da3..2f8c388ef84fc6148cf8ab736a845236a5520355 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/bitops.h>
 #include <linux/init.h>
 #include <linux/list.h>
+#include <linux/log2.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/usb.h>
@@ -1378,6 +1379,71 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
        snd_usb_mixer_add_control(&cval->head, kctl);
 }
 
+static int parse_clock_source_unit(struct mixer_build *state, int unitid,
+                                  void *_ftr)
+{
+       struct uac_clock_source_descriptor *hdr = _ftr;
+       struct usb_mixer_elem_info *cval;
+       struct snd_kcontrol *kctl;
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       int ret;
+
+       if (state->mixer->protocol != UAC_VERSION_2)
+               return -EINVAL;
+
+       if (hdr->bLength != sizeof(*hdr)) {
+               usb_audio_dbg(state->chip,
+                             "Bogus clock source descriptor length of %d, ignoring.\n",
+                             hdr->bLength);
+               return 0;
+       }
+
+       /*
+        * The only property of this unit we are interested in is the
+        * clock source validity. If that isn't readable, just bail out.
+        */
+       if (!uac2_control_is_readable(hdr->bmControls,
+                                     ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+               return 0;
+
+       cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+       if (!cval)
+               return -ENOMEM;
+
+       snd_usb_mixer_elem_init_std(&cval->head, state->mixer, hdr->bClockID);
+
+       cval->min = 0;
+       cval->max = 1;
+       cval->channels = 1;
+       cval->val_type = USB_MIXER_BOOLEAN;
+       cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
+
+       if (uac2_control_is_writeable(hdr->bmControls,
+                                     ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
+               kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+       else {
+               cval->master_readonly = 1;
+               kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
+       }
+
+       if (!kctl) {
+               kfree(cval);
+               return -ENOMEM;
+       }
+
+       kctl->private_free = snd_usb_mixer_elem_free;
+       ret = snd_usb_copy_string_desc(state, hdr->iClockSource,
+                                      name, sizeof(name));
+       if (ret > 0)
+               snprintf(kctl->id.name, sizeof(kctl->id.name),
+                        "%s Validity", name);
+       else
+               snprintf(kctl->id.name, sizeof(kctl->id.name),
+                        "Clock Source %d Validity", hdr->bClockID);
+
+       return snd_usb_mixer_add_control(&cval->head, kctl);
+}
+
 /*
  * parse a feature unit
  *
@@ -2126,10 +2192,11 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
 
        switch (p1[2]) {
        case UAC_INPUT_TERMINAL:
-       case UAC2_CLOCK_SOURCE:
                return 0; /* NOP */
        case UAC_MIXER_UNIT:
                return parse_audio_mixer_unit(state, unitid, p1);
+       case UAC2_CLOCK_SOURCE:
+               return parse_clock_source_unit(state, unitid, p1);
        case UAC_SELECTOR_UNIT:
        case UAC2_CLOCK_SELECTOR:
                return parse_audio_selector_unit(state, unitid, p1);
@@ -2307,6 +2374,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
        __u8 unitid = (index >> 8) & 0xff;
        __u8 control = (value >> 8) & 0xff;
        __u8 channel = value & 0xff;
+       unsigned int count = 0;
 
        if (channel >= MAX_CHANNELS) {
                usb_audio_dbg(mixer->chip,
@@ -2315,6 +2383,12 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
                return;
        }
 
+       for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem)
+               count++;
+
+       if (count == 0)
+               return;
+
        for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
                struct usb_mixer_elem_info *info;
 
@@ -2322,7 +2396,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
                        continue;
 
                info = (struct usb_mixer_elem_info *)list;
-               if (info->control != control)
+               if (count > 1 && info->control != control)
                        continue;
 
                switch (attribute) {
index b665d85555cb3aad0c9a621232f28d8e1f75344a..4d5c89a7ba2b5b97b62a1464ead6743710e34ed8 100644 (file)
@@ -47,6 +47,7 @@ struct snd_usb_audio {
        
        int num_interfaces;
        int num_suspended_intf;
+       int sample_rate_read_error;
 
        struct list_head pcm_list;      /* list of pcm streams */
        struct list_head ep_list;       /* list of audio-related endpoints */