浏览代码

Merge tag 'sound-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound updates from Takashi Iwai:
 "The majority of this update is about ASoC, including a few new
  drivers, and the rest are mostly minor changes.  The only substantial
  change in ALSA core is about the additional error handling in the
  compress-offload API.  Below are highlights:

   - Add the error propagating support in compress-offload API

   - HD-audio: a usual Dell headset fixup, an Intel HDMI/DP fix, and the
     default mixer setup change ot turn off the loopback

   - Lots of updates for ASoC Intel drivers, mostly board support and
     bug fixing, and to the NAU8825 driver

   - Work on generalizing bits of simple-card to allow more code sharing
     with the Renesas rsrc-card (which can't use simple-card due to DPCM)

   - Removal of the Odroid X2 driver due to replacement with simple-card

   - Support for several new Mediatek platforms and associated boards

   - New ASoC drivers for Allwinner A10, Analog Devices ADAU7002,
     Broadcom Cygnus, Cirrus Logic CS35L33 and CS53L30, Maxim MAX8960
     and MAX98504, Realtek RT5514 and Wolfson WM8758"

* tag 'sound-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (278 commits)
  sound: oss: Use kernel_read_file_from_path() for mod_firmware_load()
  ASoC: Intel: Skylake: Delete an unnecessary check before the function call "release_firmware"
  ASoC: Intel: Skylake: Fix NULL Pointer exception in dynamic_debug.
  ASoC: samsung: Specify DMA channels through struct snd_dmaengine_pcm_config
  ASoC: samsung: Fix error paths in the I2S driver's probe()
  ASoC: cs53l30: Fix bit shift issue of TDM mode
  ASoC: cs53l30: Fix a bug for TDM slot location validation
  ASoC: rockchip: correct the spdif clk
  ALSA: echoaudio: purge contradictions between dimension matrix members and total number of members
  ASoC: rsrc-card: use asoc_simple_card_parse_card_name()
  ASoC: rsrc-card: use asoc_simple_dai instead of rsrc_card_dai
  ASoC: rsrc-card: use asoc_simple_card_parse_dailink_name()
  ASoC: simple-card: use asoc_simple_card_parse_card_name()
  ASoC: simple-card-utils: add asoc_simple_card_parse_card_name()
  ASoC: simple-card: use asoc_simple_card_parse_dailink_name()
  ASoC: simple-card-utils: add asoc_simple_card_set_dailink_name()
  ASoC: nau8825: drop redundant idiom when converting integer to boolean
  ASoC: nau8825: jack connection decision with different insertion logic
  ASoC: mediatek: Add HDMI dai-links to the mt8173-rt5650 machine driver
  ASoC: mediatek: mt2701: fix non static symbol warning
  ...
Linus Torvalds 9 年之前
父节点
当前提交
c9b95e5961
共有 100 个文件被更改,包括 5647 次插入502 次删除
  1. 8 0
      Documentation/devicetree/bindings/sound/adi,adau17x1.txt
  2. 19 0
      Documentation/devicetree/bindings/sound/adi,adau7002.txt
  3. 67 0
      Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt
  4. 1 1
      Documentation/devicetree/bindings/sound/bt-sco.txt
  5. 126 0
      Documentation/devicetree/bindings/sound/cs35l33.txt
  6. 44 0
      Documentation/devicetree/bindings/sound/cs53l30.txt
  7. 4 0
      Documentation/devicetree/bindings/sound/designware-i2s.txt
  8. 1 1
      Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
  9. 44 0
      Documentation/devicetree/bindings/sound/max98504.txt
  10. 28 0
      Documentation/devicetree/bindings/sound/max9860.txt
  11. 150 0
      Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
  12. 43 0
      Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
  13. 8 2
      Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
  14. 10 0
      Documentation/devicetree/bindings/sound/omap-mcpdm.txt
  15. 2 0
      Documentation/devicetree/bindings/sound/renesas,rsnd.txt
  16. 5 0
      Documentation/devicetree/bindings/sound/rockchip-i2s.txt
  17. 5 0
      Documentation/devicetree/bindings/sound/rt5514.txt
  18. 0 35
      Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt
  19. 9 9
      Documentation/devicetree/bindings/sound/sgtl5000.txt
  20. 10 10
      Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
  21. 34 0
      Documentation/devicetree/bindings/sound/sun4i-i2s.txt
  22. 1 1
      Documentation/sound/alsa/soc/machine.txt
  23. 6 6
      Documentation/sound/alsa/timestamping.txt
  24. 10 1
      MAINTAINERS
  25. 28 0
      drivers/base/property.c
  26. 7 0
      include/acpi/acpi_bus.h
  27. 6 0
      include/linux/acpi.h
  28. 10 0
      include/linux/mfd/arizona/core.h
  29. 7 7
      include/linux/of.h
  30. 3 0
      include/linux/property.h
  31. 5 0
      include/sound/compress_driver.h
  32. 48 0
      include/sound/cs35l33.h
  33. 8 5
      include/sound/hdmi-codec.h
  34. 1 10
      include/sound/simple_card.h
  35. 36 0
      include/sound/simple_card_utils.h
  36. 7 1
      include/sound/soc-dapm.h
  37. 11 0
      include/sound/soc.h
  38. 0 1
      sound/Makefile
  39. 65 2
      sound/core/compress_offload.c
  40. 32 0
      sound/core/control.c
  41. 5 5
      sound/core/seq/oss/seq_oss_synth.c
  42. 9 14
      sound/core/seq/seq_timer.c
  43. 1 1
      sound/core/seq/seq_timer.h
  44. 25 3
      sound/hda/hdmi_chmap.c
  45. 1 1
      sound/i2c/other/ak4114.c
  46. 1 1
      sound/i2c/other/ak4117.c
  47. 1 12
      sound/isa/ad1848/ad1848.c
  48. 1 12
      sound/isa/adlib.c
  49. 1 12
      sound/isa/cmi8328.c
  50. 1 12
      sound/isa/cs423x/cs4231.c
  51. 1 12
      sound/isa/galaxy/galaxy.c
  52. 1 12
      sound/isa/gus/gusclassic.c
  53. 1 12
      sound/isa/gus/gusextreme.c
  54. 1 12
      sound/isa/gus/gusmax.c
  55. 1 12
      sound/isa/sb/jazz16.c
  56. 1 12
      sound/isa/sb/sb8.c
  57. 1 12
      sound/isa/sc6000.c
  58. 1 1
      sound/oss/ad1848.c
  59. 7 7
      sound/oss/aedsp16.c
  60. 28 1
      sound/oss/sound_firmware.h
  61. 1 1
      sound/oss/sound_timer.c
  62. 1 1
      sound/oss/sys_timer.c
  63. 17 17
      sound/pci/ctxfi/cthw20k2.c
  64. 3 3
      sound/pci/echoaudio/echoaudio.c
  65. 7 1
      sound/pci/hda/hda_codec.c
  66. 0 4
      sound/pci/hda/hda_generic.c
  67. 5 0
      sound/pci/hda/patch_hdmi.c
  68. 37 1
      sound/pci/hda/patch_realtek.c
  69. 1 1
      sound/pci/mixart/mixart_mixer.c
  70. 1 1
      sound/pci/riptide/riptide.c
  71. 1 0
      sound/ppc/awacs.c
  72. 3 13
      sound/sh/aica.c
  73. 1 0
      sound/soc/atmel/Kconfig
  74. 0 5
      sound/soc/atmel/atmel-classd.c
  75. 0 5
      sound/soc/atmel/atmel-pdmic.c
  76. 1 1
      sound/soc/atmel/atmel_ssc_dai.c
  77. 9 0
      sound/soc/bcm/Kconfig
  78. 5 0
      sound/soc/bcm/Makefile
  79. 861 0
      sound/soc/bcm/cygnus-pcm.c
  80. 1529 0
      sound/soc/bcm/cygnus-ssp.c
  81. 139 0
      sound/soc/bcm/cygnus-ssp.h
  82. 36 2
      sound/soc/codecs/Kconfig
  83. 14 0
      sound/soc/codecs/Makefile
  84. 61 0
      sound/soc/codecs/adau-utils.c
  85. 7 0
      sound/soc/codecs/adau-utils.h
  86. 10 28
      sound/soc/codecs/adau1373.c
  87. 1 1
      sound/soc/codecs/adau1761-i2c.c
  88. 1 1
      sound/soc/codecs/adau1761-spi.c
  89. 1 1
      sound/soc/codecs/adau1781-i2c.c
  90. 1 1
      sound/soc/codecs/adau1781-spi.c
  91. 162 89
      sound/soc/codecs/adau17x1.c
  92. 6 0
      sound/soc/codecs/adau17x1.h
  93. 80 0
      sound/soc/codecs/adau7002.c
  94. 11 1
      sound/soc/codecs/ak4613.c
  95. 11 2
      sound/soc/codecs/ak4642.c
  96. 46 45
      sound/soc/codecs/arizona.c
  97. 19 3
      sound/soc/codecs/arizona.h
  98. 36 16
      sound/soc/codecs/bt-sco.c
  99. 1303 0
      sound/soc/codecs/cs35l33.c
  100. 221 0
      sound/soc/codecs/cs35l33.h

+ 8 - 0
Documentation/devicetree/bindings/sound/adi,adau17x1.txt

@@ -13,6 +13,11 @@ Required properties:
  - reg:			The i2c address. Value depends on the state of ADDR0
 			and ADDR1, as wired in hardware.
 
+Optional properties:
+ - clock-names:		If provided must be "mclk".
+ - clocks:		phandle + clock-specifiers for the clock that provides
+			the audio master clock for the device.
+
 Examples:
 #include <dt-bindings/sound/adau17x1.h>
 
@@ -20,5 +25,8 @@ Examples:
 		adau1361@38 {
 			compatible = "adi,adau1761";
 			reg = <0x38>;
+
+			clock-names = "mclk";
+			clocks = <&audio_clock>;
 		};
 	};

+ 19 - 0
Documentation/devicetree/bindings/sound/adi,adau7002.txt

@@ -0,0 +1,19 @@
+Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter
+
+Required properties:
+
+ - compatible: Must be "adi,adau7002"
+
+Optional properties:
+
+ - IOVDD-supply: Phandle and specifier for the power supply providing the IOVDD
+	supply as covered in Documentation/devicetree/bindings/regulator/regulator.txt
+
+	If this property is not present it is assumed that the supply pin is
+	hardwired to always on.
+
+Example:
+	adau7002: pdm-to-i2s {
+		compatible = "adi,adau7002";
+		IOVDD-supply = <&supply>;
+	};

+ 67 - 0
Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt

@@ -0,0 +1,67 @@
+BROADCOM Cygnus Audio I2S/TDM/SPDIF controller
+
+Required properties:
+	- compatible : "brcm,cygnus-audio"
+	- #address-cells: 32bit valued, 1 cell.
+	- #size-cells:  32bit valued, 0 cell.
+	- reg : Should contain audio registers location and length
+	- reg-names: names of the registers listed in "reg" property
+		Valid names are "aud" and "i2s_in". "aud" contains a
+		set of DMA, I2S_OUT and SPDIF registers. "i2s_in" contains
+		a set of I2S_IN registers.
+	- clocks: PLL and leaf clocks used by audio ports
+	- assigned-clocks: PLL and leaf clocks
+	- assigned-clock-parents: parent clocks of the assigned clocks
+		(usually the PLL)
+	- assigned-clock-rates: List of clock frequencies of the
+		assigned clocks
+	- clock-names: names of 3 leaf clocks used by audio ports
+		Valid names are "ch0_audio", "ch1_audio", "ch2_audio"
+	- interrupts: audio DMA interrupt number
+
+SSP Subnode properties:
+- reg: The index of ssp port interface to use
+	Valid value are 0, 1, 2, or 3 (for spdif)
+
+Example:
+	cygnus_audio: audio@180ae000 {
+		compatible = "brcm,cygnus-audio";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x180ae000 0xafd>, <0x180aec00 0x1f8>;
+		reg-names = "aud", "i2s_in";
+		clocks = <&audiopll BCM_CYGNUS_AUDIOPLL_CH0>,
+				<&audiopll BCM_CYGNUS_AUDIOPLL_CH1>,
+				<&audiopll BCM_CYGNUS_AUDIOPLL_CH2>;
+		assigned-clocks = <&audiopll BCM_CYGNUS_AUDIOPLL>,
+							<&audiopll BCM_CYGNUS_AUDIOPLL_CH0>,
+							<&audiopll BCM_CYGNUS_AUDIOPLL_CH1>,
+							<&audiopll BCM_CYGNUS_AUDIOPLL_CH2>;
+		assigned-clock-parents = <&audiopll BCM_CYGNUS_AUDIOPLL>;
+		assigned-clock-rates = <1769470191>,
+								<0>,
+								<0>,
+								<0>;
+		clock-names = "ch0_audio", "ch1_audio", "ch2_audio";
+		interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+
+		ssp0: ssp_port@0 {
+			reg = <0>;
+			status = "okay";
+		};
+
+		ssp1: ssp_port@1 {
+			reg = <1>;
+			status = "disabled";
+		};
+
+		ssp2: ssp_port@2 {
+			reg = <2>;
+			status = "disabled";
+		};
+
+		spdif: spdif_port@3 {
+			reg = <3>;
+			status = "disabled";
+		};
+	};

+ 1 - 1
Documentation/devicetree/bindings/sound/bt-sco.txt

@@ -4,7 +4,7 @@ This device support generic Bluetooth SCO link.
 
 Required properties:
 
-  - compatible : "delta,dfbmcs320"
+  - compatible : "delta,dfbmcs320" or "linux,bt-sco"
 
 Example:
 

+ 126 - 0
Documentation/devicetree/bindings/sound/cs35l33.txt

@@ -0,0 +1,126 @@
+CS35L33 Speaker Amplifier
+
+Required properties:
+
+  - compatible : "cirrus,cs35l33"
+
+  - reg : the I2C address of the device for I2C
+
+  - VA-supply, VP-supply : power supplies for the device,
+    as covered in
+    Documentation/devicetree/bindings/regulator/regulator.txt.
+
+Optional properties:
+
+  - reset-gpios : gpio used to reset the amplifier
+
+  - interrupt-parent : Specifies the phandle of the interrupt controller to
+    which the IRQs from CS35L33 are delivered to.
+ -  interrupts : IRQ line info CS35L33.
+    (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+    for further information relating to interrupt properties)
+
+  - cirrus,boost-ctl : Booster voltage use to supply the amp. If the value is
+    0, then VBST = VP. If greater than 0, the boost voltage will be 3300mV with
+    a value of 1 and will increase at a step size of 100mV until a maximum of
+    8000mV.
+
+  - cirrus,ramp-rate : On power up, it affects the time from when the power
+    up sequence begins to the time the audio reaches a full-scale output.
+    On power down, it affects the time from when the power-down sequence
+    begins to when the amplifier disables the PWM outputs. If this property
+    is not set then soft ramping will be disabled and ramp time would be
+    20ms. If this property is set to 0,1,2,3 then ramp times would be 40ms,
+    60ms,100ms,175ms respectively for 48KHz sample rate.
+
+  - cirrus,boost-ipk : The maximum current allowed for the boost converter.
+    The range starts at 1850000uA and goes to a maximum of 3600000uA
+    with a step size of 15625uA. The default is 2500000uA.
+
+  - cirrus,imon-adc-scale : Configures the scaling of data bits from the IMON
+    ADC data word. This property can be set as a value of 0 for bits 15 down
+    to 0, 6 for 21 down to 6, 7, for 22 down to 7, 8 for 23 down to 8.
+
+
+Optional H/G Algorithm sub-node:
+
+The cs35l33 node can have a single "cirrus,hg-algo" sub-node that will enable
+the internal H/G Algorithm.
+
+  - cirrus,hg-algo : Sub-node for internal Class H/G algorithm that
+    controls the amplifier supplies.
+
+Optional properties for the "cirrus,hg-algo" sub-node:
+
+  - cirrus,mem-depth : Memory depth for the Class H/G algorithm measured in
+    LRCLK cycles. If this property is set to 0, 1, 2, or 3 then the memory
+    depths will be 1, 4, 8, 16 LRCLK cycles.  The default is 16 LRCLK cycles.
+
+    cirrus,release-rate : The number of consecutive LRCLK periods before
+    allowing release condition tracking updates. The number of LRCLK periods
+    start at 3 to a maximum of 255.
+
+  - cirrus,ldo-thld : Configures the signal threshold at which the PWM output
+    stage enters LDO operation. Starts as a default value of 50mV for a value
+    of 1 and increases with a step size of 50mV to a maximum of 750mV (value of
+    0xF).
+
+  - cirrus,ldo-path-disable : This is a boolean property. If present, the H/G
+    algorithm uses the max detection path.  If not present, the LDO
+    detection path is used.
+
+  - cirrus,ldo-entry-delay : The LDO entry delay in milliseconds before the H/G
+    algorithm switches to the LDO voltage.  This property can be set to values
+    from 0 to 7 for delays of 5ms, 10ms, 50ms, 100ms, 200ms, 500ms, 1000ms.
+    The default is 100ms.
+
+  - cirrus,vp-hg-auto : This is a boolean property.  When set, class H/G VPhg
+    automatic updating is enabled.
+
+  - cirrus,vp-hg :  Class H/G algorithm VPhg.  Controls the H/G algorithm's
+    reference to the VP voltage for when to start generating a boosted VBST.
+    The reference voltage starts at 3000mV with a value of 0x3 and is increased
+    by 100mV per step to a maximum of 5500mV.
+
+  - cirrus,vp-hg-rate : The rate (number of LRCLK periods) at which the VPhg is
+    allowed to increase to a higher voltage when using VPhg automatic
+    tracking. This property can be set to values from 0 to 3 with rates of 128
+    periods, 2048 periods, 32768 periods, and 524288 periods.
+    The default is 32768 periods.
+
+  - cirrus,vp-hg-va : VA calculation reference for automatic VPhg tracking
+    using VPMON. This property can be set to values from 0 to 6 starting at
+    1800mV with a step size of 50mV up to a maximum value of 1750mV.
+    Default is 1800mV.
+
+Example:
+
+cs35l33: cs35l33@40 {
+	compatible = "cirrus,cs35l33";
+	reg = <0x40>;
+
+	VA-supply = <&ldo5_reg>;
+	VP-supply = <&ldo5_reg>;
+
+	interrupt-parent = <&gpio8>;
+	interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+
+	reset-gpios = <&cs47l91 34 0>;
+
+	cirrus,ramp-rate = <0x0>;
+	cirrus,boost-ctl = <0x30>;  /* VBST = 8000mV */
+	cirrus,boost-ipk = <0xE0>; /* 3600mA */
+	cirrus,imon-adc-scale = <0> /* Bits 15 down to 0 */
+
+	cirrus,hg-algo {
+		cirrus,mem-depth = <0x3>;
+		cirrus,release-rate = <0x3>;
+		cirrus,ldo-thld = <0x1>;
+		cirrus,ldo-path-disable = <0x0>;
+		cirrus,ldo-entry-delay=<0x4>;
+		cirrus,vp-hg-auto;
+		cirrus,vp-hg=<0xF>;
+		cirrus,vp-hg-rate=<0x2>;
+		cirrus,vp-hg-va=<0x0>;
+	};
+};

+ 44 - 0
Documentation/devicetree/bindings/sound/cs53l30.txt

@@ -0,0 +1,44 @@
+CS53L30 audio CODEC
+
+Required properties:
+
+  - compatible : "cirrus,cs53l30"
+
+  - reg : the I2C address of the device
+
+  - VA-supply, VP-supply : power supplies for the device,
+    as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
+
+Optional properties:
+
+  - reset-gpios : a GPIO spec for the reset pin.
+
+  - mute-gpios : a GPIO spec for the MUTE pin. The active state can be either
+		 GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW, which would be handled
+		 by the driver automatically.
+
+  - cirrus,micbias-lvl : Set the output voltage level on the MICBIAS Pin.
+			 0 = Hi-Z
+			 1 = 1.80 V
+			 2 = 2.75 V
+
+  - cirrus,use-sdout2 : This is a boolean property. If present, it indicates
+			the hardware design connects both SDOUT1 and SDOUT2
+			pins to output data. Otherwise, it indicates that
+			only SDOUT1 is connected for data output.
+			* CS53l30 supports 4-channel data output in the same
+			* frame using two different ways:
+			* 1) Normal I2S mode on two data pins -- each SDOUT
+			*    carries 2-channel data in the same time.
+			* 2) TDM mode on one signle data pin -- SDOUT1 carries
+			*    4-channel data per frame.
+
+Example:
+
+codec: cs53l30@48 {
+	compatible = "cirrus,cs53l30";
+	reg = <0x48>;
+	reset-gpios = <&gpio 54 0>;
+	VA-supply = <&cs53l30_va>;
+	VP-supply = <&cs53l30_vp>;
+};

+ 4 - 0
Documentation/devicetree/bindings/sound/designware-i2s.txt

@@ -12,6 +12,10 @@ Required properties:
    one for receive.
  - dma-names : "tx" for the transmit channel, "rx" for the receive channel.
 
+Optional properties:
+ - interrupts: The interrupt line number for the I2S controller. Add this
+   parameter if the I2S controller that you are using does not support DMA.
+
 For more details on the 'dma', 'dma-names', 'clock' and 'clock-names'
 properties please check:
 	* resource-names.txt

+ 1 - 1
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt

@@ -58,7 +58,7 @@ Required properties:
 			   * DMIC (stands for Digital Microphone Jack)
 
 			  Note: The "Mic Jack" and "AMIC" are redundant while
-			        coexsiting in order to support the old bindings
+			        coexisting in order to support the old bindings
 				of wm8962 and sgtl5000.
 
 Optional properties:

+ 44 - 0
Documentation/devicetree/bindings/sound/max98504.txt

@@ -0,0 +1,44 @@
+Maxim MAX98504 class D mono speaker amplifier
+
+This device supports I2C control interface and an IRQ output signal. It features
+a PCM and PDM digital audio interface (DAI) and a differential analog input.
+
+Required properties:
+
+ - compatible : "maxim,max98504"
+ - reg : should contain the I2C slave device address
+ - DVDD-supply, DIOVDD-supply, PVDD-supply: power supplies for the device,
+   as covered in ../regulator/regulator.txt
+ - interrupts : should specify the interrupt line the device is connected to,
+   as described in ../interrupt-controller/interrupts.txt
+
+Optional properties:
+
+ - maxim,brownout-threshold - the PVDD brownout threshold, the value must be
+   from 0, 1...21 range, corresponding to 2.6V, 2.65V...3.65V voltage range
+ - maxim,brownout-attenuation - the brownout attenuation to the speaker gain
+   applied during the "attack hold" and "timed hold" phase, the value must be
+   from 0...6 (dB) range
+ - maxim,brownout-attack-hold-ms - the brownout attack hold phase time in ms,
+   0...255 (VBATBROWN_ATTK_HOLD, register 0x0018)
+ - maxim,brownout-timed-hold-ms - the brownout timed hold phase time in ms,
+   0...255 (VBATBROWN_TIME_HOLD, register 0x0019)
+ - maxim,brownout-release-rate-ms - the brownout release phase step time in ms,
+   0...255 (VBATBROWN_RELEASE, register 0x001A)
+
+The default value when the above properties are not specified is 0,
+the maxim,brownout-threshold property must be specified to actually enable
+the PVDD brownout protection.
+
+Example:
+
+ max98504@31 {
+	compatible = "maxim,max98504";
+	reg = <0x31>;
+	interrupt-parent = <&gpio_bank_0>;
+	interrupts = <2 0>;
+
+	DVDD-supply = <&regulator>;
+	DIOVDD-supply = <&regulator>;
+	PVDD-supply = <&regulator>;
+};

+ 28 - 0
Documentation/devicetree/bindings/sound/max9860.txt

@@ -0,0 +1,28 @@
+MAX9860 Mono Audio Voice Codec
+
+Required properties:
+
+  - compatible : "maxim,max9860"
+
+  - reg : the I2C address of the device
+
+  - AVDD-supply, DVDD-supply and DVDDIO-supply : power supplies for
+    the device, as covered in bindings/regulator/regulator.txt
+
+  - clock-names : Required element: "mclk".
+
+  - clocks : A clock specifier for the clock connected as MCLK.
+
+Examples:
+
+	max9860: max9860@10 {
+		compatible = "maxim,max9860";
+		reg = <0x10>;
+
+		AVDD-supply = <&reg_1v8>;
+		DVDD-supply = <&reg_1v8>;
+		DVDDIO-supply = <&reg_3v0>;
+
+		clock-names = "mclk";
+		clocks = <&pck2>;
+	};

+ 150 - 0
Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt

@@ -0,0 +1,150 @@
+Mediatek AFE PCM controller for mt2701
+
+Required properties:
+- compatible = "mediatek,mt2701-audio";
+- reg: register location and size
+- interrupts: Should contain AFE interrupt
+- clock-names: should have these clock names:
+		"infra_sys_audio_clk",
+		"top_audio_mux1_sel",
+		"top_audio_mux2_sel",
+		"top_audio_mux1_div",
+		"top_audio_mux2_div",
+		"top_audio_48k_timing",
+		"top_audio_44k_timing",
+		"top_audpll_mux_sel",
+		"top_apll_sel",
+		"top_aud1_pll_98M",
+		"top_aud2_pll_90M",
+		"top_hadds2_pll_98M",
+		"top_hadds2_pll_294M",
+		"top_audpll",
+		"top_audpll_d4",
+		"top_audpll_d8",
+		"top_audpll_d16",
+		"top_audpll_d24",
+		"top_audintbus_sel",
+		"clk_26m",
+		"top_syspll1_d4",
+		"top_aud_k1_src_sel",
+		"top_aud_k2_src_sel",
+		"top_aud_k3_src_sel",
+		"top_aud_k4_src_sel",
+		"top_aud_k5_src_sel",
+		"top_aud_k6_src_sel",
+		"top_aud_k1_src_div",
+		"top_aud_k2_src_div",
+		"top_aud_k3_src_div",
+		"top_aud_k4_src_div",
+		"top_aud_k5_src_div",
+		"top_aud_k6_src_div",
+		"top_aud_i2s1_mclk",
+		"top_aud_i2s2_mclk",
+		"top_aud_i2s3_mclk",
+		"top_aud_i2s4_mclk",
+		"top_aud_i2s5_mclk",
+		"top_aud_i2s6_mclk",
+		"top_asm_m_sel",
+		"top_asm_h_sel",
+		"top_univpll2_d4",
+		"top_univpll2_d2",
+		"top_syspll_d5";
+
+Example:
+
+	afe: mt2701-afe-pcm@11220000 {
+		compatible = "mediatek,mt2701-audio";
+		reg = <0 0x11220000 0 0x2000>,
+		      <0 0x112A0000 0 0x20000>;
+		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_AUDIO>,
+			 <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+			 <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+			 <&topckgen CLK_TOP_AUD_48K_TIMING>,
+			 <&topckgen CLK_TOP_AUD_44K_TIMING>,
+			 <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
+			 <&topckgen CLK_TOP_APLL_SEL>,
+			 <&topckgen CLK_TOP_AUD1PLL_98M>,
+			 <&topckgen CLK_TOP_AUD2PLL_90M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_98M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_294M>,
+			 <&topckgen CLK_TOP_AUDPLL>,
+			 <&topckgen CLK_TOP_AUDPLL_D4>,
+			 <&topckgen CLK_TOP_AUDPLL_D8>,
+			 <&topckgen CLK_TOP_AUDPLL_D16>,
+			 <&topckgen CLK_TOP_AUDPLL_D24>,
+			 <&topckgen CLK_TOP_AUDINTBUS_SEL>,
+			 <&clk26m>,
+			 <&topckgen CLK_TOP_SYSPLL1_D4>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_I2S1_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S2_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S3_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S4_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
+			 <&topckgen CLK_TOP_ASM_M_SEL>,
+			 <&topckgen CLK_TOP_ASM_H_SEL>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D4>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D2>,
+			 <&topckgen CLK_TOP_SYSPLL_D5>;
+
+		clock-names = "infra_sys_audio_clk",
+			      "top_audio_mux1_sel",
+			      "top_audio_mux2_sel",
+			      "top_audio_mux1_div",
+			      "top_audio_mux2_div",
+			      "top_audio_48k_timing",
+			      "top_audio_44k_timing",
+			      "top_audpll_mux_sel",
+			      "top_apll_sel",
+			      "top_aud1_pll_98M",
+			      "top_aud2_pll_90M",
+			      "top_hadds2_pll_98M",
+			      "top_hadds2_pll_294M",
+			      "top_audpll",
+			      "top_audpll_d4",
+			      "top_audpll_d8",
+			      "top_audpll_d16",
+			      "top_audpll_d24",
+			      "top_audintbus_sel",
+			      "clk_26m",
+			      "top_syspll1_d4",
+			      "top_aud_k1_src_sel",
+			      "top_aud_k2_src_sel",
+			      "top_aud_k3_src_sel",
+			      "top_aud_k4_src_sel",
+			      "top_aud_k5_src_sel",
+			      "top_aud_k6_src_sel",
+			      "top_aud_k1_src_div",
+			      "top_aud_k2_src_div",
+			      "top_aud_k3_src_div",
+			      "top_aud_k4_src_div",
+			      "top_aud_k5_src_div",
+			      "top_aud_k6_src_div",
+			      "top_aud_i2s1_mclk",
+			      "top_aud_i2s2_mclk",
+			      "top_aud_i2s3_mclk",
+			      "top_aud_i2s4_mclk",
+			      "top_aud_i2s5_mclk",
+			      "top_aud_i2s6_mclk",
+			      "top_asm_m_sel",
+			      "top_asm_h_sel",
+			      "top_univpll2_d4",
+			      "top_univpll2_d2",
+			      "top_syspll_d5";
+	};

+ 43 - 0
Documentation/devicetree/bindings/sound/mt2701-cs42448.txt

@@ -0,0 +1,43 @@
+MT2701 with CS42448 CODEC
+
+Required properties:
+- compatible: "mediatek,mt2701-cs42448-machine"
+- mediatek,platform: the phandle of MT2701 ASoC platform
+- audio-routing: a list of the connections between audio
+- mediatek,audio-codec: the phandles of cs42448 codec
+- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
+- pinctrl-names: Should contain only one value - "default"
+- pinctrl-0: Should specify pin control groups used for this controller.
+- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
+					control I2S1-in mux.
+
+Example:
+
+	sound:sound {
+		compatible = "mediatek,mt2701-cs42448-machine";
+		mediatek,platform = <&afe>;
+		/* CS42448 Machine name */
+		audio-routing =
+			"Line Out Jack", "AOUT1L",
+			"Line Out Jack", "AOUT1R",
+			"Line Out Jack", "AOUT2L",
+			"Line Out Jack", "AOUT2R",
+			"Line Out Jack", "AOUT3L",
+			"Line Out Jack", "AOUT3R",
+			"Line Out Jack", "AOUT4L",
+			"Line Out Jack", "AOUT4R",
+			"AIN1L", "AMIC",
+			"AIN1R", "AMIC",
+			"AIN2L", "Tuner In",
+			"AIN2R", "Tuner In",
+			"AIN3L", "Satellite Tuner In",
+			"AIN3R", "Satellite Tuner In",
+			"AIN3L", "AUX In",
+			"AIN3R", "AUX In";
+		mediatek,audio-codec = <&cs42448>;
+		mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&aud_pins_default>;
+		i2s1-in-sel-gpio1 = <&pio 53 0>;
+		i2s1-in-sel-gpio2 = <&pio 54 0>;
+	};

+ 8 - 2
Documentation/devicetree/bindings/sound/mt8173-rt5650.txt

@@ -1,8 +1,9 @@
-MT8173 with RT5650 CODECS
+MT8173 with RT5650 CODECS and HDMI via I2S
 
 Required properties:
 - compatible : "mediatek,mt8173-rt5650"
 - mediatek,audio-codec: the phandles of rt5650 codecs
+                        and of the hdmi encoder node
 - mediatek,platform: the phandle of MT8173 ASoC platform
 
 Optional subnodes:
@@ -12,12 +13,17 @@ Required codec-capture subnode properties:
   <&rt5650 0> : Default setting. Connect rt5650 I2S1 for capture. (dai_name = rt5645-aif1)
   <&rt5650 1> : Connect rt5650 I2S2 for capture. (dai_name = rt5645-aif2)
 
+- mediatek,mclk: the MCLK source
+  0 : external oscillator, MCLK = 12.288M
+  1 : internal source from mt8173, MCLK = sampling rate*256
+
 Example:
 
 	sound {
 		compatible = "mediatek,mt8173-rt5650";
-		mediatek,audio-codec = <&rt5650>;
+		mediatek,audio-codec = <&rt5650 &hdmi0>;
 		mediatek,platform = <&afe>;
+		mediatek,mclk = <0>;
 		codec-capture {
 			sound-dai = <&rt5650 1>;
 		};

+ 10 - 0
Documentation/devicetree/bindings/sound/omap-mcpdm.txt

@@ -8,6 +8,8 @@ Required properties:
 - interrupts: Interrupt number for McPDM
 - interrupt-parent: The parent interrupt controller
 - ti,hwmods: Name of the hwmod associated to the McPDM
+- clocks:  phandle for the pdmclk provider, likely <&twl6040>
+- clock-names: Must be "pdmclk"
 
 Example:
 
@@ -19,3 +21,11 @@ mcpdm: mcpdm@40132000 {
 	interrupt-parent = <&gic>;
 	ti,hwmods = "mcpdm";
 };
+
+In board DTS file the pdmclk needs to be added:
+
+&mcpdm {
+	clocks = <&twl6040>;
+	clock-names = "pdmclk";
+	status = "okay";
+};

+ 2 - 0
Documentation/devicetree/bindings/sound/renesas,rsnd.txt

@@ -373,6 +373,8 @@ Optional properties:
 - #clock-cells			: it must be 0 if your system has audio_clkout
 				  it must be 1 if your system has audio_clkout0/1/2/3
 - clock-frequency		: for all audio_clkout0/1/2/3
+- clkout-lr-asynchronous	: boolean property. it indicates that audio_clkoutn
+				  is asynchronizes with lr-clock.
 
 SSI subnode properties:
 - interrupts			: Should contain SSI interrupt for PIO transfer

+ 5 - 0
Documentation/devicetree/bindings/sound/rockchip-i2s.txt

@@ -23,6 +23,11 @@ Required properties:
 - rockchip,playback-channels: max playback channels, if not set, 8 channels default.
 - rockchip,capture-channels: max capture channels, if not set, 2 channels default.
 
+Required properties for controller which support multi channels
+playback/capture:
+
+- rockchip,grf: the phandle of the syscon node for GRF register.
+
 Example for rk3288 I2S controller:
 
 i2s@ff890000 {

+ 5 - 0
Documentation/devicetree/bindings/sound/rt5514.txt

@@ -8,6 +8,11 @@ Required properties:
 
 - reg : The I2C address of the device.
 
+Optional properties:
+
+- clocks: The phandle of the master clock to the CODEC
+- clock-names: Should be "mclk"
+
 Pins on the device (for linking into audio routes) for RT5514:
 
   * DMIC1L

+ 0 - 35
Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt

@@ -1,35 +0,0 @@
-Samsung Exynos Odroid X2/U3 audio complex with MAX98090 codec
-
-Required properties:
- - compatible : "samsung,odroidx2-audio" - for Odroid X2 board,
-		"samsung,odroidu3-audio" - for Odroid U3 board
- - samsung,model : the user-visible name of this sound complex
- - samsung,i2s-controller : the phandle of the I2S controller
- - samsung,audio-codec : the phandle of the MAX98090 audio codec
- - samsung,audio-routing : a list of the connections between audio
-   components;  each entry is a pair of strings, the first being the
-   connection's sink, the second being the connection's source;
-   valid names for sources and sinks are the MAX98090's pins (as
-   documented in its binding), and the jacks on the board
-   For Odroid X2:
-     * Headphone Jack
-     * Mic Jack
-     * DMIC
-
-   For Odroid U3:
-     * Headphone Jack
-     * Speakers
-
-Example:
-
-sound {
-	compatible = "samsung,odroidu3-audio";
-	samsung,i2s-controller = <&i2s0>;
-	samsung,audio-codec = <&max98090>;
-	samsung,model = "Odroid-X2";
-	samsung,audio-routing =
-		"Headphone Jack", "HPL",
-		"Headphone Jack", "HPR",
-		"IN1", "Mic Jack",
-		"Mic Jack", "MICBIAS";
-};

+ 9 - 9
Documentation/devicetree/bindings/sound/sgtl5000.txt

@@ -7,6 +7,14 @@ Required properties:
 
 - clocks : the clock provider of SYS_MCLK
 
+- VDDA-supply : the regulator provider of VDDA
+
+- VDDIO-supply: the regulator provider of VDDIO
+
+Optional properties:
+
+- VDDD-supply : the regulator provider of VDDD
+
 - micbias-resistor-k-ohms : the bias resistor to be used in kOmhs
 	The resistor can take values of 2k, 4k or 8k.
 	If set to 0 it will be off.
@@ -15,17 +23,9 @@ Required properties:
 
 - micbias-voltage-m-volts : the bias voltage to be used in mVolts
 	The voltage can take values from 1.25V to 3V by 250mV steps
-	If this node is not mentionned or the value is unknown, then
+	If this node is not mentioned or the value is unknown, then
 	the value is set to 1.25V.
 
-- VDDA-supply : the regulator provider of VDDA
-
-- VDDIO-supply: the regulator provider of VDDIO
-
-Optional properties:
-
-- VDDD-supply : the regulator provider of VDDD
-
 Example:
 
 codec: sgtl5000@0a {

+ 10 - 10
Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt

@@ -33,11 +33,11 @@ Required properties:
 	"tx" for "st,sti-uni-player" compatibility
 	"rx" for "st,sti-uni-reader" compatibility
 
-  - version: IP version integrated in SOC.
+  - st,version: IP version integrated in SOC.
 
   - dai-name: DAI name that describes the IP.
 
-  - IP mode: IP working mode depending on associated codec.
+  - st,mode: IP working mode depending on associated codec.
 	"HDMI" connected to HDMI codec and support IEC HDMI formats (player only).
 	"SPDIF" connected to SPDIF codec and support SPDIF formats (player only).
 	"PCM" PCM standard mode for I2S or TDM bus.
@@ -47,7 +47,7 @@ Required properties ("st,sti-uni-player" compatibility only):
   - clocks: CPU_DAI IP clock source, listed in the same order than the
 	    CPU_DAI properties.
 
-  - uniperiph-id: internal SOC IP instance ID.
+  - st,uniperiph-id: internal SOC IP instance ID.
 
 Optional properties:
   - pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for
@@ -84,9 +84,9 @@ Example:
 		dmas = <&fdma0 4 0 1>;
 		dai-name = "Uni Player #2 (DAC)";
 		dma-names = "tx";
-		uniperiph-id = <2>;
-		version = <5>;
-		mode = "PCM";
+		st,uniperiph-id = <2>;
+		st,version = <5>;
+		st,mode = "PCM";
 	};
 
 	sti_uni_player3: sti-uni-player@3 {
@@ -100,9 +100,9 @@ Example:
 		dmas = <&fdma0 7 0 1>;
 		dma-names = "tx";
 		dai-name = "Uni Player #3 (SPDIF)";
-		uniperiph-id = <3>;
-		version = <5>;
-		mode = "SPDIF";
+		st,uniperiph-id = <3>;
+		st,version = <5>;
+		st,mode = "SPDIF";
 	};
 
 	sti_uni_reader1: sti-uni-reader@1 {
@@ -115,7 +115,7 @@ Example:
 		dmas = <&fdma0 6 0 1>;
 		dma-names = "rx";
 		dai-name = "Uni Reader #1 (HDMI RX)";
-		version = <3>;
+		st,version = <3>;
 		st,mode = "PCM";
 	};
 

+ 34 - 0
Documentation/devicetree/bindings/sound/sun4i-i2s.txt

@@ -0,0 +1,34 @@
+* Allwinner A10 I2S controller
+
+The I2S bus (Inter-IC sound bus) is a serial link for digital
+audio data transfer between devices in the system.
+
+Required properties:
+
+- compatible: should be one of the followings
+   - "allwinner,sun4i-a10-i2s"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: should contain the I2S interrupt.
+- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
+	Documentation/devicetree/bindings/dma/dma.txt
+- dma-names: should include "tx" and "rx".
+- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
+- clock-names: should contain followings:
+   - "apb" : clock for the I2S bus interface
+   - "mod" : module clock for the I2S controller
+- #sound-dai-cells : Must be equal to 0
+
+Example:
+
+i2s0: i2s@01c22400 {
+	#sound-dai-cells = <0>;
+	compatible = "allwinner,sun4i-a10-i2s";
+	reg = <0x01c22400 0x400>;
+	interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&apb0_gates 3>, <&i2s0_clk>;
+	clock-names = "apb", "mod";
+	dmas = <&dma SUN4I_DMA_NORMAL 3>,
+	       <&dma SUN4I_DMA_NORMAL 3>;
+	dma-names = "rx", "tx";
+};

+ 1 - 1
Documentation/sound/alsa/soc/machine.txt

@@ -3,7 +3,7 @@ ASoC Machine Driver
 
 The ASoC machine (or board) driver is the code that glues together all the
 component drivers (e.g. codecs, platforms and DAIs). It also describes the
-relationships between each componnent which include audio paths, GPIOs,
+relationships between each component which include audio paths, GPIOs,
 interrupts, clocking, jacks and voltage regulators.
 
 The machine driver can contain codec and platform specific code. It registers

+ 6 - 6
Documentation/sound/alsa/timestamping.txt

@@ -14,7 +14,7 @@ provides a refined estimate with a delay.
 event or application query.
 The difference (tstamp - trigger_tstamp) defines the elapsed time.
 
-The ALSA API provides reports two basic pieces of information, avail
+The ALSA API provides two basic pieces of information, avail
 and delay, which combined with the trigger and current system
 timestamps allow for applications to keep track of the 'fullness' of
 the ring buffer and the amount of queued samples.
@@ -53,21 +53,21 @@ case):
 The analog time is taken at the last stage of the playback, as close
 as possible to the actual transducer
 
-The link time is taken at the output of the SOC/chipset as the samples
+The link time is taken at the output of the SoC/chipset as the samples
 are pushed on a link. The link time can be directly measured if
 supported in hardware by sample counters or wallclocks (e.g. with
 HDAudio 24MHz or PTP clock for networked solutions) or indirectly
 estimated (e.g. with the frame counter in USB).
 
 The DMA time is measured using counters - typically the least reliable
-of all measurements due to the bursty natured of DMA transfers.
+of all measurements due to the bursty nature of DMA transfers.
 
 The app time corresponds to the time tracked by an application after
 writing in the ring buffer.
 
-The application can query what the hardware supports, define which
+The application can query the hardware capabilities, define which
 audio time it wants reported by selecting the relevant settings in
-audio_tstamp_config fields, get an estimate of the timestamp
+audio_tstamp_config fields, thus get an estimate of the timestamp
 accuracy. It can also request the delay-to-analog be included in the
 measurement. Direct access to the link time is very interesting on
 platforms that provide an embedded DSP; measuring directly the link
@@ -169,7 +169,7 @@ playback: systime: 938107562 nsec, audio time 938112708 nsec, 	systime delta -51
 Example 1 shows that the timestamp at the DMA level is close to 1ms
 ahead of the actual playback time (as a side time this sort of
 measurement can help define rewind safeguards). Compensating for the
-DMA-link delay in example 2 helps remove the hardware buffering abut
+DMA-link delay in example 2 helps remove the hardware buffering but
 the information is still very jittery, with up to one sample of
 error. In example 3 where the timestamps are measured with the link
 wallclock, the timestamps show a monotonic behavior and a lower

+ 10 - 1
MAINTAINERS

@@ -1611,7 +1611,6 @@ F:	drivers/*/*/*s3c2410*
 F:	drivers/memory/samsung/*
 F:	drivers/soc/samsung/*
 F:	drivers/spi/spi-s3c*
-F:	sound/soc/samsung/*
 F:	Documentation/arm/Samsung/
 F:	Documentation/devicetree/bindings/arm/samsung/
 F:	Documentation/devicetree/bindings/sram/samsung-sram.txt
@@ -7355,6 +7354,13 @@ F:	Documentation/devicetree/bindings/i2c/max6697.txt
 F:	drivers/hwmon/max6697.c
 F:	include/linux/platform_data/max6697.h
 
+MAX9860 MONO AUDIO VOICE CODEC DRIVER
+M:	Peter Rosin <peda@axentia.se>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:	Maintained
+F:	Documentation/devicetree/bindings/sound/max9860.txt
+F:	sound/soc/codecs/max9860.*
+
 MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
 M:	Krzysztof Kozlowski <k.kozlowski@samsung.com>
 L:	linux-pm@vger.kernel.org
@@ -10027,7 +10033,9 @@ S:	Maintained
 F:	drivers/platform/x86/samsung-laptop.c
 
 SAMSUNG AUDIO (ASoC) DRIVERS
+M:	Krzysztof Kozlowski <k.kozlowski@samsung.com>
 M:	Sangbeom Kim <sbkim73@samsung.com>
+M:	Sylwester Nawrocki <s.nawrocki@samsung.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:	Supported
 F:	sound/soc/samsung/
@@ -10856,6 +10864,7 @@ T:	git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
 W:	http://alsa-project.org/main/index.php/ASoC
 S:	Supported
+F:	Documentation/devicetree/bindings/sound/
 F:	Documentation/sound/alsa/soc/
 F:	sound/soc/
 F:	include/sound/soc*

+ 28 - 0
drivers/base/property.c

@@ -887,6 +887,34 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(device_get_next_child_node);
 
+/**
+ * device_get_named_child_node - Return first matching named child node handle
+ * @dev: Device to find the named child node for.
+ * @childname: String to match child node name against.
+ */
+struct fwnode_handle *device_get_named_child_node(struct device *dev,
+						  const char *childname)
+{
+	struct fwnode_handle *child;
+
+	/*
+	 * Find first matching named child node of this device.
+	 * For ACPI this will be a data only sub-node.
+	 */
+	device_for_each_child_node(dev, child) {
+		if (is_of_node(child)) {
+			if (!of_node_cmp(to_of_node(child)->name, childname))
+				return child;
+		} else if (is_acpi_data_node(child)) {
+			if (acpi_data_node_match(child, childname))
+				return child;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(device_get_named_child_node);
+
 /**
  * fwnode_handle_put - Drop reference to a device node
  * @fwnode: Pointer to the device node to drop the reference to.

+ 7 - 0
include/acpi/acpi_bus.h

@@ -420,6 +420,13 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn
 		container_of(fwnode, struct acpi_data_node, fwnode) : NULL;
 }
 
+static inline bool acpi_data_node_match(struct fwnode_handle *fwnode,
+					const char *name)
+{
+	return is_acpi_data_node(fwnode) ?
+		(!strcmp(to_acpi_data_node(fwnode)->name, name)) : false;
+}
+
 static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
 {
 	return &adev->fwnode;

+ 6 - 0
include/linux/acpi.h

@@ -608,6 +608,12 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn
 	return NULL;
 }
 
+static inline bool acpi_data_node_match(struct fwnode_handle *fwnode,
+					const char *name)
+{
+	return false;
+}
+
 static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
 {
 	return NULL;

+ 10 - 0
include/linux/mfd/arizona/core.h

@@ -14,6 +14,7 @@
 #define _WM_ARIZONA_CORE_H
 
 #include <linux/interrupt.h>
+#include <linux/notifier.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/mfd/arizona/pdata.h>
@@ -148,8 +149,17 @@ struct arizona {
 	uint16_t dac_comp_coeff;
 	uint8_t dac_comp_enabled;
 	struct mutex dac_comp_lock;
+
+	struct blocking_notifier_head notifier;
 };
 
+static inline int arizona_call_notifiers(struct arizona *arizona,
+					 unsigned long event,
+					 void *data)
+{
+	return blocking_notifier_call_chain(&arizona->notifier, event, data);
+}
+
 int arizona_clk32k_enable(struct arizona *arizona);
 int arizona_clk32k_disable(struct arizona *arizona);
 

+ 7 - 7
include/linux/of.h

@@ -238,13 +238,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
 #define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
 #endif
 
-/* Default string compare functions, Allow arch asm/prom.h to override */
-#if !defined(of_compat_cmp)
-#define of_compat_cmp(s1, s2, l)	strcasecmp((s1), (s2))
-#define of_prop_cmp(s1, s2)		strcmp((s1), (s2))
-#define of_node_cmp(s1, s2)		strcasecmp((s1), (s2))
-#endif
-
 #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
 #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
 
@@ -728,6 +721,13 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag
 #define of_match_node(_matches, _node)	NULL
 #endif /* CONFIG_OF */
 
+/* Default string compare functions, Allow arch asm/prom.h to override */
+#if !defined(of_compat_cmp)
+#define of_compat_cmp(s1, s2, l)	strcasecmp((s1), (s2))
+#define of_prop_cmp(s1, s2)		strcmp((s1), (s2))
+#define of_node_cmp(s1, s2)		strcasecmp((s1), (s2))
+#endif
+
 #if defined(CONFIG_OF) && defined(CONFIG_NUMA)
 extern int of_node_to_nid(struct device_node *np);
 #else

+ 3 - 0
include/linux/property.h

@@ -77,6 +77,9 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
 	for (child = device_get_next_child_node(dev, NULL); child;	\
 	     child = device_get_next_child_node(dev, child))
 
+struct fwnode_handle *device_get_named_child_node(struct device *dev,
+						  const char *childname);
+
 void fwnode_handle_put(struct fwnode_handle *fwnode);
 
 unsigned int device_get_child_node_count(struct device *dev);

+ 5 - 0
include/sound/compress_driver.h

@@ -68,6 +68,7 @@ struct snd_compr_runtime {
  * @ops: pointer to DSP callbacks
  * @runtime: pointer to runtime structure
  * @device: device pointer
+ * @error_work: delayed work used when closing the stream due to an error
  * @direction: stream direction, playback/recording
  * @metadata_set: metadata set flag, true when set
  * @next_track: has userspace signal next track transition, true when set
@@ -78,6 +79,7 @@ struct snd_compr_stream {
 	struct snd_compr_ops *ops;
 	struct snd_compr_runtime *runtime;
 	struct snd_compr *device;
+	struct delayed_work error_work;
 	enum snd_compr_direction direction;
 	bool metadata_set;
 	bool next_track;
@@ -187,4 +189,7 @@ static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
 	wake_up(&stream->runtime->sleep);
 }
 
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+			 snd_pcm_state_t state);
+
 #endif

+ 48 - 0
include/sound/cs35l33.h

@@ -0,0 +1,48 @@
+/*
+ * linux/sound/cs35l33.h -- Platform data for CS35l33
+ *
+ * Copyright (c) 2016 Cirrus Logic Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CS35L33_H
+#define __CS35L33_H
+
+struct cs35l33_hg {
+	bool enable_hg_algo;
+	unsigned int mem_depth;
+	unsigned int release_rate;
+	unsigned int hd_rm;
+	unsigned int ldo_thld;
+	unsigned int ldo_path_disable;
+	unsigned int ldo_entry_delay;
+	bool vp_hg_auto;
+	unsigned int vp_hg;
+	unsigned int vp_hg_rate;
+	unsigned int vp_hg_va;
+};
+
+struct cs35l33_pdata {
+	/* Boost Controller Voltage Setting */
+	unsigned int boost_ctl;
+
+	/* Boost Controller Peak Current */
+	unsigned int boost_ipk;
+
+	/* Amplifier Drive Select */
+	unsigned int amp_drv_sel;
+
+	/* soft volume ramp */
+	unsigned int ramp_rate;
+
+	/* IMON adc scale */
+	unsigned int imon_adc_scale;
+
+	/* H/G algo configuration */
+	struct cs35l33_hg hg_config;
+};
+
+#endif /* __CS35L33_H */

+ 8 - 5
include/sound/hdmi-codec.h

@@ -53,18 +53,19 @@ struct hdmi_codec_params {
 	int channels;
 };
 
+struct hdmi_codec_pdata;
 struct hdmi_codec_ops {
 	/*
 	 * Called when ASoC starts an audio stream setup.
 	 * Optional
 	 */
-	int (*audio_startup)(struct device *dev);
+	int (*audio_startup)(struct device *dev, void *data);
 
 	/*
 	 * Configures HDMI-encoder for audio stream.
 	 * Mandatory
 	 */
-	int (*hw_params)(struct device *dev,
+	int (*hw_params)(struct device *dev, void *data,
 			 struct hdmi_codec_daifmt *fmt,
 			 struct hdmi_codec_params *hparms);
 
@@ -72,19 +73,20 @@ struct hdmi_codec_ops {
 	 * Shuts down the audio stream.
 	 * Mandatory
 	 */
-	void (*audio_shutdown)(struct device *dev);
+	void (*audio_shutdown)(struct device *dev, void *data);
 
 	/*
 	 * Mute/unmute HDMI audio stream.
 	 * Optional
 	 */
-	int (*digital_mute)(struct device *dev, bool enable);
+	int (*digital_mute)(struct device *dev, void *data, bool enable);
 
 	/*
 	 * Provides EDID-Like-Data from connected HDMI device.
 	 * Optional
 	 */
-	int (*get_eld)(struct device *dev, uint8_t *buf, size_t len);
+	int (*get_eld)(struct device *dev, void *data,
+		       uint8_t *buf, size_t len);
 };
 
 /* HDMI codec initalization data */
@@ -93,6 +95,7 @@ struct hdmi_codec_pdata {
 	uint i2s:1;
 	uint spdif:1;
 	int max_i2s_channels;
+	void *data;
 };
 
 #define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"

+ 1 - 10
include/sound/simple_card.h

@@ -13,16 +13,7 @@
 #define __SIMPLE_CARD_H
 
 #include <sound/soc.h>
-
-struct asoc_simple_dai {
-	const char *name;
-	unsigned int sysclk;
-	int slots;
-	int slot_width;
-	unsigned int tx_slot_mask;
-	unsigned int rx_slot_mask;
-	struct clk *clk;
-};
+#include <sound/simple_card_utils.h>
 
 struct asoc_simple_card_info {
 	const char *name;

+ 36 - 0
include/sound/simple_card_utils.h

@@ -0,0 +1,36 @@
+/*
+ * simple_card_core.h
+ *
+ * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SIMPLE_CARD_CORE_H
+#define __SIMPLE_CARD_CORE_H
+
+#include <sound/soc.h>
+
+struct asoc_simple_dai {
+	const char *name;
+	unsigned int sysclk;
+	int slots;
+	int slot_width;
+	unsigned int tx_slot_mask;
+	unsigned int rx_slot_mask;
+	struct clk *clk;
+};
+
+int asoc_simple_card_parse_daifmt(struct device *dev,
+				  struct device_node *node,
+				  struct device_node *codec,
+				  char *prefix,
+				  unsigned int *retfmt);
+int asoc_simple_card_set_dailink_name(struct device *dev,
+				      struct snd_soc_dai_link *dai_link,
+				      const char *fmt, ...);
+int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
+				     char *prefix);
+
+#endif /* __SIMPLE_CARD_CORE_H */

+ 7 - 1
include/sound/soc-dapm.h

@@ -358,6 +358,7 @@ struct snd_soc_dapm_context;
 struct regulator;
 struct snd_soc_dapm_widget_list;
 struct snd_soc_dapm_update;
+enum snd_soc_dapm_direction;
 
 int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 			 struct snd_kcontrol *kcontrol, int event);
@@ -382,6 +383,9 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
 	const struct snd_soc_dapm_widget *widget,
 	int num);
+struct snd_soc_dapm_widget *snd_soc_dapm_new_control(
+		struct snd_soc_dapm_context *dapm,
+		const struct snd_soc_dapm_widget *widget);
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 				 struct snd_soc_dai *dai);
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
@@ -451,7 +455,9 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
 
 /* dapm path query */
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
-	struct snd_soc_dapm_widget_list **list);
+	struct snd_soc_dapm_widget_list **list,
+	bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+				      enum snd_soc_dapm_direction));
 
 struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
 	struct snd_kcontrol *kcontrol);

+ 11 - 0
include/sound/soc.h

@@ -179,6 +179,17 @@
 	.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
 	.private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \
 					    xmin, xmax, xsign_bit, xinvert) }
+#define SOC_SINGLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
+{	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+		  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p  = (tlv_array), \
+	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+	.put = snd_soc_put_volsw, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+	{.reg = xreg, .rreg = xreg,  \
+	 .min = xmin, .max = xmax, .platform_max = xmax, \
+	.sign_bit = 7,} }
 #define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
 {	.iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \

+ 0 - 1
sound/Makefile

@@ -2,7 +2,6 @@
 #
 
 obj-$(CONFIG_SOUND) += soundcore.o
-obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
 obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \

+ 65 - 2
sound/core/compress_offload.c

@@ -67,6 +67,8 @@ struct snd_compr_file {
 	struct snd_compr_stream stream;
 };
 
+static void error_delayed_work(struct work_struct *work);
+
 /*
  * a note on stream states used:
  * we use following states in the compressed core
@@ -123,6 +125,9 @@ static int snd_compr_open(struct inode *inode, struct file *f)
 		snd_card_unref(compr->card);
 		return -ENOMEM;
 	}
+
+	INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
+
 	data->stream.ops = compr->ops;
 	data->stream.direction = dirn;
 	data->stream.private_data = compr->private_data;
@@ -153,6 +158,8 @@ static int snd_compr_free(struct inode *inode, struct file *f)
 	struct snd_compr_file *data = f->private_data;
 	struct snd_compr_runtime *runtime = data->stream.runtime;
 
+	cancel_delayed_work_sync(&data->stream.error_work);
+
 	switch (runtime->state) {
 	case SNDRV_PCM_STATE_RUNNING:
 	case SNDRV_PCM_STATE_DRAINING:
@@ -237,6 +244,15 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
 	avail = snd_compr_calc_avail(stream, &ioctl_avail);
 	ioctl_avail.avail = avail;
 
+	switch (stream->runtime->state) {
+	case SNDRV_PCM_STATE_OPEN:
+		return -EBADFD;
+	case SNDRV_PCM_STATE_XRUN:
+		return -EPIPE;
+	default:
+		break;
+	}
+
 	if (copy_to_user((__u64 __user *)arg,
 				&ioctl_avail, sizeof(ioctl_avail)))
 		return -EFAULT;
@@ -346,11 +362,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
 	switch (stream->runtime->state) {
 	case SNDRV_PCM_STATE_OPEN:
 	case SNDRV_PCM_STATE_PREPARED:
-	case SNDRV_PCM_STATE_XRUN:
 	case SNDRV_PCM_STATE_SUSPENDED:
 	case SNDRV_PCM_STATE_DISCONNECTED:
 		retval = -EBADFD;
 		goto out;
+	case SNDRV_PCM_STATE_XRUN:
+		retval = -EPIPE;
+		goto out;
 	}
 
 	avail = snd_compr_get_avail(stream);
@@ -399,10 +417,16 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
 	stream = &data->stream;
 
 	mutex_lock(&stream->device->lock);
-	if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+
+	switch (stream->runtime->state) {
+	case SNDRV_PCM_STATE_OPEN:
+	case SNDRV_PCM_STATE_XRUN:
 		retval = snd_compr_get_poll(stream) | POLLERR;
 		goto out;
+	default:
+		break;
 	}
+
 	poll_wait(f, &stream->runtime->sleep, wait);
 
 	avail = snd_compr_get_avail(stream);
@@ -697,6 +721,45 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
 	return retval;
 }
 
+static void error_delayed_work(struct work_struct *work)
+{
+	struct snd_compr_stream *stream;
+
+	stream = container_of(work, struct snd_compr_stream, error_work.work);
+
+	mutex_lock(&stream->device->lock);
+
+	stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+	wake_up(&stream->runtime->sleep);
+
+	mutex_unlock(&stream->device->lock);
+}
+
+/*
+ * snd_compr_stop_error: Report a fatal error on a stream
+ * @stream: pointer to stream
+ * @state: state to transition the stream to
+ *
+ * Stop the stream and set its state.
+ *
+ * Should be called with compressed device lock held.
+ */
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+			 snd_pcm_state_t state)
+{
+	if (stream->runtime->state == state)
+		return 0;
+
+	stream->runtime->state = state;
+
+	pr_debug("Changing state to: %d\n", state);
+
+	queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_compr_stop_error);
+
 static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
 {
 	int ret;

+ 32 - 0
sound/core/control.c

@@ -807,6 +807,36 @@ static int snd_ctl_elem_list(struct snd_card *card,
 	return 0;
 }
 
+static bool validate_element_member_dimension(struct snd_ctl_elem_info *info)
+{
+	unsigned int members;
+	unsigned int i;
+
+	if (info->dimen.d[0] == 0)
+		return true;
+
+	members = 1;
+	for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) {
+		if (info->dimen.d[i] == 0)
+			break;
+		members *= info->dimen.d[i];
+
+		/*
+		 * info->count should be validated in advance, to guarantee
+		 * calculation soundness.
+		 */
+		if (members > info->count)
+			return false;
+	}
+
+	for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) {
+		if (info->dimen.d[i] > 0)
+			return false;
+	}
+
+	return members == info->count;
+}
+
 static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
 			     struct snd_ctl_elem_info *info)
 {
@@ -1274,6 +1304,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
 	if (info->count < 1 ||
 	    info->count > max_value_counts[info->type])
 		return -EINVAL;
+	if (!validate_element_member_dimension(info))
+		return -EINVAL;
 	private_size = value_sizes[info->type] * info->count;
 
 	/*

+ 5 - 5
sound/core/seq/oss/seq_oss_synth.c

@@ -70,11 +70,11 @@ struct seq_oss_synth {
 static int max_synth_devs;
 static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
 static struct seq_oss_synth midi_synth_dev = {
-	-1, /* seq_device */
-	SYNTH_TYPE_MIDI, /* synth_type */
-	0, /* synth_subtype */
-	16, /* nr_voices */
-	"MIDI", /* name */
+	.seq_device = -1,
+	.synth_type = SYNTH_TYPE_MIDI,
+	.synth_subtype = 0,
+	.nr_voices = 16,
+	.name = "MIDI",
 };
 
 static DEFINE_SPINLOCK(register_lock);

+ 9 - 14
sound/core/seq/seq_timer.c

@@ -165,7 +165,7 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
 	snd_seq_timer_update_tick(&tmr->tick, resolution);
 
 	/* register actual time of this timer update */
-	do_gettimeofday(&tmr->last_update);
+	ktime_get_ts64(&tmr->last_update);
 
 	spin_unlock_irqrestore(&tmr->lock, flags);
 
@@ -392,7 +392,7 @@ static int seq_timer_start(struct snd_seq_timer *tmr)
 		return -EINVAL;
 	snd_timer_start(tmr->timeri, tmr->ticks);
 	tmr->running = 1;
-	do_gettimeofday(&tmr->last_update);
+	ktime_get_ts64(&tmr->last_update);
 	return 0;
 }
 
@@ -420,7 +420,7 @@ static int seq_timer_continue(struct snd_seq_timer *tmr)
 	}
 	snd_timer_start(tmr->timeri, tmr->ticks);
 	tmr->running = 1;
-	do_gettimeofday(&tmr->last_update);
+	ktime_get_ts64(&tmr->last_update);
 	return 0;
 }
 
@@ -444,17 +444,12 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
 	spin_lock_irqsave(&tmr->lock, flags);
 	cur_time = tmr->cur_time;
 	if (tmr->running) { 
-		struct timeval tm;
-		int usec;
-		do_gettimeofday(&tm);
-		usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
-		if (usec < 0) {
-			cur_time.tv_nsec += (1000000 + usec) * 1000;
-			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
-		} else {
-			cur_time.tv_nsec += usec * 1000;
-			cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
-		}
+		struct timespec64 tm;
+
+		ktime_get_ts64(&tm);
+		tm = timespec64_sub(tm, tmr->last_update);
+		cur_time.tv_nsec = tm.tv_nsec;
+		cur_time.tv_sec = tm.tv_sec;
 		snd_seq_sanity_real_time(&cur_time);
 	}
 	spin_unlock_irqrestore(&tmr->lock, flags);

+ 1 - 1
sound/core/seq/seq_timer.h

@@ -52,7 +52,7 @@ struct snd_seq_timer {
 	unsigned int skew;
 	unsigned int skew_base;
 
-	struct timeval 		last_update;	 /* time of last clock update, used for interpolation */
+	struct timespec64	last_update;	 /* time of last clock update, used for interpolation */
 
 	spinlock_t lock;
 };

+ 25 - 3
sound/hda/hdmi_chmap.c

@@ -353,7 +353,8 @@ static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
 		int hdmi_slot = 0;
 		/* fill actual channel mappings in ALSA channel (i) order */
 		for (i = 0; i < ch_alloc->channels; i++) {
-			while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
+			while (!WARN_ON(hdmi_slot >= 8) &&
+			       !ch_alloc->speakers[7 - hdmi_slot])
 				hdmi_slot++; /* skip zero slots */
 
 			hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
@@ -430,6 +431,12 @@ static int to_cea_slot(int ordered_ca, unsigned char pos)
 	int mask = snd_hdac_chmap_to_spk_mask(pos);
 	int i;
 
+	/* Add sanity check to pass klockwork check.
+	 * This should never happen.
+	 */
+	if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+		return -1;
+
 	if (mask) {
 		for (i = 0; i < 8; i++) {
 			if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
@@ -456,7 +463,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
 /* from CEA slot to ALSA API channel position */
 static int from_cea_slot(int ordered_ca, unsigned char slot)
 {
-	int mask = channel_allocations[ordered_ca].speakers[7 - slot];
+	int mask;
+
+	/* Add sanity check to pass klockwork check.
+	 * This should never happen.
+	 */
+	if (slot >= 8)
+		return 0;
+
+	mask = channel_allocations[ordered_ca].speakers[7 - slot];
 
 	return snd_hdac_spk_to_chmap(mask);
 }
@@ -523,7 +538,8 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
 	int ordered_ca = get_channel_allocation_order(ca);
 
 	for (i = 0; i < 8; i++) {
-		if (i < channel_allocations[ordered_ca].channels)
+		if (ordered_ca < ARRAY_SIZE(channel_allocations) &&
+		    i < channel_allocations[ordered_ca].channels)
 			map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
 		else
 			map[i] = 0;
@@ -551,6 +567,12 @@ int snd_hdac_get_active_channels(int ca)
 {
 	int ordered_ca = get_channel_allocation_order(ca);
 
+	/* Add sanity check to pass klockwork check.
+	 * This should never happen.
+	 */
+	if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+		ordered_ca = 0;
+
 	return channel_allocations[ordered_ca].channels;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);

+ 1 - 1
sound/i2c/other/ak4114.c

@@ -121,7 +121,7 @@ int snd_ak4114_create(struct snd_card *card,
 
       __fail:
 	snd_ak4114_free(chip);
-	return err < 0 ? err : -EIO;
+	return err;
 }
 EXPORT_SYMBOL(snd_ak4114_create);
 

+ 1 - 1
sound/i2c/other/ak4117.c

@@ -110,7 +110,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
 
       __fail:
 	snd_ak4117_free(chip);
-	return err < 0 ? err : -EIO;
+	return err;
 }
 
 void snd_ak4117_reg_write(struct ak4117 *chip, unsigned char reg, unsigned char mask, unsigned char val)

+ 1 - 12
sound/isa/ad1848/ad1848.c

@@ -170,15 +170,4 @@ static struct isa_driver snd_ad1848_driver = {
 	}
 };
 
-static int __init alsa_card_ad1848_init(void)
-{
-	return isa_register_driver(&snd_ad1848_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_ad1848_exit(void)
-{
-	isa_unregister_driver(&snd_ad1848_driver);
-}
-
-module_init(alsa_card_ad1848_init);
-module_exit(alsa_card_ad1848_exit);
+module_isa_driver(snd_ad1848_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/adlib.c

@@ -112,15 +112,4 @@ static struct isa_driver snd_adlib_driver = {
 	}
 };
 
-static int __init alsa_card_adlib_init(void)
-{
-	return isa_register_driver(&snd_adlib_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_adlib_exit(void)
-{
-	isa_unregister_driver(&snd_adlib_driver);
-}
-
-module_init(alsa_card_adlib_init);
-module_exit(alsa_card_adlib_exit);
+module_isa_driver(snd_adlib_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/cmi8328.c

@@ -469,15 +469,4 @@ static struct isa_driver snd_cmi8328_driver = {
 	},
 };
 
-static int __init alsa_card_cmi8328_init(void)
-{
-	return isa_register_driver(&snd_cmi8328_driver, CMI8328_MAX);
-}
-
-static void __exit alsa_card_cmi8328_exit(void)
-{
-	isa_unregister_driver(&snd_cmi8328_driver);
-}
-
-module_init(alsa_card_cmi8328_init)
-module_exit(alsa_card_cmi8328_exit)
+module_isa_driver(snd_cmi8328_driver, CMI8328_MAX);

+ 1 - 12
sound/isa/cs423x/cs4231.c

@@ -186,15 +186,4 @@ static struct isa_driver snd_cs4231_driver = {
 	}
 };
 
-static int __init alsa_card_cs4231_init(void)
-{
-	return isa_register_driver(&snd_cs4231_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_cs4231_exit(void)
-{
-	isa_unregister_driver(&snd_cs4231_driver);
-}
-
-module_init(alsa_card_cs4231_init);
-module_exit(alsa_card_cs4231_exit);
+module_isa_driver(snd_cs4231_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/galaxy/galaxy.c

@@ -634,15 +634,4 @@ static struct isa_driver snd_galaxy_driver = {
 	}
 };
 
-static int __init alsa_card_galaxy_init(void)
-{
-	return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_galaxy_exit(void)
-{
-	isa_unregister_driver(&snd_galaxy_driver);
-}
-
-module_init(alsa_card_galaxy_init);
-module_exit(alsa_card_galaxy_exit);
+module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/gus/gusclassic.c

@@ -229,15 +229,4 @@ static struct isa_driver snd_gusclassic_driver = {
 	}
 };
 
-static int __init alsa_card_gusclassic_init(void)
-{
-	return isa_register_driver(&snd_gusclassic_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusclassic_exit(void)
-{
-	isa_unregister_driver(&snd_gusclassic_driver);
-}
-
-module_init(alsa_card_gusclassic_init);
-module_exit(alsa_card_gusclassic_exit);
+module_isa_driver(snd_gusclassic_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/gus/gusextreme.c

@@ -358,15 +358,4 @@ static struct isa_driver snd_gusextreme_driver = {
 	}
 };
 
-static int __init alsa_card_gusextreme_init(void)
-{
-	return isa_register_driver(&snd_gusextreme_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusextreme_exit(void)
-{
-	isa_unregister_driver(&snd_gusextreme_driver);
-}
-
-module_init(alsa_card_gusextreme_init);
-module_exit(alsa_card_gusextreme_exit);
+module_isa_driver(snd_gusextreme_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/gus/gusmax.c

@@ -370,15 +370,4 @@ static struct isa_driver snd_gusmax_driver = {
 	},
 };
 
-static int __init alsa_card_gusmax_init(void)
-{
-	return isa_register_driver(&snd_gusmax_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusmax_exit(void)
-{
-	isa_unregister_driver(&snd_gusmax_driver);
-}
-
-module_init(alsa_card_gusmax_init)
-module_exit(alsa_card_gusmax_exit)
+module_isa_driver(snd_gusmax_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/sb/jazz16.c

@@ -387,15 +387,4 @@ static struct isa_driver snd_jazz16_driver = {
 	},
 };
 
-static int __init alsa_card_jazz16_init(void)
-{
-	return isa_register_driver(&snd_jazz16_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_jazz16_exit(void)
-{
-	isa_unregister_driver(&snd_jazz16_driver);
-}
-
-module_init(alsa_card_jazz16_init)
-module_exit(alsa_card_jazz16_exit)
+module_isa_driver(snd_jazz16_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/sb/sb8.c

@@ -251,15 +251,4 @@ static struct isa_driver snd_sb8_driver = {
 	},
 };
 
-static int __init alsa_card_sb8_init(void)
-{
-	return isa_register_driver(&snd_sb8_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sb8_exit(void)
-{
-	isa_unregister_driver(&snd_sb8_driver);
-}
-
-module_init(alsa_card_sb8_init)
-module_exit(alsa_card_sb8_exit)
+module_isa_driver(snd_sb8_driver, SNDRV_CARDS);

+ 1 - 12
sound/isa/sc6000.c

@@ -711,15 +711,4 @@ static struct isa_driver snd_sc6000_driver = {
 };
 
 
-static int __init alsa_card_sc6000_init(void)
-{
-	return isa_register_driver(&snd_sc6000_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sc6000_exit(void)
-{
-	isa_unregister_driver(&snd_sc6000_driver);
-}
-
-module_init(alsa_card_sc6000_init)
-module_exit(alsa_card_sc6000_exit)
+module_isa_driver(snd_sc6000_driver, SNDRV_CARDS);

+ 1 - 1
sound/oss/ad1848.c

@@ -254,7 +254,7 @@ static void ad_write(ad1848_info * devc, int reg, int data)
 
 static void wait_for_calibration(ad1848_info * devc)
 {
-	int timeout = 0;
+	int timeout;
 
 	/*
 	 * Wait until the auto calibration process has finished.

+ 7 - 7
sound/oss/aedsp16.c

@@ -482,13 +482,13 @@ static struct orVals orDMA[] __initdata = {
 };
 
 static struct aedsp16_info ae_config = {
-	DEF_AEDSP16_IOB,
-	DEF_AEDSP16_IRQ,
-	DEF_AEDSP16_MRQ,
-	DEF_AEDSP16_DMA,
-	-1,
-	-1,
-	INIT_NONE
+	.base_io = DEF_AEDSP16_IOB,
+	.irq = DEF_AEDSP16_IRQ,
+	.mpu_irq = DEF_AEDSP16_MRQ,
+	.dma = DEF_AEDSP16_DMA,
+	.mss_base = -1,
+	.mpu_base = -1,
+	.init = INIT_NONE
 };
 
 /*

+ 28 - 1
sound/oss/sound_firmware.h

@@ -1,2 +1,29 @@
-extern int mod_firmware_load(const char *fn, char **fp);
+#include <linux/fs.h>
 
+/**
+ *	mod_firmware_load - load sound driver firmware
+ *	@fn: filename
+ *	@fp: return for the buffer.
+ *
+ *	Load the firmware for a sound module (up to 128K) into a buffer.
+ *	The buffer is returned in *fp. It is allocated with vmalloc so is
+ *	virtually linear and not DMAable. The caller should free it with
+ *	vfree when finished.
+ *
+ *	The length of the buffer is returned on a successful load, the
+ *	value zero on a failure.
+ *
+ *	Caution: This API is not recommended. Firmware should be loaded via
+ *	request_firmware.
+ */
+static inline int mod_firmware_load(const char *fn, char **fp)
+{
+	loff_t size;
+	int err;
+
+	err = kernel_read_file_from_path((char *)fn, (void **)fp, &size,
+					 131072, READING_FIRMWARE);
+	if (err < 0)
+		return 0;
+	return size;
+}

+ 1 - 1
sound/oss/sound_timer.c

@@ -17,7 +17,7 @@
 #include "sound_config.h"
 
 static volatile int initialized, opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned int tmr_offs, tmr_ctr;
 static volatile unsigned long ticks_offs;
 static volatile int curr_tempo, curr_timebase;
 static volatile unsigned long curr_ticks;

+ 1 - 1
sound/oss/sys_timer.c

@@ -19,7 +19,7 @@
 #include "sound_config.h"
 
 static volatile int opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned int tmr_offs, tmr_ctr;
 static volatile unsigned long ticks_offs;
 static volatile int curr_tempo, curr_timebase;
 static volatile unsigned long curr_ticks;

+ 17 - 17
sound/pci/ctxfi/cthw20k2.c

@@ -1615,23 +1615,23 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
 	int i;
 	struct regs_cs4382 cs_read = {0};
 	struct regs_cs4382 cs_def = {
-				   0x00000001,  /* Mode Control 1 */
-				   0x00000000,  /* Mode Control 2 */
-				   0x00000084,  /* Mode Control 3 */
-				   0x00000000,  /* Filter Control */
-				   0x00000000,  /* Invert Control */
-				   0x00000024,  /* Mixing Control Pair 1 */
-				   0x00000000,  /* Vol Control A1 */
-				   0x00000000,  /* Vol Control B1 */
-				   0x00000024,  /* Mixing Control Pair 2 */
-				   0x00000000,  /* Vol Control A2 */
-				   0x00000000,  /* Vol Control B2 */
-				   0x00000024,  /* Mixing Control Pair 3 */
-				   0x00000000,  /* Vol Control A3 */
-				   0x00000000,  /* Vol Control B3 */
-				   0x00000024,  /* Mixing Control Pair 4 */
-				   0x00000000,  /* Vol Control A4 */
-				   0x00000000   /* Vol Control B4 */
+		.mode_control_1 = 0x00000001, /* Mode Control 1 */
+		.mode_control_2 = 0x00000000, /* Mode Control 2 */
+		.mode_control_3 = 0x00000084, /* Mode Control 3 */
+		.filter_control = 0x00000000, /* Filter Control */
+		.invert_control = 0x00000000, /* Invert Control */
+		.mix_control_P1 = 0x00000024, /* Mixing Control Pair 1 */
+		.vol_control_A1 = 0x00000000, /* Vol Control A1 */
+		.vol_control_B1 = 0x00000000, /* Vol Control B1 */
+		.mix_control_P2 = 0x00000024, /* Mixing Control Pair 2 */
+		.vol_control_A2 = 0x00000000, /* Vol Control A2 */
+		.vol_control_B2 = 0x00000000, /* Vol Control B2 */
+		.mix_control_P3 = 0x00000024, /* Mixing Control Pair 3 */
+		.vol_control_A3 = 0x00000000, /* Vol Control A3 */
+		.vol_control_B3 = 0x00000000, /* Vol Control B3 */
+		.mix_control_P4 = 0x00000024, /* Mixing Control Pair 4 */
+		.vol_control_A4 = 0x00000000, /* Vol Control A4 */
+		.vol_control_B4 = 0x00000000  /* Vol Control B4 */
 				 };
 
 	if (hw->model == CTSB1270) {

+ 3 - 3
sound/pci/echoaudio/echoaudio.c

@@ -1272,11 +1272,11 @@ static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
 
 	chip = snd_kcontrol_chip(kcontrol);
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
 	uinfo->value.integer.min = ECHOGAIN_MINOUT;
 	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
 	uinfo->dimen.d[0] = num_busses_out(chip);
 	uinfo->dimen.d[1] = num_busses_in(chip);
+	uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
 	return 0;
 }
 
@@ -1344,11 +1344,11 @@ static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
 
 	chip = snd_kcontrol_chip(kcontrol);
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
 	uinfo->value.integer.min = ECHOGAIN_MINOUT;
 	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
 	uinfo->dimen.d[0] = num_busses_out(chip);
 	uinfo->dimen.d[1] = num_pipes_out(chip);
+	uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
 	return 0;
 }
 
@@ -1728,7 +1728,6 @@ static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 96;
 	uinfo->value.integer.min = ECHOGAIN_MINOUT;
 	uinfo->value.integer.max = 0;
 #ifdef ECHOCARD_HAS_VMIXER
@@ -1738,6 +1737,7 @@ static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
 #endif
 	uinfo->dimen.d[1] = 16;	/* 16 channels */
 	uinfo->dimen.d[2] = 2;	/* 0=level, 1=peak */
+	uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1] * uinfo->dimen.d[2];
 	return 0;
 }
 

+ 7 - 1
sound/pci/hda/hda_codec.c

@@ -3584,6 +3584,12 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
 	bool reset;
 
 	spdif = snd_hda_spdif_out_of_nid(codec, nid);
+	/* Add sanity check to pass klockwork check.
+	 * This should never happen.
+	 */
+	if (WARN_ON(spdif == NULL))
+		return;
+
 	curr_fmt = snd_hda_codec_read(codec, nid, 0,
 				      AC_VERB_GET_STREAM_FORMAT, 0);
 	reset = codec->spdif_status_reset &&
@@ -3768,7 +3774,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 	spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
 	if (mout->dig_out_nid && mout->share_spdif &&
 	    mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
-		if (chs == 2 &&
+		if (chs == 2 && spdif != NULL &&
 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
 						format) &&
 		    !(spdif->status & IEC958_AES0_NONAUDIO)) {

+ 0 - 4
sound/pci/hda/hda_generic.c

@@ -2492,10 +2492,6 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
 	if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
 		return -ENOMEM;
 	spec->have_aamix_ctl = 1;
-	/* if no explicit aamix path is present (e.g. for Realtek codecs),
-	 * enable aamix as default -- just for compatibility
-	 */
-	spec->aamix_mode = !has_aamix_out_paths(spec);
 	return 0;
 }
 

+ 5 - 0
sound/pci/hda/patch_hdmi.c

@@ -1680,6 +1680,11 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
 
 	mutex_lock(&codec->spdif_mutex);
 	spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid);
+	/* Add sanity check to pass klockwork check.
+	 * This should never happen.
+	 */
+	if (WARN_ON(spdif == NULL))
+		return true;
 	non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
 	mutex_unlock(&codec->spdif_mutex);
 	return non_pcm;

+ 37 - 1
sound/pci/hda/patch_realtek.c

@@ -3718,6 +3718,9 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
 	case 0x10ec0295:
 		alc_process_coef_fw(codec, coef0225);
 		break;
+	case 0x10ec0867:
+		alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+		break;
 	}
 	codec_dbg(codec, "Headset jack set to unplugged mode.\n");
 }
@@ -3805,6 +3808,9 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
 		alc_process_coef_fw(codec, coef0293);
 		snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
 		break;
+	case 0x10ec0867:
+		alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14);
+		/* fallthru */
 	case 0x10ec0662:
 		snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
 		snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
@@ -3899,6 +3905,9 @@ static void alc_headset_mode_default(struct hda_codec *codec)
 	case 0x10ec0668:
 		alc_process_coef_fw(codec, coef0688);
 		break;
+	case 0x10ec0867:
+		alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+		break;
 	}
 	codec_dbg(codec, "Headset jack set to headphone (default) mode.\n");
 }
@@ -3989,6 +3998,9 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
 	case 0x10ec0295:
 		alc_process_coef_fw(codec, coef0225);
 		break;
+	case 0x10ec0867:
+		alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+		break;
 	}
 	codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
 }
@@ -4166,6 +4178,9 @@ static void alc_determine_headset_type(struct hda_codec *codec)
 		val = alc_read_coef_idx(codec, 0x46);
 		is_ctia = (val & 0x00f0) == 0x00f0;
 		break;
+	case 0x10ec0867:
+		is_ctia = true;
+		break;
 	}
 
 	codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
@@ -6532,6 +6547,8 @@ enum {
 	ALC668_FIXUP_DELL_XPS13,
 	ALC662_FIXUP_ASUS_Nx50,
 	ALC668_FIXUP_ASUS_Nx51,
+	ALC891_FIXUP_HEADSET_MODE,
+	ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
 };
 
 static const struct hda_fixup alc662_fixups[] = {
@@ -6787,6 +6804,20 @@ static const struct hda_fixup alc662_fixups[] = {
 		.chained = true,
 		.chain_id = ALC662_FIXUP_BASS_CHMAP,
 	},
+	[ALC891_FIXUP_HEADSET_MODE] = {
+		.type = HDA_FIXUP_FUNC,
+		.v.func = alc_fixup_headset_mode,
+	},
+	[ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = {
+		.type = HDA_FIXUP_PINS,
+		.v.pins = (const struct hda_pintbl[]) {
+			{ 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+			{ 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+			{ }
+		},
+		.chained = true,
+		.chain_id = ALC891_FIXUP_HEADSET_MODE
+	},
 };
 
 static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -6903,6 +6934,11 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
 };
 
 static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
+	SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+		{0x17, 0x02211010},
+		{0x18, 0x01a19030},
+		{0x1a, 0x01813040},
+		{0x21, 0x01014020}),
 	SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
 		{0x14, 0x01014010},
 		{0x18, 0x01a19020},
@@ -7091,7 +7127,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
 	HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269),
 	HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269),
 	HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269),
-	HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
+	HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662),
 	HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
 	HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
 	HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),

+ 1 - 1
sound/pci/mixart/mixart_mixer.c

@@ -965,7 +965,7 @@ static int mixart_update_monitoring(struct snd_mixart* chip, int channel)
 	int err;
 	struct mixart_msg request;
 	struct mixart_set_out_audio_level audio_level;
-	u32 resp;
+	u32 resp = 0;
 
 	if(chip->pipe_out_ana.status == PIPE_UNDEFINED)
 		return -EINVAL; /* no pipe defined */

+ 1 - 1
sound/pci/riptide/riptide.c

@@ -1496,7 +1496,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
 		f = PAGE_SIZE;
 		while ((size + (f >> 1) - 1) <= (f << 7) && (f << 1) > period)
 			f = f >> 1;
-		pages = (size + f - 1) / f;
+		pages = DIV_ROUND_UP(size, f);
 		data->size = size;
 		data->pages = pages;
 		snd_printdd

+ 1 - 0
sound/ppc/awacs.c

@@ -991,6 +991,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
 		if (err < 0)
 			return err;
 	}
+	master_vol = NULL;
 	if (pm7500)
 		err = build_mixers(chip,
 				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),

+ 3 - 13
sound/sh/aica.c

@@ -63,9 +63,6 @@ MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
 module_param(enable, bool, 0644);
 MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
 
-/* Use workqueue */
-static struct workqueue_struct *aica_queue;
-
 /* Simple platform device */
 static struct platform_device *pd;
 static struct resource aica_memory_space[2] = {
@@ -327,7 +324,7 @@ static void aica_period_elapsed(unsigned long timer_var)
 		dreamcastcard->current_period = play_period;
 	if (unlikely(dreamcastcard->dma_check == 0))
 		dreamcastcard->dma_check = 1;
-	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+	schedule_work(&(dreamcastcard->spu_dma_work));
 }
 
 static void spu_begin_dma(struct snd_pcm_substream *substream)
@@ -337,7 +334,7 @@ static void spu_begin_dma(struct snd_pcm_substream *substream)
 	runtime = substream->runtime;
 	dreamcastcard = substream->pcm->private_data;
 	/*get the queue to do the work */
-	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+	schedule_work(&(dreamcastcard->spu_dma_work));
 	/* Timer may already be running */
 	if (unlikely(dreamcastcard->timer.data)) {
 		mod_timer(&dreamcastcard->timer, jiffies + 4);
@@ -381,7 +378,7 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream
 				 *substream)
 {
 	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
-	flush_workqueue(aica_queue);
+	flush_work(&(dreamcastcard->spu_dma_work));
 	if (dreamcastcard->timer.data)
 		del_timer(&dreamcastcard->timer);
 	kfree(dreamcastcard->channel);
@@ -633,9 +630,6 @@ static int snd_aica_probe(struct platform_device *devptr)
 	if (unlikely(err < 0))
 		goto freedreamcast;
 	platform_set_drvdata(devptr, dreamcastcard);
-	aica_queue = create_workqueue(CARD_NAME);
-	if (unlikely(!aica_queue))
-		goto freedreamcast;
 	snd_printk
 	    ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
 	return 0;
@@ -671,10 +665,6 @@ static int __init aica_init(void)
 
 static void __exit aica_exit(void)
 {
-	/* Destroy the aica kernel thread            *
-	 * being extra cautious to check if it exists*/
-	if (likely(aica_queue))
-		destroy_workqueue(aica_queue);
 	platform_device_unregister(pd);
 	platform_driver_unregister(&snd_aica_driver);
 	/* Kill any sound still playing and reset ARM7 to safe state */

+ 1 - 0
sound/soc/atmel/Kconfig

@@ -10,6 +10,7 @@ if SND_ATMEL_SOC
 
 config SND_ATMEL_SOC_PDC
 	tristate
+	depends on HAS_DMA
 	default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
 	default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
 

+ 0 - 5
sound/soc/atmel/atmel-classd.c

@@ -593,11 +593,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		dev_err(dev, "no memory resource\n");
-		return -ENXIO;
-	}
-
 	io_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(io_base)) {
 		ret =  PTR_ERR(io_base);

+ 0 - 5
sound/soc/atmel/atmel-pdmic.c

@@ -624,11 +624,6 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
 	}
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		dev_err(dev, "no memory resource\n");
-		return -ENXIO;
-	}
-
 	io_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(io_base)) {
 		ret = PTR_ERR(io_base);

+ 1 - 1
sound/soc/atmel/atmel_ssc_dai.c

@@ -321,7 +321,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 		return ret;
 	}
 
-	dma_params = &ssc_dma_params[dai->id][dir];
+	dma_params = &ssc_dma_params[pdev->id][dir];
 	dma_params->ssc = ssc_p->ssc;
 	dma_params->substream = substream;
 

+ 9 - 0
sound/soc/bcm/Kconfig

@@ -7,3 +7,12 @@ config SND_BCM2835_SOC_I2S
 	  Say Y or M if you want to add support for codecs attached to
 	  the BCM2835 I2S interface. You will also need
 	  to select the audio interfaces to support below.
+
+config SND_SOC_CYGNUS
+	tristate "SoC platform audio for Broadcom Cygnus chips"
+	depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+	help
+	  Say Y if you want to add support for ASoC audio on Broadcom
+	  Cygnus chips (bcm958300, bcm958305, bcm911360)
+
+	  If you don't know what to do here, say N.

+ 5 - 0
sound/soc/bcm/Makefile

@@ -3,3 +3,8 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
 
 obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
 
+# CYGNUS Platform Support
+snd-soc-cygnus-objs := cygnus-pcm.o cygnus-ssp.o
+
+obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
+

+ 861 - 0
sound/soc/bcm/cygnus-pcm.c

@@ -0,0 +1,861 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "cygnus-ssp.h"
+
+/* Register offset needed for ASoC PCM module */
+
+#define INTH_R5F_STATUS_OFFSET     0x040
+#define INTH_R5F_CLEAR_OFFSET      0x048
+#define INTH_R5F_MASK_SET_OFFSET   0x050
+#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
+
+#define BF_REARM_FREE_MARK_OFFSET 0x344
+#define BF_REARM_FULL_MARK_OFFSET 0x348
+
+/* Ring Buffer Ctrl Regs --- Start */
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
+#define SRC_RBUF_0_RDADDR_OFFSET 0x500
+#define SRC_RBUF_1_RDADDR_OFFSET 0x518
+#define SRC_RBUF_2_RDADDR_OFFSET 0x530
+#define SRC_RBUF_3_RDADDR_OFFSET 0x548
+#define SRC_RBUF_4_RDADDR_OFFSET 0x560
+#define SRC_RBUF_5_RDADDR_OFFSET 0x578
+#define SRC_RBUF_6_RDADDR_OFFSET 0x590
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
+#define SRC_RBUF_0_WRADDR_OFFSET 0x504
+#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
+#define SRC_RBUF_2_WRADDR_OFFSET 0x534
+#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
+#define SRC_RBUF_4_WRADDR_OFFSET 0x564
+#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
+#define SRC_RBUF_6_WRADDR_OFFSET 0x594
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
+#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
+#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
+#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
+#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
+#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
+#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
+#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
+#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
+#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
+#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
+#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
+#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
+#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
+#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
+#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
+#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
+#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
+#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
+#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
+#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
+#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
+#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
+#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
+#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
+#define DST_RBUF_3_RDADDR_OFFSET 0x608
+#define DST_RBUF_4_RDADDR_OFFSET 0x620
+#define DST_RBUF_5_RDADDR_OFFSET 0x638
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
+#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
+#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
+#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
+#define DST_RBUF_3_WRADDR_OFFSET 0x60c
+#define DST_RBUF_4_WRADDR_OFFSET 0x624
+#define DST_RBUF_5_WRADDR_OFFSET 0x63c
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
+#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
+#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
+#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
+#define DST_RBUF_3_BASEADDR_OFFSET 0x610
+#define DST_RBUF_4_BASEADDR_OFFSET 0x628
+#define DST_RBUF_5_BASEADDR_OFFSET 0x640
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
+#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
+#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
+#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
+#define DST_RBUF_3_ENDADDR_OFFSET 0x614
+#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
+#define DST_RBUF_5_ENDADDR_OFFSET 0x644
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
+#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
+#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
+#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
+#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
+#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
+#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
+/* Ring Buffer Ctrl Regs --- End */
+
+/* Error Status Regs --- Start */
+/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
+#define ESR0_STATUS_OFFSET 0x900
+#define ESR1_STATUS_OFFSET 0x918
+#define ESR2_STATUS_OFFSET 0x930
+#define ESR3_STATUS_OFFSET 0x948
+#define ESR4_STATUS_OFFSET 0x960
+
+/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
+#define ESR0_STATUS_CLR_OFFSET 0x908
+#define ESR1_STATUS_CLR_OFFSET 0x920
+#define ESR2_STATUS_CLR_OFFSET 0x938
+#define ESR3_STATUS_CLR_OFFSET 0x950
+#define ESR4_STATUS_CLR_OFFSET 0x968
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
+#define ESR0_MASK_STATUS_OFFSET 0x90c
+#define ESR1_MASK_STATUS_OFFSET 0x924
+#define ESR2_MASK_STATUS_OFFSET 0x93c
+#define ESR3_MASK_STATUS_OFFSET 0x954
+#define ESR4_MASK_STATUS_OFFSET 0x96c
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
+#define ESR0_MASK_SET_OFFSET 0x910
+#define ESR1_MASK_SET_OFFSET 0x928
+#define ESR2_MASK_SET_OFFSET 0x940
+#define ESR3_MASK_SET_OFFSET 0x958
+#define ESR4_MASK_SET_OFFSET 0x970
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
+#define ESR0_MASK_CLR_OFFSET 0x914
+#define ESR1_MASK_CLR_OFFSET 0x92c
+#define ESR2_MASK_CLR_OFFSET 0x944
+#define ESR3_MASK_CLR_OFFSET 0x95c
+#define ESR4_MASK_CLR_OFFSET 0x974
+/* Error Status Regs --- End */
+
+#define R5F_ESR0_SHIFT  0    /* esr0 = fifo underflow */
+#define R5F_ESR1_SHIFT  1    /* esr1 = ringbuf underflow */
+#define R5F_ESR2_SHIFT  2    /* esr2 = ringbuf overflow */
+#define R5F_ESR3_SHIFT  3    /* esr3 = freemark */
+#define R5F_ESR4_SHIFT  4    /* esr4 = fullmark */
+
+
+/* Mask for R5F register.  Set all relevant interrupt for playback handler */
+#define ANY_PLAYBACK_IRQ  (BIT(R5F_ESR0_SHIFT) | \
+			   BIT(R5F_ESR1_SHIFT) | \
+			   BIT(R5F_ESR3_SHIFT))
+
+/* Mask for R5F register.  Set all relevant interrupt for capture handler */
+#define ANY_CAPTURE_IRQ   (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
+
+/*
+ * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
+ * This number should be a multiple of 256. Minimum value is 256
+ */
+#define PERIOD_BYTES_MIN 0x100
+
+static const struct snd_pcm_hardware cygnus_pcm_hw = {
+	.info = SNDRV_PCM_INFO_MMAP |
+			SNDRV_PCM_INFO_MMAP_VALID |
+			SNDRV_PCM_INFO_INTERLEAVED,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+
+	/* A period is basically an interrupt */
+	.period_bytes_min = PERIOD_BYTES_MIN,
+	.period_bytes_max = 0x10000,
+
+	/* period_min/max gives range of approx interrupts per buffer */
+	.periods_min = 2,
+	.periods_max = 8,
+
+	/*
+	 * maximum buffer size in bytes = period_bytes_max * periods_max
+	 * We allocate this amount of data for each enabled channel
+	 */
+	.buffer_bytes_max = 4 * 0x8000,
+};
+
+static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
+
+static struct cygnus_aio_port *cygnus_dai_get_dma_data(
+				struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+
+	return snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
+}
+
+static void ringbuf_set_initial(void __iomem *audio_io,
+		struct ringbuf_regs *p_rbuf,
+		bool is_playback,
+		u32 start,
+		u32 periodsize,
+		u32 bufsize)
+{
+	u32 initial_rd;
+	u32 initial_wr;
+	u32 end;
+	u32 fmark_val; /* free or full mark */
+
+	p_rbuf->period_bytes = periodsize;
+	p_rbuf->buf_size = bufsize;
+
+	if (is_playback) {
+		/* Set the pointers to indicate full (flip uppermost bit) */
+		initial_rd = start;
+		initial_wr = initial_rd ^ BIT(31);
+	} else {
+		/* Set the pointers to indicate empty */
+		initial_wr = start;
+		initial_rd = initial_wr;
+	}
+
+	end = start + bufsize - 1;
+
+	/*
+	 * The interrupt will fire when free/full mark is *exceeded*
+	 * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
+	 * to be PERIOD_BYTES_MIN less than the period size.
+	 */
+	fmark_val = periodsize - PERIOD_BYTES_MIN;
+
+	writel(start, audio_io + p_rbuf->baseaddr);
+	writel(end, audio_io + p_rbuf->endaddr);
+	writel(fmark_val, audio_io + p_rbuf->fmark);
+	writel(initial_rd, audio_io + p_rbuf->rdaddr);
+	writel(initial_wr, audio_io + p_rbuf->wraddr);
+}
+
+static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
+{
+	struct cygnus_aio_port *aio;
+	struct ringbuf_regs *p_rbuf;
+	int status = 0;
+
+	aio = cygnus_dai_get_dma_data(substream);
+
+	/* Map the ssp portnum to a set of ring buffers. */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		p_rbuf = &aio->play_rb_regs;
+
+		switch (aio->portnum) {
+		case 0:
+			*p_rbuf = RINGBUF_REG_PLAYBACK(0);
+			break;
+		case 1:
+			*p_rbuf = RINGBUF_REG_PLAYBACK(2);
+			break;
+		case 2:
+			*p_rbuf = RINGBUF_REG_PLAYBACK(4);
+			break;
+		case 3: /* SPDIF */
+			*p_rbuf = RINGBUF_REG_PLAYBACK(6);
+			break;
+		default:
+			status = -EINVAL;
+		}
+	} else {
+		p_rbuf = &aio->capture_rb_regs;
+
+		switch (aio->portnum) {
+		case 0:
+			*p_rbuf = RINGBUF_REG_CAPTURE(0);
+			break;
+		case 1:
+			*p_rbuf = RINGBUF_REG_CAPTURE(2);
+			break;
+		case 2:
+			*p_rbuf = RINGBUF_REG_CAPTURE(4);
+			break;
+		default:
+			status = -EINVAL;
+		}
+	}
+
+	return status;
+}
+
+static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
+{
+	struct cygnus_aio_port *aio;
+	struct ringbuf_regs *p_rbuf = NULL;
+
+	aio = cygnus_dai_get_dma_data(substream);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		p_rbuf = &aio->play_rb_regs;
+	else
+		p_rbuf = &aio->capture_rb_regs;
+
+	return p_rbuf;
+}
+
+static void enable_intr(struct snd_pcm_substream *substream)
+{
+	struct cygnus_aio_port *aio;
+	u32 clear_mask;
+
+	aio = cygnus_dai_get_dma_data(substream);
+
+	/* The port number maps to the bit position to be cleared */
+	clear_mask = BIT(aio->portnum);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* Clear interrupt status before enabling them */
+		writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
+		writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
+		writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
+		/* Unmask the interrupts of the given port*/
+		writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
+		writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
+		writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
+
+		writel(ANY_PLAYBACK_IRQ,
+			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+	} else {
+		writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
+		writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
+		writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
+		writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
+
+		writel(ANY_CAPTURE_IRQ,
+			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+	}
+
+}
+
+static void disable_intr(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cygnus_aio_port *aio;
+	u32 set_mask;
+
+	aio = cygnus_dai_get_dma_data(substream);
+
+	dev_dbg(rtd->cpu_dai->dev, "%s on port %d\n", __func__, aio->portnum);
+
+	/* The port number maps to the bit position to be set */
+	set_mask = BIT(aio->portnum);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* Mask the interrupts of the given port*/
+		writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
+		writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
+		writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
+	} else {
+		writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
+		writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
+	}
+
+}
+
+static int cygnus_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		enable_intr(substream);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		disable_intr(substream);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+	struct cygnus_aio_port *aio;
+	struct ringbuf_regs *p_rbuf = NULL;
+	u32 regval;
+
+	aio = cygnus_dai_get_dma_data(substream);
+
+	p_rbuf = get_ringbuf(substream);
+
+	/*
+	 * If free/full mark interrupt occurs, provide timestamp
+	 * to ALSA and update appropriate idx by period_bytes
+	 */
+	snd_pcm_period_elapsed(substream);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* Set the ring buffer to full */
+		regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+		regval = regval ^ BIT(31);
+		writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
+	} else {
+		/* Set the ring buffer to empty */
+		regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
+		writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
+	}
+}
+
+/*
+ * ESR0/1/3 status  Description
+ *  0x1	I2S0_out port caused interrupt
+ *  0x2	I2S1_out port caused interrupt
+ *  0x4	I2S2_out port caused interrupt
+ *  0x8	SPDIF_out port caused interrupt
+ */
+static void handle_playback_irq(struct cygnus_audio *cygaud)
+{
+	void __iomem *audio_io;
+	u32 port;
+	u32 esr_status0, esr_status1, esr_status3;
+
+	audio_io = cygaud->audio;
+
+	/*
+	 * ESR status gets updates with/without interrupts enabled.
+	 * So, check the ESR mask, which provides interrupt enable/
+	 * disable status and use it to determine which ESR status
+	 * should be serviced.
+	 */
+	esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
+	esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
+	esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
+	esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
+	esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
+	esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
+
+	for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
+		u32 esrmask = BIT(port);
+
+		/*
+		 * Ringbuffer or FIFO underflow
+		 * If we get this interrupt then, it is also true that we have
+		 * not yet responded to the freemark interrupt.
+		 * Log a debug message.  The freemark handler below will
+		 * handle getting everything going again.
+		 */
+		if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
+			dev_dbg(cygaud->dev,
+				"Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
+				esr_status0, esr_status1, esr_status3);
+		}
+
+		/*
+		 * Freemark is hit. This is the normal interrupt.
+		 * In typical operation the read and write regs will be equal
+		 */
+		if (esrmask & esr_status3) {
+			struct snd_pcm_substream *playstr;
+
+			playstr = cygaud->portinfo[port].play_stream;
+			cygnus_pcm_period_elapsed(playstr);
+		}
+	}
+
+	/* Clear ESR interrupt */
+	writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
+	writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
+	writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
+	/* Rearm freemark logic by writing 1 to the correct bit */
+	writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
+}
+
+/*
+ * ESR2/4 status  Description
+ *  0x1	I2S0_in port caused interrupt
+ *  0x2	I2S1_in port caused interrupt
+ *  0x4	I2S2_in port caused interrupt
+ */
+static void handle_capture_irq(struct cygnus_audio *cygaud)
+{
+	void __iomem *audio_io;
+	u32 port;
+	u32 esr_status2, esr_status4;
+
+	audio_io = cygaud->audio;
+
+	/*
+	 * ESR status gets updates with/without interrupts enabled.
+	 * So, check the ESR mask, which provides interrupt enable/
+	 * disable status and use it to determine which ESR status
+	 * should be serviced.
+	 */
+	esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
+	esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
+	esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
+	esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
+
+	for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
+		u32 esrmask = BIT(port);
+
+		/*
+		 * Ringbuffer or FIFO overflow
+		 * If we get this interrupt then, it is also true that we have
+		 * not yet responded to the fullmark interrupt.
+		 * Log a debug message.  The fullmark handler below will
+		 * handle getting everything going again.
+		 */
+		if (esrmask & esr_status2)
+			dev_dbg(cygaud->dev,
+				"Overflow: esr2=0x%x\n", esr_status2);
+
+		if (esrmask & esr_status4) {
+			struct snd_pcm_substream *capstr;
+
+			capstr = cygaud->portinfo[port].capture_stream;
+			cygnus_pcm_period_elapsed(capstr);
+		}
+	}
+
+	writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
+	writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
+	/* Rearm fullmark logic by writing 1 to the correct bit */
+	writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
+}
+
+static irqreturn_t cygnus_dma_irq(int irq, void *data)
+{
+	u32 r5_status;
+	struct cygnus_audio *cygaud = data;
+
+	/*
+	 * R5 status bits	Description
+	 *  0		ESR0 (playback FIFO interrupt)
+	 *  1		ESR1 (playback rbuf interrupt)
+	 *  2		ESR2 (capture rbuf interrupt)
+	 *  3		ESR3 (Freemark play. interrupt)
+	 *  4		ESR4 (Fullmark capt. interrupt)
+	 */
+	r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
+
+	if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
+		return IRQ_NONE;
+
+	/* If playback interrupt happened */
+	if (ANY_PLAYBACK_IRQ & r5_status) {
+		handle_playback_irq(cygaud);
+		writel(ANY_PLAYBACK_IRQ & r5_status,
+			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+	}
+
+	/* If  capture interrupt happened */
+	if (ANY_CAPTURE_IRQ & r5_status) {
+		handle_capture_irq(cygaud);
+		writel(ANY_CAPTURE_IRQ & r5_status,
+			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int cygnus_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct cygnus_aio_port *aio;
+	int ret;
+
+	aio = cygnus_dai_get_dma_data(substream);
+	if (!aio)
+		return -ENODEV;
+
+	dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+
+	snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
+
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
+	if (ret < 0)
+		return ret;
+	/*
+	 * Keep track of which substream belongs to which port.
+	 * This info is needed by snd_pcm_period_elapsed() in irq_handler
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		aio->play_stream = substream;
+	else
+		aio->capture_stream = substream;
+
+	return 0;
+}
+
+static int cygnus_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cygnus_aio_port *aio;
+
+	aio = cygnus_dai_get_dma_data(substream);
+
+	dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		aio->play_stream = NULL;
+	else
+		aio->capture_stream = NULL;
+
+	if (!aio->play_stream && !aio->capture_stream)
+		dev_dbg(rtd->cpu_dai->dev, "freed  port %d\n", aio->portnum);
+
+	return 0;
+}
+
+static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct cygnus_aio_port *aio;
+	int ret = 0;
+
+	aio = cygnus_dai_get_dma_data(substream);
+	dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = params_buffer_bytes(params);
+
+	return ret;
+}
+
+static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cygnus_aio_port *aio;
+
+	aio = cygnus_dai_get_dma_data(substream);
+	dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct cygnus_aio_port *aio;
+	unsigned long bufsize, periodsize;
+	int ret = 0;
+	bool is_play;
+	u32 start;
+	struct ringbuf_regs *p_rbuf = NULL;
+
+	aio = cygnus_dai_get_dma_data(substream);
+	dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+
+	bufsize = snd_pcm_lib_buffer_bytes(substream);
+	periodsize = snd_pcm_lib_period_bytes(substream);
+
+	dev_dbg(rtd->cpu_dai->dev, "%s (buf_size %lu) (period_size %lu)\n",
+			__func__, bufsize, periodsize);
+
+	configure_ringbuf_regs(substream);
+
+	p_rbuf = get_ringbuf(substream);
+
+	start = runtime->dma_addr;
+
+	is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
+
+	ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
+				periodsize, bufsize);
+
+	return ret;
+}
+
+static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct cygnus_aio_port *aio;
+	unsigned int res = 0, cur = 0, base = 0;
+	struct ringbuf_regs *p_rbuf = NULL;
+
+	aio = cygnus_dai_get_dma_data(substream);
+
+	/*
+	 * Get the offset of the current read (for playack) or write
+	 * index (for capture).  Report this value back to the asoc framework.
+	 */
+	p_rbuf = get_ringbuf(substream);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+	else
+		cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
+
+	base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
+
+	/*
+	 * Mask off the MSB of the rdaddr,wraddr and baseaddr
+	 * since MSB is not part of the address
+	 */
+	res = (cur & 0x7fffffff) - (base & 0x7fffffff);
+
+	return bytes_to_frames(substream->runtime, res);
+}
+
+static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size;
+
+	size = cygnus_pcm_hw.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+			&buf->addr, GFP_KERNEL);
+
+	dev_dbg(rtd->cpu_dai->dev, "%s: size 0x%zx @ %pK\n",
+				__func__, size, buf->area);
+
+	if (!buf->area) {
+		dev_err(rtd->cpu_dai->dev, "%s: dma_alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+	buf->bytes = size;
+
+	return 0;
+}
+
+
+static const struct snd_pcm_ops cygnus_pcm_ops = {
+	.open		= cygnus_pcm_open,
+	.close		= cygnus_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= cygnus_pcm_hw_params,
+	.hw_free	= cygnus_pcm_hw_free,
+	.prepare	= cygnus_pcm_prepare,
+	.trigger	= cygnus_pcm_trigger,
+	.pointer	= cygnus_pcm_pointer,
+};
+
+static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+
+	substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	if (substream) {
+		buf = &substream->dma_buffer;
+		if (buf->area) {
+			dma_free_coherent(pcm->card->dev, buf->bytes,
+				buf->area, buf->addr);
+			buf->area = NULL;
+		}
+	}
+
+	substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+	if (substream) {
+		buf = &substream->dma_buffer;
+		if (buf->area) {
+			dma_free_coherent(pcm->card->dev, buf->bytes,
+				buf->area, buf->addr);
+			buf->area = NULL;
+		}
+	}
+}
+
+static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &cygnus_dma_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+		ret = cygnus_pcm_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			return ret;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ret = cygnus_pcm_preallocate_dma_buffer(pcm,
+				SNDRV_PCM_STREAM_CAPTURE);
+		if (ret) {
+			cygnus_dma_free_dma_buffers(pcm);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver cygnus_soc_platform = {
+	.ops		= &cygnus_pcm_ops,
+	.pcm_new	= cygnus_dma_new,
+	.pcm_free	= cygnus_dma_free_dma_buffers,
+};
+
+int cygnus_soc_platform_register(struct device *dev,
+				 struct cygnus_audio *cygaud)
+{
+	int rc = 0;
+
+	dev_dbg(dev, "%s Enter\n", __func__);
+
+	rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
+				IRQF_SHARED, "cygnus-audio", cygaud);
+	if (rc) {
+		dev_err(dev, "%s request_irq error %d\n", __func__, rc);
+		return rc;
+	}
+
+	rc = snd_soc_register_platform(dev, &cygnus_soc_platform);
+	if (rc) {
+		dev_err(dev, "%s failed\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+int cygnus_soc_platform_unregister(struct device *dev)
+{
+	snd_soc_unregister_platform(dev);
+
+	return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC PCM module");

+ 1529 - 0
sound/soc/bcm/cygnus-ssp.c

@@ -0,0 +1,1529 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "cygnus-ssp.h"
+
+#define DEFAULT_VCO    1354750204
+
+#define CYGNUS_TDM_RATE \
+		(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+		SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | \
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+		SNDRV_PCM_RATE_48000)
+
+#define CAPTURE_FCI_ID_BASE 0x180
+#define CYGNUS_SSP_TRISTATE_MASK 0x001fff
+#define CYGNUS_PLLCLKSEL_MASK 0xf
+
+/* Used with stream_on field to indicate which streams are active */
+#define  PLAYBACK_STREAM_MASK   BIT(0)
+#define  CAPTURE_STREAM_MASK    BIT(1)
+
+#define I2S_STREAM_CFG_MASK      0xff003ff
+#define I2S_CAP_STREAM_CFG_MASK  0xf0
+#define SPDIF_STREAM_CFG_MASK    0x3ff
+#define CH_GRP_STEREO            0x1
+
+/* Begin register offset defines */
+#define AUD_MISC_SEROUT_OE_REG_BASE  0x01c
+#define AUD_MISC_SEROUT_SPDIF_OE  12
+#define AUD_MISC_SEROUT_MCLK_OE   3
+#define AUD_MISC_SEROUT_LRCK_OE   2
+#define AUD_MISC_SEROUT_SCLK_OE   1
+#define AUD_MISC_SEROUT_SDAT_OE   0
+
+/* AUD_FMM_BF_CTRL_xxx regs */
+#define BF_DST_CFG0_OFFSET  0x100
+#define BF_DST_CFG1_OFFSET  0x104
+#define BF_DST_CFG2_OFFSET  0x108
+
+#define BF_DST_CTRL0_OFFSET 0x130
+#define BF_DST_CTRL1_OFFSET 0x134
+#define BF_DST_CTRL2_OFFSET 0x138
+
+#define BF_SRC_CFG0_OFFSET  0x148
+#define BF_SRC_CFG1_OFFSET  0x14c
+#define BF_SRC_CFG2_OFFSET  0x150
+#define BF_SRC_CFG3_OFFSET  0x154
+
+#define BF_SRC_CTRL0_OFFSET 0x1c0
+#define BF_SRC_CTRL1_OFFSET 0x1c4
+#define BF_SRC_CTRL2_OFFSET 0x1c8
+#define BF_SRC_CTRL3_OFFSET 0x1cc
+
+#define BF_SRC_GRP0_OFFSET  0x1fc
+#define BF_SRC_GRP1_OFFSET  0x200
+#define BF_SRC_GRP2_OFFSET  0x204
+#define BF_SRC_GRP3_OFFSET  0x208
+
+#define BF_SRC_GRP_EN_OFFSET        0x320
+#define BF_SRC_GRP_FLOWON_OFFSET    0x324
+#define BF_SRC_GRP_SYNC_DIS_OFFSET  0x328
+
+/* AUD_FMM_IOP_OUT_I2S_xxx regs */
+#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00
+#define OUT_I2S_0_CFG_OFFSET        0xa04
+#define OUT_I2S_0_MCLK_CFG_OFFSET   0xa0c
+
+#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40
+#define OUT_I2S_1_CFG_OFFSET        0xa44
+#define OUT_I2S_1_MCLK_CFG_OFFSET   0xa4c
+
+#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80
+#define OUT_I2S_2_CFG_OFFSET        0xa84
+#define OUT_I2S_2_MCLK_CFG_OFFSET   0xa8c
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */
+#define SPDIF_STREAM_CFG_OFFSET  0xac0
+#define SPDIF_CTRL_OFFSET        0xac4
+#define SPDIF_FORMAT_CFG_OFFSET  0xad8
+#define SPDIF_MCLK_CFG_OFFSET    0xadc
+
+/* AUD_FMM_IOP_PLL_0_xxx regs */
+#define IOP_PLL_0_MACRO_OFFSET    0xb00
+#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14
+#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18
+#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c
+
+#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30
+#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34
+#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38
+
+/* AUD_FMM_IOP_xxx regs */
+#define IOP_PLL_0_CONTROL_OFFSET     0xb04
+#define IOP_PLL_0_USER_NDIV_OFFSET   0xb08
+#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20
+#define IOP_PLL_0_RESET_OFFSET       0xb5c
+
+/* AUD_FMM_IOP_IN_I2S_xxx regs */
+#define IN_I2S_0_STREAM_CFG_OFFSET 0x00
+#define IN_I2S_0_CFG_OFFSET        0x04
+#define IN_I2S_1_STREAM_CFG_OFFSET 0x40
+#define IN_I2S_1_CFG_OFFSET        0x44
+#define IN_I2S_2_STREAM_CFG_OFFSET 0x80
+#define IN_I2S_2_CFG_OFFSET        0x84
+
+/* AUD_FMM_IOP_MISC_xxx regs */
+#define IOP_SW_INIT_LOGIC          0x1c0
+
+/* End register offset defines */
+
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */
+#define I2S_OUT_MCLKRATE_SHIFT 16
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */
+#define I2S_OUT_PLLCLKSEL_SHIFT  0
+
+/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */
+#define I2S_OUT_STREAM_ENA  31
+#define I2S_OUT_STREAM_CFG_GROUP_ID  20
+#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING  24
+
+/* AUD_FMM_IOP_IN_I2S_x_CAP */
+#define I2S_IN_STREAM_CFG_CAP_ENA   31
+#define I2S_IN_STREAM_CFG_0_GROUP_ID 4
+
+/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */
+#define I2S_OUT_CFGX_CLK_ENA         0
+#define I2S_OUT_CFGX_DATA_ENABLE     1
+#define I2S_OUT_CFGX_DATA_ALIGNMENT  6
+#define I2S_OUT_CFGX_BITS_PER_SLOT  13
+#define I2S_OUT_CFGX_VALID_SLOT     14
+#define I2S_OUT_CFGX_FSYNC_WIDTH    18
+#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26
+#define I2S_OUT_CFGX_SLAVE_MODE     30
+#define I2S_OUT_CFGX_TDM_MODE       31
+
+/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */
+#define BF_SRC_CFGX_SFIFO_ENA              0
+#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE     1
+#define BF_SRC_CFGX_SAMPLE_CH_MODE         2
+#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE        5
+#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY  10
+#define BF_SRC_CFGX_BIT_RES               20
+#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID  31
+
+/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */
+#define BF_DST_CFGX_CAP_ENA              0
+#define BF_DST_CFGX_BUFFER_PAIR_ENABLE   1
+#define BF_DST_CFGX_DFIFO_SZ_DOUBLE      2
+#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11
+#define BF_DST_CFGX_FCI_ID              12
+#define BF_DST_CFGX_CAP_MODE            24
+#define BF_DST_CFGX_PROC_SEQ_ID_VALID   31
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx */
+#define SPDIF_0_OUT_DITHER_ENA     3
+#define SPDIF_0_OUT_STREAM_ENA    31
+
+/* AUD_FMM_IOP_PLL_0_USER */
+#define IOP_PLL_0_USER_NDIV_FRAC   10
+
+/* AUD_FMM_IOP_PLL_0_ACTIVE */
+#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10
+
+
+#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \
+		.i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \
+		.i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \
+		.i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \
+		.i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \
+		.i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \
+		.bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \
+		.bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \
+		.bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \
+		.bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \
+		.bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \
+}
+
+struct pll_macro_entry {
+	u32 mclk;
+	u32 pll_ch_num;
+};
+
+/*
+ * PLL has 3 output channels (1x, 2x, and 4x). Below are
+ * the common MCLK frequencies used by audio driver
+ */
+static const struct pll_macro_entry pll_predef_mclk[] = {
+	{ 4096000, 0},
+	{ 8192000, 1},
+	{16384000, 2},
+
+	{ 5644800, 0},
+	{11289600, 1},
+	{22579200, 2},
+
+	{ 6144000, 0},
+	{12288000, 1},
+	{24576000, 2},
+
+	{12288000, 0},
+	{24576000, 1},
+	{49152000, 2},
+
+	{22579200, 0},
+	{45158400, 1},
+	{90316800, 2},
+
+	{24576000, 0},
+	{49152000, 1},
+	{98304000, 2},
+};
+
+/* List of valid frame sizes for tdm mode */
+static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};
+
+/*
+ * Use this relationship to derive the sampling rate (lrclk)
+ * lrclk = (mclk) / ((2*mclk_to_sclk_ratio) * (32 * SCLK))).
+ *
+ * Use mclk and pll_ch from the table above
+ *
+ * Valid SCLK = 0/1/2/4/8/12
+ *
+ * mclk_to_sclk_ratio = number of MCLK per SCLK. Division is twice the
+ * value programmed in this field.
+ * Valid mclk_to_sclk_ratio = 1 through to 15
+ *
+ * eg: To set lrclk = 48khz, set mclk = 12288000, mclk_to_sclk_ratio = 2,
+ * SCLK = 64
+ */
+struct _ssp_clk_coeff {
+	u32 mclk;
+	u32 sclk_rate;
+	u32 rate;
+	u32 mclk_rate;
+};
+
+static const struct _ssp_clk_coeff ssp_clk_coeff[] = {
+	{ 4096000,  32,  16000, 4},
+	{ 4096000,  32,  32000, 2},
+	{ 4096000,  64,   8000, 4},
+	{ 4096000,  64,  16000, 2},
+	{ 4096000,  64,  32000, 1},
+	{ 4096000, 128,   8000, 2},
+	{ 4096000, 128,  16000, 1},
+	{ 4096000, 256,   8000, 1},
+
+	{ 6144000,  32,  16000, 6},
+	{ 6144000,  32,  32000, 3},
+	{ 6144000,  32,  48000, 2},
+	{ 6144000,  32,  96000, 1},
+	{ 6144000,  64,   8000, 6},
+	{ 6144000,  64,  16000, 3},
+	{ 6144000,  64,  48000, 1},
+	{ 6144000, 128,   8000, 3},
+
+	{ 8192000,  32,  32000, 4},
+	{ 8192000,  64,  16000, 4},
+	{ 8192000,  64,  32000, 2},
+	{ 8192000, 128,   8000, 4},
+	{ 8192000, 128,  16000, 2},
+	{ 8192000, 128,  32000, 1},
+	{ 8192000, 256,   8000, 2},
+	{ 8192000, 256,  16000, 1},
+	{ 8192000, 512,   8000, 1},
+
+	{12288000,  32,  32000, 6},
+	{12288000,  32,  48000, 4},
+	{12288000,  32,  96000, 2},
+	{12288000,  32, 192000, 1},
+	{12288000,  64,  16000, 6},
+	{12288000,  64,  32000, 3},
+	{12288000,  64,  48000, 2},
+	{12288000,  64,  96000, 1},
+	{12288000, 128,   8000, 6},
+	{12288000, 128,  16000, 3},
+	{12288000, 128,  48000, 1},
+	{12288000, 256,   8000, 3},
+
+	{16384000,  64,  32000, 4},
+	{16384000, 128,  16000, 4},
+	{16384000, 128,  32000, 2},
+	{16384000, 256,   8000, 4},
+	{16384000, 256,  16000, 2},
+	{16384000, 256,  32000, 1},
+	{16384000, 512,   8000, 2},
+	{16384000, 512,  16000, 1},
+
+	{24576000,  32,  96000, 4},
+	{24576000,  32, 192000, 2},
+	{24576000,  64,  32000, 6},
+	{24576000,  64,  48000, 4},
+	{24576000,  64,  96000, 2},
+	{24576000,  64, 192000, 1},
+	{24576000, 128,  16000, 6},
+	{24576000, 128,  32000, 3},
+	{24576000, 128,  48000, 2},
+	{24576000, 256,   8000, 6},
+	{24576000, 256,  16000, 3},
+	{24576000, 256,  48000, 1},
+	{24576000, 512,   8000, 3},
+
+	{49152000,  32, 192000, 4},
+	{49152000,  64,  96000, 4},
+	{49152000,  64, 192000, 2},
+	{49152000, 128,  32000, 6},
+	{49152000, 128,  48000, 4},
+	{49152000, 128,  96000, 2},
+	{49152000, 128, 192000, 1},
+	{49152000, 256,  16000, 6},
+	{49152000, 256,  32000, 3},
+	{49152000, 256,  48000, 2},
+	{49152000, 256,  96000, 1},
+	{49152000, 512,   8000, 6},
+	{49152000, 512,  16000, 3},
+	{49152000, 512,  48000, 1},
+
+	{ 5644800,  32,  22050, 4},
+	{ 5644800,  32,  44100, 2},
+	{ 5644800,  32,  88200, 1},
+	{ 5644800,  64,  11025, 4},
+	{ 5644800,  64,  22050, 2},
+	{ 5644800,  64,  44100, 1},
+
+	{11289600,  32,  44100, 4},
+	{11289600,  32,  88200, 2},
+	{11289600,  32, 176400, 1},
+	{11289600,  64,  22050, 4},
+	{11289600,  64,  44100, 2},
+	{11289600,  64,  88200, 1},
+	{11289600, 128,  11025, 4},
+	{11289600, 128,  22050, 2},
+	{11289600, 128,  44100, 1},
+
+	{22579200,  32,  88200, 4},
+	{22579200,  32, 176400, 2},
+	{22579200,  64,  44100, 4},
+	{22579200,  64,  88200, 2},
+	{22579200,  64, 176400, 1},
+	{22579200, 128,  22050, 4},
+	{22579200, 128,  44100, 2},
+	{22579200, 128,  88200, 1},
+	{22579200, 256,  11025, 4},
+	{22579200, 256,  22050, 2},
+	{22579200, 256,  44100, 1},
+
+	{45158400,  32, 176400, 4},
+	{45158400,  64,  88200, 4},
+	{45158400,  64, 176400, 2},
+	{45158400, 128,  44100, 4},
+	{45158400, 128,  88200, 2},
+	{45158400, 128, 176400, 1},
+	{45158400, 256,  22050, 4},
+	{45158400, 256,  44100, 2},
+	{45158400, 256,  88200, 1},
+	{45158400, 512,  11025, 4},
+	{45158400, 512,  22050, 2},
+	{45158400, 512,  44100, 1},
+};
+
+static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai)
+{
+	struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+	return &cygaud->portinfo[dai->id];
+}
+
+static int audio_ssp_init_portregs(struct cygnus_aio_port *aio)
+{
+	u32 value, fci_id;
+	int status = 0;
+
+	switch (aio->port_type) {
+	case PORT_TDM:
+		value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+		value &= ~I2S_STREAM_CFG_MASK;
+
+		/* Set Group ID */
+		writel(aio->portnum,
+			aio->cygaud->audio + aio->regs.bf_sourcech_grp);
+
+		/* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */
+		value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID;
+		value |= aio->portnum; /* FCI ID is the port num */
+		value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING;
+		writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+		/* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+		value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+		value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+		/* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */
+		value = readl(aio->cygaud->i2s_in +
+			aio->regs.i2s_cap_stream_cfg);
+		value &= ~I2S_CAP_STREAM_CFG_MASK;
+		value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID;
+		writel(value, aio->cygaud->i2s_in +
+			aio->regs.i2s_cap_stream_cfg);
+
+		/* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */
+		fci_id = CAPTURE_FCI_ID_BASE + aio->portnum;
+
+		value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+		value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE);
+		value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL);
+		value |= (fci_id << BF_DST_CFGX_FCI_ID);
+		value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID);
+		writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+		/* Enable the transmit pin for this port */
+		value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+		value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE);
+		writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+		break;
+	case PORT_SPDIF:
+		writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET);
+
+		value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+		value |= BIT(SPDIF_0_OUT_DITHER_ENA);
+		writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+
+		/* Enable and set the FCI ID for the SPDIF channel */
+		value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+		value &= ~SPDIF_STREAM_CFG_MASK;
+		value |= aio->portnum; /* FCI ID is the port num */
+		value |= BIT(SPDIF_0_OUT_STREAM_ENA);
+		writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+		value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+		value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+		/* Enable the spdif output pin */
+		value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+		value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE);
+		writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+		break;
+	default:
+		dev_err(aio->cygaud->dev, "Port not supported\n");
+		status = -EINVAL;
+	}
+
+	return status;
+}
+
+static void audio_ssp_in_enable(struct cygnus_aio_port *aio)
+{
+	u32 value;
+
+	value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+	value |= BIT(BF_DST_CFGX_CAP_ENA);
+	writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+	writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+	value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+	value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+	value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+	writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+	value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+	value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+	writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+	aio->streams_on |= CAPTURE_STREAM_MASK;
+}
+
+static void audio_ssp_in_disable(struct cygnus_aio_port *aio)
+{
+	u32 value;
+
+	value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+	value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+	writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+	aio->streams_on &= ~CAPTURE_STREAM_MASK;
+
+	/* If both playback and capture are off */
+	if (!aio->streams_on) {
+		value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+		value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+		value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+		writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+	}
+
+	writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+	value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+	value &= ~BIT(BF_DST_CFGX_CAP_ENA);
+	writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+}
+
+static int audio_ssp_out_enable(struct cygnus_aio_port *aio)
+{
+	u32 value;
+	int status = 0;
+
+	switch (aio->port_type) {
+	case PORT_TDM:
+		value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+		value |= BIT(I2S_OUT_STREAM_ENA);
+		writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+		writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+		value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+		value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+		value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+		writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+		aio->streams_on |= PLAYBACK_STREAM_MASK;
+		break;
+	case PORT_SPDIF:
+		value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+		value |= 0x3;
+		writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+
+		writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		break;
+	default:
+		dev_err(aio->cygaud->dev,
+			"Port not supported %d\n", aio->portnum);
+		status = -EINVAL;
+	}
+
+	return status;
+}
+
+static int audio_ssp_out_disable(struct cygnus_aio_port *aio)
+{
+	u32 value;
+	int status = 0;
+
+	switch (aio->port_type) {
+	case PORT_TDM:
+		aio->streams_on &= ~PLAYBACK_STREAM_MASK;
+
+		/* If both playback and capture are off */
+		if (!aio->streams_on) {
+			value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+			value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+			value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+			writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+		}
+
+		/* set group_sync_dis = 1 */
+		value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+		value |= BIT(aio->portnum);
+		writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+		writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+		/* set group_sync_dis = 0 */
+		value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+		value &= ~BIT(aio->portnum);
+		writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+		value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+		value &= ~BIT(I2S_OUT_STREAM_ENA);
+		writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+		/* IOP SW INIT on OUT_I2S_x */
+		value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+		value |= BIT(aio->portnum);
+		writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+		value &= ~BIT(aio->portnum);
+		writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+		break;
+	case PORT_SPDIF:
+		value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+		value &= ~0x3;
+		writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+		writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		break;
+	default:
+		dev_err(aio->cygaud->dev,
+			"Port not supported %d\n", aio->portnum);
+		status = -EINVAL;
+	}
+
+	return status;
+}
+
+static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk,
+	struct cygnus_aio_port *aio)
+{
+	int i = 0, error;
+	bool found = false;
+	const struct pll_macro_entry *p_entry;
+	struct clk *ch_clk;
+
+	for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) {
+		p_entry = &pll_predef_mclk[i];
+		if (p_entry->mclk == mclk) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		dev_err(cygaud->dev,
+			"%s No valid mclk freq (%u) found!\n", __func__, mclk);
+		return -EINVAL;
+	}
+
+	ch_clk = cygaud->audio_clk[p_entry->pll_ch_num];
+
+	if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) {
+		error = clk_prepare_enable(ch_clk);
+		if (error) {
+			dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+				__func__, error);
+			return error;
+		}
+		aio->clk_trace.cap_clk_en = true;
+	}
+
+	if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) {
+		error = clk_prepare_enable(ch_clk);
+		if (error) {
+			dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+				__func__, error);
+			return error;
+		}
+		aio->clk_trace.play_clk_en = true;
+	}
+
+	error = clk_set_rate(ch_clk, mclk);
+	if (error) {
+		dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n",
+			__func__, error);
+		return error;
+	}
+
+	return p_entry->pll_ch_num;
+}
+
+static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio,
+			struct cygnus_audio *cygaud)
+{
+	u32 value, i = 0;
+	u32 mask = 0xf;
+	u32 sclk;
+	bool found = false;
+	const struct _ssp_clk_coeff *p_entry = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(ssp_clk_coeff); i++) {
+		p_entry = &ssp_clk_coeff[i];
+		if ((p_entry->rate == aio->lrclk) &&
+		    (p_entry->sclk_rate == aio->bit_per_frame) &&
+		    (p_entry->mclk == aio->mclk)) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		dev_err(aio->cygaud->dev,
+			"No valid match found in ssp_clk_coeff array\n");
+		dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",
+			aio->lrclk, aio->bit_per_frame, aio->mclk);
+		return -EINVAL;
+	}
+
+	sclk = aio->bit_per_frame;
+	if (sclk == 512)
+		sclk = 0;
+	/* sclks_per_1fs_div = sclk cycles/32 */
+	sclk /= 32;
+	/* Set sclk rate */
+	switch (aio->port_type) {
+	case PORT_TDM:
+		/* Set number of bitclks per frame */
+		value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+		value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32);
+		value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32;
+		writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+		dev_dbg(aio->cygaud->dev,
+			"SCLKS_PER_1FS_DIV32 = 0x%x\n", value);
+		break;
+	case PORT_SPDIF:
+		break;
+	default:
+		dev_err(aio->cygaud->dev, "Unknown port type\n");
+		return -EINVAL;
+	}
+
+	/* Set MCLK_RATE ssp port (spdif and ssp are the same) */
+	value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+	value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT);
+	value |= (p_entry->mclk_rate << I2S_OUT_MCLKRATE_SHIFT);
+	writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+	dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value);
+	dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n",
+			aio->bit_per_frame, aio->mclk, aio->lrclk);
+	return 0;
+}
+
+static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+	struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+	int rate, bitres;
+	u32 value;
+	u32 mask = 0x1f;
+	int ret = 0;
+
+	dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum);
+	dev_dbg(aio->cygaud->dev, "params_channels %d\n",
+			params_channels(params));
+	dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params));
+	dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params));
+
+	rate = params_rate(params);
+
+	switch (aio->mode) {
+	case CYGNUS_SSPMODE_TDM:
+		if ((rate == 192000) && (params_channels(params) > 4)) {
+			dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n",
+				params_channels(params), rate);
+			return -EINVAL;
+		}
+		break;
+	case CYGNUS_SSPMODE_I2S:
+		aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */
+		break;
+	default:
+		dev_err(aio->cygaud->dev,
+			"%s port running in unknown mode\n", __func__);
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE);
+		/* Configure channels as mono or stereo/TDM */
+		if (params_channels(params) == 1)
+			value |= BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
+		else
+			value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S8:
+			if (aio->port_type == PORT_SPDIF) {
+				dev_err(aio->cygaud->dev,
+				"SPDIF does not support 8bit format\n");
+				return -EINVAL;
+			}
+			bitres = 8;
+			break;
+
+		case SNDRV_PCM_FORMAT_S16_LE:
+			bitres = 16;
+			break;
+
+		case SNDRV_PCM_FORMAT_S32_LE:
+			/* 32 bit mode is coded as 0 */
+			bitres = 0;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+
+		value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+		value &= ~(mask << BF_SRC_CFGX_BIT_RES);
+		value |= (bitres << BF_SRC_CFGX_BIT_RES);
+		writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+	} else {
+
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			value = readl(aio->cygaud->audio +
+					aio->regs.bf_destch_cfg);
+			value |= BIT(BF_DST_CFGX_CAP_MODE);
+			writel(value, aio->cygaud->audio +
+					aio->regs.bf_destch_cfg);
+			break;
+
+		case SNDRV_PCM_FORMAT_S32_LE:
+			value = readl(aio->cygaud->audio +
+					aio->regs.bf_destch_cfg);
+			value &= ~BIT(BF_DST_CFGX_CAP_MODE);
+			writel(value, aio->cygaud->audio +
+					aio->regs.bf_destch_cfg);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	aio->lrclk = rate;
+
+	if (!aio->is_slave)
+		ret = cygnus_ssp_set_clocks(aio, cygaud);
+
+	return ret;
+}
+
+/*
+ * This function sets the mclk frequency for pll clock
+ */
+static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai,
+			int clk_id, unsigned int freq, int dir)
+{
+	int sel;
+	u32 value;
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+	struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(aio->cygaud->dev,
+		"%s Enter port = %d\n", __func__, aio->portnum);
+	sel = pll_configure_mclk(cygaud, freq, aio);
+	if (sel < 0) {
+		dev_err(aio->cygaud->dev,
+			"%s Setting mclk failed.\n", __func__);
+		return -EINVAL;
+	}
+
+	aio->mclk = freq;
+
+	dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel);
+	value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+	value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT);
+	value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT);
+	writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+	return 0;
+}
+
+static int cygnus_ssp_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+	snd_soc_dai_set_dma_data(dai, substream, aio);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		aio->clk_trace.play_en = true;
+	else
+		aio->clk_trace.cap_en = true;
+
+	return 0;
+}
+
+static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		aio->clk_trace.play_en = false;
+	else
+		aio->clk_trace.cap_en = false;
+
+	if (!aio->is_slave) {
+		u32 val;
+
+		val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+		val &= CYGNUS_PLLCLKSEL_MASK;
+		if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+			dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+				val);
+			return;
+		}
+
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			if (aio->clk_trace.play_clk_en) {
+				clk_disable_unprepare(aio->cygaud->
+						audio_clk[val]);
+				aio->clk_trace.play_clk_en = false;
+			}
+		} else {
+			if (aio->clk_trace.cap_clk_en) {
+				clk_disable_unprepare(aio->cygaud->
+						audio_clk[val]);
+				aio->clk_trace.cap_clk_en = false;
+			}
+		}
+	}
+}
+
+/*
+ * Bit    Update  Notes
+ * 31     Yes     TDM Mode        (1 = TDM, 0 = i2s)
+ * 30     Yes     Slave Mode	  (1 = Slave, 0 = Master)
+ * 29:26  No      Sclks per frame
+ * 25:18  Yes     FS Width
+ * 17:14  No      Valid Slots
+ * 13     No      Bits		  (1 = 16 bits, 0 = 32 bits)
+ * 12:08  No     Bits per samp
+ * 07     Yes     Justifcation    (1 = LSB, 0 = MSB)
+ * 06     Yes     Alignment       (1 = Delay 1 clk, 0 = no delay
+ * 05     Yes     SCLK polarity   (1 = Rising, 0 = Falling)
+ * 04     Yes     LRCLK Polarity  (1 = High for left, 0 = Low for left)
+ * 03:02  Yes     Reserved - write as zero
+ * 01     No      Data Enable
+ * 00     No      CLK Enable
+ */
+#define I2S_OUT_CFG_REG_UPDATE_MASK   0x3C03FF03
+
+/* Input cfg is same as output, but the FS width is not a valid field */
+#define I2S_IN_CFG_REG_UPDATE_MASK  (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000)
+
+int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+	if ((len > 0) && (len < 256)) {
+		aio->fsync_width = len;
+		return 0;
+	} else {
+		return -EINVAL;
+	}
+}
+
+static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+	u32 ssp_curcfg;
+	u32 ssp_newcfg;
+	u32 ssp_outcfg;
+	u32 ssp_incfg;
+	u32 val;
+	u32 mask;
+
+	dev_dbg(aio->cygaud->dev, "%s Enter  fmt: %x\n", __func__, fmt);
+
+	if (aio->port_type == PORT_SPDIF)
+		return -EINVAL;
+
+	ssp_newcfg = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
+		aio->is_slave = 1;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
+		aio->is_slave = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+		ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+		aio->mode = CYGNUS_SSPMODE_I2S;
+		break;
+
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE);
+
+		/* DSP_A = data after FS, DSP_B = data during FS */
+		if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)
+			ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+
+		if ((aio->fsync_width > 0) && (aio->fsync_width < 256))
+			ssp_newcfg |=
+				(aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH);
+		else
+			ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+
+		aio->mode = CYGNUS_SSPMODE_TDM;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * SSP out cfg.
+	 * Retain bits we do not want to update, then OR in new bits
+	 */
+	ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+	ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+	writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+	/*
+	 * SSP in cfg.
+	 * Retain bits we do not want to update, then OR in new bits
+	 */
+	ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+	ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+	writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+	val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+	/*
+	 * Configure the word clk and bit clk as output or tristate
+	 * Each port has 4 bits for controlling its pins.
+	 * Shift the mask based upon port number.
+	 */
+	mask = BIT(AUD_MISC_SEROUT_LRCK_OE)
+			| BIT(AUD_MISC_SEROUT_SCLK_OE)
+			| BIT(AUD_MISC_SEROUT_MCLK_OE);
+	mask = mask << (aio->portnum * 4);
+	if (aio->is_slave)
+		/* Set bit for tri-state */
+		val |= mask;
+	else
+		/* Clear bit for drive */
+		val &= ~mask;
+
+	dev_dbg(aio->cygaud->dev, "%s  Set OE bits 0x%x\n", __func__, val);
+	writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+	return 0;
+}
+
+static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+			       struct snd_soc_dai *dai)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+	struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+	dev_dbg(aio->cygaud->dev,
+		"%s cmd %d at port = %d\n", __func__, cmd, aio->portnum);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			audio_ssp_out_enable(aio);
+		else
+			audio_ssp_in_enable(aio);
+		cygaud->active_ports++;
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			audio_ssp_out_disable(aio);
+		else
+			audio_ssp_in_disable(aio);
+		cygaud->active_ports--;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+	u32 value;
+	int bits_per_slot = 0;     /* default to 32-bits per slot */
+	int frame_bits;
+	unsigned int active_slots;
+	bool found = false;
+	int i;
+
+	if (tx_mask != rx_mask) {
+		dev_err(aio->cygaud->dev,
+			"%s tx_mask must equal rx_mask\n", __func__);
+		return -EINVAL;
+	}
+
+	active_slots = hweight32(tx_mask);
+
+	if ((active_slots < 0) || (active_slots > 16))
+		return -EINVAL;
+
+	/* Slot value must be even */
+	if (active_slots % 2)
+		return -EINVAL;
+
+	/* We encode 16 slots as 0 in the reg */
+	if (active_slots == 16)
+		active_slots = 0;
+
+	/* Slot Width is either 16 or 32 */
+	switch (slot_width) {
+	case 16:
+		bits_per_slot = 1;
+		break;
+	case 32:
+		bits_per_slot = 0;
+		break;
+	default:
+		bits_per_slot = 0;
+		dev_warn(aio->cygaud->dev,
+			"%s Defaulting Slot Width to 32\n", __func__);
+	}
+
+	frame_bits = slots * slot_width;
+
+	for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) {
+		if (ssp_valid_tdm_framesize[i] == frame_bits) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		dev_err(aio->cygaud->dev,
+			"%s In TDM mode, frame bits INVALID (%d)\n",
+			__func__, frame_bits);
+		return -EINVAL;
+	}
+
+	aio->bit_per_frame = frame_bits;
+
+	dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n",
+			__func__, active_slots, frame_bits);
+
+	/* Set capture side of ssp port */
+	value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+	value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+	value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+	value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+	value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+	writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+	/* Set playback side of ssp port */
+	value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+	value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+	value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+	value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+	value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+	writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+	if (!aio->is_slave) {
+		u32 val;
+
+		val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+		val &= CYGNUS_PLLCLKSEL_MASK;
+		if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+			dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+				val);
+			return -EINVAL;
+		}
+
+		if (aio->clk_trace.cap_clk_en)
+			clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+		if (aio->clk_trace.play_clk_en)
+			clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+
+		aio->pll_clk_num = val;
+	}
+
+	return 0;
+}
+
+static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+	struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+	int error;
+
+	if (!aio->is_slave) {
+		if (aio->clk_trace.cap_clk_en) {
+			error = clk_prepare_enable(aio->cygaud->
+					audio_clk[aio->pll_clk_num]);
+			if (error) {
+				dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+					__func__);
+				return -EINVAL;
+			}
+		}
+		if (aio->clk_trace.play_clk_en) {
+			error = clk_prepare_enable(aio->cygaud->
+					audio_clk[aio->pll_clk_num]);
+			if (error) {
+				if (aio->clk_trace.cap_clk_en)
+					clk_disable_unprepare(aio->cygaud->
+						audio_clk[aio->pll_clk_num]);
+				dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+					__func__);
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+#else
+#define cygnus_ssp_suspend NULL
+#define cygnus_ssp_resume  NULL
+#endif
+
+static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = {
+	.startup	= cygnus_ssp_startup,
+	.shutdown	= cygnus_ssp_shutdown,
+	.trigger	= cygnus_ssp_trigger,
+	.hw_params	= cygnus_ssp_hw_params,
+	.set_fmt	= cygnus_ssp_set_fmt,
+	.set_sysclk	= cygnus_ssp_set_sysclk,
+	.set_tdm_slot	= cygnus_set_dai_tdm_slot,
+};
+
+
+#define INIT_CPU_DAI(num) { \
+	.name = "cygnus-ssp" #num, \
+	.playback = { \
+		.channels_min = 1, \
+		.channels_max = 16, \
+		.rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \
+			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+			SNDRV_PCM_RATE_192000, \
+		.formats = SNDRV_PCM_FMTBIT_S8 | \
+				SNDRV_PCM_FMTBIT_S16_LE | \
+				SNDRV_PCM_FMTBIT_S32_LE, \
+	}, \
+	.capture = { \
+		.channels_min = 2, \
+		.channels_max = 16, \
+		.rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \
+			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+			SNDRV_PCM_RATE_192000, \
+		.formats =  SNDRV_PCM_FMTBIT_S16_LE | \
+					SNDRV_PCM_FMTBIT_S32_LE, \
+	}, \
+	.ops = &cygnus_ssp_dai_ops, \
+	.suspend = cygnus_ssp_suspend, \
+	.resume = cygnus_ssp_resume, \
+}
+
+static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
+	INIT_CPU_DAI(0),
+	INIT_CPU_DAI(1),
+	INIT_CPU_DAI(2),
+};
+
+static struct snd_soc_dai_driver cygnus_spdif_dai_info = {
+	.name = "cygnus-spdif",
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 |
+			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+			SNDRV_PCM_RATE_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S32_LE,
+	},
+	.ops = &cygnus_ssp_dai_ops,
+	.suspend = cygnus_ssp_suspend,
+	.resume = cygnus_ssp_resume,
+};
+
+static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];
+
+static const struct snd_soc_component_driver cygnus_ssp_component = {
+	.name		= "cygnus-audio",
+};
+
+/*
+ * Return < 0 if error
+ * Return 0 if disabled
+ * Return 1 if enabled and node is parsed successfully
+ */
+static int parse_ssp_child_node(struct platform_device *pdev,
+				struct device_node *dn,
+				struct cygnus_audio *cygaud,
+				struct snd_soc_dai_driver *p_dai)
+{
+	struct cygnus_aio_port *aio;
+	struct cygnus_ssp_regs ssp_regs[3];
+	u32 rawval;
+	int portnum = -1;
+	enum cygnus_audio_port_type port_type;
+
+	if (of_property_read_u32(dn, "reg", &rawval)) {
+		dev_err(&pdev->dev, "Missing reg property\n");
+		return -EINVAL;
+	}
+
+	portnum = rawval;
+	switch (rawval) {
+	case 0:
+		ssp_regs[0] = INIT_SSP_REGS(0);
+		port_type = PORT_TDM;
+		break;
+	case 1:
+		ssp_regs[1] = INIT_SSP_REGS(1);
+		port_type = PORT_TDM;
+		break;
+	case 2:
+		ssp_regs[2] = INIT_SSP_REGS(2);
+		port_type = PORT_TDM;
+		break;
+	case 3:
+		port_type = PORT_SPDIF;
+		break;
+	default:
+		dev_err(&pdev->dev, "Bad value for reg %u\n", rawval);
+		return -EINVAL;
+	}
+
+	aio = &cygaud->portinfo[portnum];
+	aio->cygaud = cygaud;
+	aio->portnum = portnum;
+	aio->port_type = port_type;
+	aio->fsync_width = -1;
+
+	switch (port_type) {
+	case PORT_TDM:
+		aio->regs = ssp_regs[portnum];
+		*p_dai = cygnus_ssp_dai_info[portnum];
+		aio->mode = CYGNUS_SSPMODE_UNKNOWN;
+		break;
+
+	case PORT_SPDIF:
+		aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET;
+		aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET;
+		aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET;
+		aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET;
+		*p_dai = cygnus_spdif_dai_info;
+
+		/* For the purposes of this code SPDIF can be I2S mode */
+		aio->mode = CYGNUS_SSPMODE_I2S;
+		break;
+	default:
+		dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type);
+		return -EINVAL;
+	}
+
+	dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum);
+	aio->streams_on = 0;
+	aio->cygaud->dev = &pdev->dev;
+	aio->clk_trace.play_en = false;
+	aio->clk_trace.cap_en = false;
+
+	audio_ssp_init_portregs(aio);
+	return 0;
+}
+
+static int audio_clk_init(struct platform_device *pdev,
+						struct cygnus_audio *cygaud)
+{
+	int i;
+	char clk_name[PROP_LEN_MAX];
+
+	for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) {
+		snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i);
+
+		cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name);
+		if (IS_ERR(cygaud->audio_clk[i]))
+			return PTR_ERR(cygaud->audio_clk[i]);
+	}
+
+	return 0;
+}
+
+static int cygnus_ssp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *child_node;
+	struct resource *res = pdev->resource;
+	struct cygnus_audio *cygaud;
+	int err = -EINVAL;
+	int node_count;
+	int active_port_count;
+
+	cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL);
+	if (!cygaud)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, cygaud);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud");
+	cygaud->audio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cygaud->audio))
+		return PTR_ERR(cygaud->audio);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "i2s_in");
+	cygaud->i2s_in = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cygaud->i2s_in))
+		return PTR_ERR(cygaud->i2s_in);
+
+	/* Tri-state all controlable pins until we know that we need them */
+	writel(CYGNUS_SSP_TRISTATE_MASK,
+			cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+	node_count = of_get_child_count(pdev->dev.of_node);
+	if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) {
+		dev_err(dev, "child nodes is %d.  Must be between 1 and %d\n",
+			node_count, CYGNUS_MAX_PORTS);
+		return -EINVAL;
+	}
+
+	active_port_count = 0;
+
+	for_each_available_child_of_node(pdev->dev.of_node, child_node) {
+		err = parse_ssp_child_node(pdev, child_node, cygaud,
+					&cygnus_ssp_dai[active_port_count]);
+
+		/* negative is err, 0 is active and good, 1 is disabled */
+		if (err < 0)
+			return err;
+		else if (!err) {
+			dev_dbg(dev, "Activating DAI: %s\n",
+				cygnus_ssp_dai[active_port_count].name);
+			active_port_count++;
+		}
+	}
+
+	cygaud->dev = dev;
+	cygaud->active_ports = 0;
+
+	dev_dbg(dev, "Registering %d DAIs\n", active_port_count);
+	err = snd_soc_register_component(dev, &cygnus_ssp_component,
+				cygnus_ssp_dai, active_port_count);
+	if (err) {
+		dev_err(dev, "snd_soc_register_dai failed\n");
+		return err;
+	}
+
+	cygaud->irq_num = platform_get_irq(pdev, 0);
+	if (cygaud->irq_num <= 0) {
+		dev_err(dev, "platform_get_irq failed\n");
+		err = cygaud->irq_num;
+		goto err_irq;
+	}
+
+	err = audio_clk_init(pdev, cygaud);
+	if (err) {
+		dev_err(dev, "audio clock initialization failed\n");
+		goto err_irq;
+	}
+
+	err = cygnus_soc_platform_register(dev, cygaud);
+	if (err) {
+		dev_err(dev, "platform reg error %d\n", err);
+		goto err_irq;
+	}
+
+	return 0;
+
+err_irq:
+	snd_soc_unregister_component(dev);
+	return err;
+}
+
+static int cygnus_ssp_remove(struct platform_device *pdev)
+{
+	cygnus_soc_platform_unregister(&pdev->dev);
+	snd_soc_unregister_component(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id cygnus_ssp_of_match[] = {
+	{ .compatible = "brcm,cygnus-audio" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);
+
+static struct platform_driver cygnus_ssp_driver = {
+	.probe		= cygnus_ssp_probe,
+	.remove		= cygnus_ssp_remove,
+	.driver		= {
+		.name	= "cygnus-ssp",
+		.of_match_table = cygnus_ssp_of_match,
+	},
+};
+
+module_platform_driver(cygnus_ssp_driver);
+
+MODULE_ALIAS("platform:cygnus-ssp");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC SSP Interface");

+ 139 - 0
sound/soc/bcm/cygnus-ssp.h

@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __CYGNUS_SSP_H__
+#define __CYGNUS_SSP_H__
+
+#define CYGNUS_TDM_DAI_MAX_SLOTS 16
+
+#define CYGNUS_MAX_PLAYBACK_PORTS 4
+#define CYGNUS_MAX_CAPTURE_PORTS 3
+#define CYGNUS_MAX_I2S_PORTS 3
+#define CYGNUS_MAX_PORTS  CYGNUS_MAX_PLAYBACK_PORTS
+#define CYGNUS_AUIDO_MAX_NUM_CLKS 3
+
+#define CYGNUS_SSP_FRAMEBITS_DIV 1
+
+#define CYGNUS_SSPMODE_I2S 0
+#define CYGNUS_SSPMODE_TDM 1
+#define CYGNUS_SSPMODE_UNKNOWN -1
+
+#define CYGNUS_SSP_CLKSRC_PLL      0
+
+/* Max string length of our dt property names */
+#define PROP_LEN_MAX 40
+
+struct ringbuf_regs {
+	unsigned rdaddr;
+	unsigned wraddr;
+	unsigned baseaddr;
+	unsigned endaddr;
+	unsigned fmark;   /* freemark for play, fullmark for caputure */
+	unsigned period_bytes;
+	unsigned buf_size;
+};
+
+#define RINGBUF_REG_PLAYBACK(num) ((struct ringbuf_regs) { \
+	.rdaddr = SRC_RBUF_ ##num## _RDADDR_OFFSET, \
+	.wraddr = SRC_RBUF_ ##num## _WRADDR_OFFSET, \
+	.baseaddr = SRC_RBUF_ ##num## _BASEADDR_OFFSET, \
+	.endaddr = SRC_RBUF_ ##num## _ENDADDR_OFFSET, \
+	.fmark = SRC_RBUF_ ##num## _FREE_MARK_OFFSET, \
+	.period_bytes = 0, \
+	.buf_size = 0, \
+})
+
+#define RINGBUF_REG_CAPTURE(num) ((struct ringbuf_regs)  { \
+	.rdaddr = DST_RBUF_ ##num## _RDADDR_OFFSET, \
+	.wraddr = DST_RBUF_ ##num## _WRADDR_OFFSET, \
+	.baseaddr = DST_RBUF_ ##num## _BASEADDR_OFFSET, \
+	.endaddr = DST_RBUF_ ##num## _ENDADDR_OFFSET, \
+	.fmark = DST_RBUF_ ##num## _FULL_MARK_OFFSET, \
+	.period_bytes = 0, \
+	.buf_size = 0, \
+})
+
+enum cygnus_audio_port_type {
+	PORT_TDM,
+	PORT_SPDIF,
+};
+
+struct cygnus_ssp_regs {
+	u32 i2s_stream_cfg;
+	u32 i2s_cfg;
+	u32 i2s_cap_stream_cfg;
+	u32 i2s_cap_cfg;
+	u32 i2s_mclk_cfg;
+
+	u32 bf_destch_ctrl;
+	u32 bf_destch_cfg;
+	u32 bf_sourcech_ctrl;
+	u32 bf_sourcech_cfg;
+	u32 bf_sourcech_grp;
+};
+
+struct cygnus_track_clk {
+	bool cap_en;
+	bool play_en;
+	bool cap_clk_en;
+	bool play_clk_en;
+};
+
+struct cygnus_aio_port {
+	int portnum;
+	int mode;
+	bool is_slave;
+	int streams_on;   /* will be 0 if both capture and play are off */
+	int fsync_width;
+	int port_type;
+
+	u32 mclk;
+	u32 lrclk;
+	u32 bit_per_frame;
+	u32 pll_clk_num;
+
+	struct cygnus_audio *cygaud;
+	struct cygnus_ssp_regs regs;
+
+	struct ringbuf_regs play_rb_regs;
+	struct ringbuf_regs capture_rb_regs;
+
+	struct snd_pcm_substream *play_stream;
+	struct snd_pcm_substream *capture_stream;
+
+	struct cygnus_track_clk clk_trace;
+};
+
+
+struct cygnus_audio {
+	struct cygnus_aio_port  portinfo[CYGNUS_MAX_PORTS];
+
+	int irq_num;
+	void __iomem *audio;
+	struct device *dev;
+	void __iomem *i2s_in;
+
+	struct clk *audio_clk[CYGNUS_AUIDO_MAX_NUM_CLKS];
+	int active_ports;
+	unsigned long vco_rate;
+};
+
+extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai);
+extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd);
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+						int len);
+extern int cygnus_soc_platform_register(struct device *dev,
+					struct cygnus_audio *cygaud);
+extern int cygnus_soc_platform_unregister(struct device *dev);
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+	int len);
+#endif

+ 36 - 2
sound/soc/codecs/Kconfig

@@ -32,6 +32,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_ADAU1977_SPI if SPI_MASTER
 	select SND_SOC_ADAU1977_I2C if I2C
 	select SND_SOC_ADAU1701 if I2C
+	select SND_SOC_ADAU7002
 	select SND_SOC_ADS117X
 	select SND_SOC_AK4104 if SPI_MASTER
 	select SND_SOC_AK4535 if I2C
@@ -46,6 +47,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_BT_SCO
 	select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
 	select SND_SOC_CS35L32 if I2C
+	select SND_SOC_CS35L33 if I2C
 	select SND_SOC_CS42L51_I2C if I2C
 	select SND_SOC_CS42L52 if I2C && INPUT
 	select SND_SOC_CS42L56 if I2C && INPUT
@@ -57,6 +59,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS42XX8_I2C if I2C
 	select SND_SOC_CS4349 if I2C
 	select SND_SOC_CS47L24 if MFD_CS47L24
+	select SND_SOC_CS53L30 if I2C
 	select SND_SOC_CX20442 if TTY
 	select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_DA7213 if I2C
@@ -84,6 +87,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_MAX98925 if I2C
 	select SND_SOC_MAX98926 if I2C
 	select SND_SOC_MAX9850 if I2C
+	select SND_SOC_MAX9860 if I2C
 	select SND_SOC_MAX9768 if I2C
 	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_MC13783 if MFD_MC13XXX
@@ -269,8 +273,12 @@ config SND_SOC_AD1980
 config SND_SOC_AD73311
 	tristate
 
+config SND_SOC_ADAU_UTILS
+	tristate
+
 config SND_SOC_ADAU1373
 	tristate
+	select SND_SOC_ADAU_UTILS
 
 config SND_SOC_ADAU1701
 	tristate "Analog Devices ADAU1701 CODEC"
@@ -280,6 +288,7 @@ config SND_SOC_ADAU1701
 config SND_SOC_ADAU17X1
 	tristate
 	select SND_SOC_SIGMADSP_REGMAP
+	select SND_SOC_ADAU_UTILS
 
 config SND_SOC_ADAU1761
 	tristate
@@ -322,6 +331,9 @@ config SND_SOC_ADAU1977_I2C
 	select SND_SOC_ADAU1977
 	select REGMAP_I2C
 
+config SND_SOC_ADAU7002
+	tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
+
 config SND_SOC_ADAV80X
 	tristate
 
@@ -371,7 +383,7 @@ config SND_SOC_ALC5632
 	tristate
 
 config SND_SOC_BT_SCO
-	tristate
+	tristate "Dummy BT SCO codec driver"
 
 config SND_SOC_CQ0093VC
 	tristate
@@ -380,6 +392,10 @@ config SND_SOC_CS35L32
 	tristate "Cirrus Logic CS35L32 CODEC"
 	depends on I2C
 
+config SND_SOC_CS35L33
+	tristate "Cirrus Logic CS35L33 CODEC"
+	depends on I2C
+
 config SND_SOC_CS42L51
 	tristate
 
@@ -450,6 +466,11 @@ config SND_SOC_CS4349
 config SND_SOC_CS47L24
 	tristate
 
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+	tristate "Cirrus Logic CS53L30 CODEC"
+	depends on I2C
+
 config SND_SOC_CX20442
 	tristate
 	depends on TTY
@@ -536,6 +557,10 @@ config SND_SOC_MAX98357A
 config SND_SOC_MAX98371
        tristate
 
+config SND_SOC_MAX98504
+	tristate "Maxim MAX98504 speaker amplifier"
+	depends on I2C
+
 config SND_SOC_MAX9867
 	tristate
 
@@ -548,6 +573,11 @@ config SND_SOC_MAX98926
 config SND_SOC_MAX9850
 	tristate
 
+config SND_SOC_MAX9860
+	tristate "Maxim MAX9860 Mono Audio Voice Codec"
+	depends on I2C
+	select REGMAP_I2C
+
 config SND_SOC_PCM1681
 	tristate "Texas Instruments PCM1681 CODEC"
 	depends on I2C
@@ -644,6 +674,9 @@ config SND_SOC_RT298
 config SND_SOC_RT5514
 	tristate
 
+config SND_SOC_RT5514_SPI
+	tristate
+
 config SND_SOC_RT5616
 	tristate "Realtek RT5616 CODEC"
 	depends on I2C
@@ -969,7 +1002,8 @@ config SND_SOC_WM8983
 	tristate
 
 config SND_SOC_WM8985
-	tristate
+	tristate "Wolfson Microelectronics WM8985 and WM8758 codec driver"
+	depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8988
 	tristate

+ 14 - 0
sound/soc/codecs/Makefile

@@ -7,6 +7,7 @@ snd-soc-ad193x-spi-objs := ad193x-spi.o
 snd-soc-ad193x-i2c-objs := ad193x-i2c.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
+snd-soc-adau-utils-objs := adau-utils.o
 snd-soc-adau1373-objs := adau1373.o
 snd-soc-adau1701-objs := adau1701.o
 snd-soc-adau17x1-objs := adau17x1.o
@@ -19,6 +20,7 @@ snd-soc-adau1781-spi-objs := adau1781-spi.o
 snd-soc-adau1977-objs := adau1977.o
 snd-soc-adau1977-spi-objs := adau1977-spi.o
 snd-soc-adau1977-i2c-objs := adau1977-i2c.o
+snd-soc-adau7002-objs := adau7002.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-adav801-objs := adav801.o
 snd-soc-adav803-objs := adav803.o
@@ -35,6 +37,7 @@ snd-soc-arizona-objs := arizona.o
 snd-soc-bt-sco-objs := bt-sco.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs35l32-objs := cs35l32.o
+snd-soc-cs35l33-objs := cs35l33.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
 snd-soc-cs42l52-objs := cs42l52.o
@@ -49,6 +52,7 @@ snd-soc-cs42xx8-objs := cs42xx8.o
 snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
 snd-soc-cs4349-objs := cs4349.o
 snd-soc-cs47l24-objs := cs47l24.o
+snd-soc-cs53l30-objs := cs53l30.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-da7213-objs := da7213.o
@@ -79,6 +83,7 @@ snd-soc-max9867-objs := max9867.o
 snd-soc-max98925-objs := max98925.o
 snd-soc-max98926-objs := max98926.o
 snd-soc-max9850-objs := max9850.o
+snd-soc-max9860-objs := max9860.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
 snd-soc-nau8825-objs := nau8825.o
@@ -100,6 +105,7 @@ snd-soc-rl6347a-objs := rl6347a.o
 snd-soc-rt286-objs := rt286.o
 snd-soc-rt298-objs := rt298.o
 snd-soc-rt5514-objs := rt5514.o
+snd-soc-rt5514-spi-objs := rt5514-spi.o
 snd-soc-rt5616-objs := rt5616.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5640-objs := rt5640.o
@@ -208,6 +214,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
 
 # Amp
 snd-soc-max9877-objs := max9877.o
+snd-soc-max98504-objs := max98504.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 snd-soc-tas2552-objs := tas2552.o
 
@@ -220,6 +227,7 @@ obj-$(CONFIG_SND_SOC_AD193X_SPI)	+= snd-soc-ad193x-spi.o
 obj-$(CONFIG_SND_SOC_AD193X_I2C)	+= snd-soc-ad193x-i2c.o
 obj-$(CONFIG_SND_SOC_AD1980)	+= snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU_UTILS)	+= snd-soc-adau-utils.o
 obj-$(CONFIG_SND_SOC_ADAU1373)	+= snd-soc-adau1373.o
 obj-$(CONFIG_SND_SOC_ADAU1701)		+= snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAU17X1)		+= snd-soc-adau17x1.o
@@ -232,6 +240,7 @@ obj-$(CONFIG_SND_SOC_ADAU1781_SPI)	+= snd-soc-adau1781-spi.o
 obj-$(CONFIG_SND_SOC_ADAU1977)		+= snd-soc-adau1977.o
 obj-$(CONFIG_SND_SOC_ADAU1977_SPI)	+= snd-soc-adau1977-spi.o
 obj-$(CONFIG_SND_SOC_ADAU1977_I2C)	+= snd-soc-adau1977-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7002)	+= snd-soc-adau7002.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADAV801)  += snd-soc-adav801.o
 obj-$(CONFIG_SND_SOC_ADAV803)  += snd-soc-adav803.o
@@ -250,6 +259,7 @@ obj-$(CONFIG_SND_SOC_ARIZONA)	+= snd-soc-arizona.o
 obj-$(CONFIG_SND_SOC_BT_SCO)	+= snd-soc-bt-sco.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS35L32)	+= snd-soc-cs35l32.o
+obj-$(CONFIG_SND_SOC_CS35L33)	+= snd-soc-cs35l33.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)	+= snd-soc-cs42l51-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L52)	+= snd-soc-cs42l52.o
@@ -264,6 +274,7 @@ obj-$(CONFIG_SND_SOC_CS42XX8)	+= snd-soc-cs42xx8.o
 obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
 obj-$(CONFIG_SND_SOC_CS4349)	+= snd-soc-cs4349.o
 obj-$(CONFIG_SND_SOC_CS47L24)	+= snd-soc-cs47l24.o
+obj-$(CONFIG_SND_SOC_CS53L30)	+= snd-soc-cs53l30.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DA7213)	+= snd-soc-da7213.o
@@ -293,6 +304,7 @@ obj-$(CONFIG_SND_SOC_MAX9867)	+= snd-soc-max9867.o
 obj-$(CONFIG_SND_SOC_MAX98925)	+= snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX98926)	+= snd-soc-max98926.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
+obj-$(CONFIG_SND_SOC_MAX9860)	+= snd-soc-max9860.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
@@ -314,6 +326,7 @@ obj-$(CONFIG_SND_SOC_RL6347A)	+= snd-soc-rl6347a.o
 obj-$(CONFIG_SND_SOC_RT286)	+= snd-soc-rt286.o
 obj-$(CONFIG_SND_SOC_RT298)	+= snd-soc-rt298.o
 obj-$(CONFIG_SND_SOC_RT5514)	+= snd-soc-rt5514.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI)	+= snd-soc-rt5514-spi.o
 obj-$(CONFIG_SND_SOC_RT5616)	+= snd-soc-rt5616.o
 obj-$(CONFIG_SND_SOC_RT5631)	+= snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5640)	+= snd-soc-rt5640.o
@@ -419,4 +432,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS)	+= snd-soc-wm-hubs.o
 
 # Amp
 obj-$(CONFIG_SND_SOC_MAX9877)	+= snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_MAX98504)	+= snd-soc-max98504.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)	+= snd-soc-tpa6130a2.o

+ 61 - 0
sound/soc/codecs/adau-utils.c

@@ -0,0 +1,61 @@
+/*
+ * Shared helper functions for devices from the ADAU family
+ *
+ * Copyright 2011-2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "adau-utils.h"
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+	uint8_t regs[5])
+{
+	unsigned int r, n, m, i, j;
+	unsigned int div;
+
+	if (!freq_out) {
+		r = 0;
+		n = 0;
+		m = 0;
+		div = 0;
+	} else {
+		if (freq_out % freq_in != 0) {
+			div = DIV_ROUND_UP(freq_in, 13500000);
+			freq_in /= div;
+			r = freq_out / freq_in;
+			i = freq_out % freq_in;
+			j = gcd(i, freq_in);
+			n = i / j;
+			m = freq_in / j;
+			div--;
+		} else {
+			r = freq_out / freq_in;
+			n = 0;
+			m = 0;
+			div = 0;
+		}
+		if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
+			return -EINVAL;
+	}
+
+	regs[0] = m >> 8;
+	regs[1] = m & 0xff;
+	regs[2] = n >> 8;
+	regs[3] = n & 0xff;
+	regs[4] = (r << 3) | (div << 1);
+	if (m != 0)
+		regs[4] |= 1; /* Fractional mode */
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(adau_calc_pll_cfg);
+
+MODULE_DESCRIPTION("ASoC ADAU audio CODECs shared helper functions");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");

+ 7 - 0
sound/soc/codecs/adau-utils.h

@@ -0,0 +1,7 @@
+#ifndef SOUND_SOC_CODECS_ADAU_PLL_H
+#define SOUND_SOC_CODECS_ADAU_PLL_H
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+	uint8_t regs[5]);
+
+#endif

+ 10 - 28
sound/soc/codecs/adau1373.c

@@ -23,6 +23,7 @@
 #include <sound/adau1373.h>
 
 #include "adau1373.h"
+#include "adau-utils.h"
 
 struct adau1373_dai {
 	unsigned int clk_src;
@@ -1254,7 +1255,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
 {
 	struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
 	unsigned int dpll_div = 0;
-	unsigned int x, r, n, m, i, j, mode;
+	uint8_t pll_regs[5];
+	int ret;
 
 	switch (pll_id) {
 	case ADAU1373_PLL1:
@@ -1295,27 +1297,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
 		dpll_div++;
 	}
 
-	if (freq_out % freq_in != 0) {
-		/* fout = fin * (r + (n/m)) / x */
-		x = DIV_ROUND_UP(freq_in, 13500000);
-		freq_in /= x;
-		r = freq_out / freq_in;
-		i = freq_out % freq_in;
-		j = gcd(i, freq_in);
-		n = i / j;
-		m = freq_in / j;
-		x--;
-		mode = 1;
-	} else {
-		/* fout = fin / r */
-		r = freq_out / freq_in;
-		n = 0;
-		m = 0;
-		x = 0;
-		mode = 0;
-	}
-
-	if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+	ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs);
+	if (ret)
 		return -EINVAL;
 
 	if (dpll_div) {
@@ -1330,12 +1313,11 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
 
 	regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
 		(source << 4) | dpll_div);
-	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
-	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
-	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
-	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
-	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id),
-		(r << 3) | (x << 1) | mode);
+	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), pll_regs[0]);
+	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), pll_regs[1]);
+	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), pll_regs[2]);
+	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), pll_regs[3]);
+	regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), pll_regs[4]);
 
 	/* Set sysclk to pll_rate / 4 */
 	regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);

+ 1 - 1
sound/soc/codecs/adau1761-i2c.c

@@ -31,7 +31,7 @@ static int adau1761_i2c_probe(struct i2c_client *client,
 
 static int adau1761_i2c_remove(struct i2c_client *client)
 {
-	snd_soc_unregister_codec(&client->dev);
+	adau17x1_remove(&client->dev);
 	return 0;
 }
 

+ 1 - 1
sound/soc/codecs/adau1761-spi.c

@@ -48,7 +48,7 @@ static int adau1761_spi_probe(struct spi_device *spi)
 
 static int adau1761_spi_remove(struct spi_device *spi)
 {
-	snd_soc_unregister_codec(&spi->dev);
+	adau17x1_remove(&spi->dev);
 	return 0;
 }
 

+ 1 - 1
sound/soc/codecs/adau1781-i2c.c

@@ -31,7 +31,7 @@ static int adau1781_i2c_probe(struct i2c_client *client,
 
 static int adau1781_i2c_remove(struct i2c_client *client)
 {
-	snd_soc_unregister_codec(&client->dev);
+	adau17x1_remove(&client->dev);
 	return 0;
 }
 

+ 1 - 1
sound/soc/codecs/adau1781-spi.c

@@ -48,7 +48,7 @@ static int adau1781_spi_probe(struct spi_device *spi)
 
 static int adau1781_spi_remove(struct spi_device *spi)
 {
-	snd_soc_unregister_codec(&spi->dev);
+	adau17x1_remove(&spi->dev);
 	return 0;
 }
 

+ 162 - 89
sound/soc/codecs/adau17x1.c

@@ -9,6 +9,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -23,6 +24,7 @@
 
 #include "sigmadsp.h"
 #include "adau17x1.h"
+#include "adau-utils.h"
 
 static const char * const adau17x1_capture_mixer_boost_text[] = {
 	"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
@@ -302,6 +304,116 @@ bool adau17x1_has_dsp(struct adau *adau)
 }
 EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
 
+static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
+	int source, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	if (freq_in < 8000000 || freq_in > 27000000)
+		return -EINVAL;
+
+	ret = adau_calc_pll_cfg(freq_in, freq_out, adau->pll_regs);
+	if (ret < 0)
+		return ret;
+
+	/* The PLL register is 6 bytes long and can only be written at once. */
+	ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+			adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+	if (ret)
+		return ret;
+
+	adau->pll_freq = freq_out;
+
+	return 0;
+}
+
+static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
+	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+	bool is_pll;
+	bool was_pll;
+
+	switch (clk_id) {
+	case ADAU17X1_CLK_SRC_MCLK:
+		is_pll = false;
+		break;
+	case ADAU17X1_CLK_SRC_PLL_AUTO:
+		if (!adau->mclk)
+			return -EINVAL;
+		/* Fall-through */
+	case ADAU17X1_CLK_SRC_PLL:
+		is_pll = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (adau->clk_src) {
+	case ADAU17X1_CLK_SRC_MCLK:
+		was_pll = false;
+		break;
+	case ADAU17X1_CLK_SRC_PLL:
+	case ADAU17X1_CLK_SRC_PLL_AUTO:
+		was_pll = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	adau->sysclk = freq;
+
+	if (is_pll != was_pll) {
+		if (is_pll) {
+			snd_soc_dapm_add_routes(dapm,
+				&adau17x1_dapm_pll_route, 1);
+		} else {
+			snd_soc_dapm_del_routes(dapm,
+				&adau17x1_dapm_pll_route, 1);
+		}
+	}
+
+	adau->clk_src = clk_id;
+
+	return 0;
+}
+
+static int adau17x1_auto_pll(struct snd_soc_dai *dai,
+	struct snd_pcm_hw_params *params)
+{
+	struct adau *adau = snd_soc_dai_get_drvdata(dai);
+	unsigned int pll_rate;
+
+	switch (params_rate(params)) {
+	case 48000:
+	case 8000:
+	case 12000:
+	case 16000:
+	case 24000:
+	case 32000:
+	case 96000:
+		pll_rate = 48000 * 1024;
+		break;
+	case 44100:
+	case 7350:
+	case 11025:
+	case 14700:
+	case 22050:
+	case 29400:
+	case 88200:
+		pll_rate = 44100 * 1024;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return adau17x1_set_dai_pll(dai, ADAU17X1_PLL, ADAU17X1_PLL_SRC_MCLK,
+		clk_get_rate(adau->mclk), pll_rate);
+}
+
 static int adau17x1_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -311,10 +423,19 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
 	unsigned int freq;
 	int ret;
 
-	if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+	switch (adau->clk_src) {
+	case ADAU17X1_CLK_SRC_PLL_AUTO:
+		ret = adau17x1_auto_pll(dai, params);
+		if (ret)
+			return ret;
+		/* Fall-through */
+	case ADAU17X1_CLK_SRC_PLL:
 		freq = adau->pll_freq;
-	else
+		break;
+	default:
 		freq = adau->sysclk;
+		break;
+	}
 
 	if (freq % params_rate(params) != 0)
 		return -EINVAL;
@@ -386,93 +507,6 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
 			ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
 }
 
-static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
-	int source, unsigned int freq_in, unsigned int freq_out)
-{
-	struct snd_soc_codec *codec = dai->codec;
-	struct adau *adau = snd_soc_codec_get_drvdata(codec);
-	unsigned int r, n, m, i, j;
-	unsigned int div;
-	int ret;
-
-	if (freq_in < 8000000 || freq_in > 27000000)
-		return -EINVAL;
-
-	if (!freq_out) {
-		r = 0;
-		n = 0;
-		m = 0;
-		div = 0;
-	} else {
-		if (freq_out % freq_in != 0) {
-			div = DIV_ROUND_UP(freq_in, 13500000);
-			freq_in /= div;
-			r = freq_out / freq_in;
-			i = freq_out % freq_in;
-			j = gcd(i, freq_in);
-			n = i / j;
-			m = freq_in / j;
-			div--;
-		} else {
-			r = freq_out / freq_in;
-			n = 0;
-			m = 0;
-			div = 0;
-		}
-		if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
-			return -EINVAL;
-	}
-
-	adau->pll_regs[0] = m >> 8;
-	adau->pll_regs[1] = m & 0xff;
-	adau->pll_regs[2] = n >> 8;
-	adau->pll_regs[3] = n & 0xff;
-	adau->pll_regs[4] = (r << 3) | (div << 1);
-	if (m != 0)
-		adau->pll_regs[4] |= 1; /* Fractional mode */
-
-	/* The PLL register is 6 bytes long and can only be written at once. */
-	ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
-			adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
-	if (ret)
-		return ret;
-
-	adau->pll_freq = freq_out;
-
-	return 0;
-}
-
-static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
-		int clk_id, unsigned int freq, int dir)
-{
-	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
-	struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
-
-	switch (clk_id) {
-	case ADAU17X1_CLK_SRC_MCLK:
-	case ADAU17X1_CLK_SRC_PLL:
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	adau->sysclk = freq;
-
-	if (adau->clk_src != clk_id) {
-		if (clk_id == ADAU17X1_CLK_SRC_PLL) {
-			snd_soc_dapm_add_routes(dapm,
-				&adau17x1_dapm_pll_route, 1);
-		} else {
-			snd_soc_dapm_del_routes(dapm,
-				&adau17x1_dapm_pll_route, 1);
-		}
-	}
-
-	adau->clk_src = clk_id;
-
-	return 0;
-}
-
 static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
 		unsigned int fmt)
 {
@@ -857,6 +891,10 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
 		ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
 			ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
 	}
+
+	if (adau->clk_src != ADAU17X1_CLK_SRC_MCLK)
+		snd_soc_dapm_add_routes(dapm, &adau17x1_dapm_pll_route, 1);
+
 	return ret;
 }
 EXPORT_SYMBOL_GPL(adau17x1_add_routes);
@@ -879,6 +917,7 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
 	const char *firmware_name)
 {
 	struct adau *adau;
+	int ret;
 
 	if (IS_ERR(regmap))
 		return PTR_ERR(regmap);
@@ -887,6 +926,30 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
 	if (!adau)
 		return -ENOMEM;
 
+	adau->mclk = devm_clk_get(dev, "mclk");
+	if (IS_ERR(adau->mclk)) {
+		if (PTR_ERR(adau->mclk) != -ENOENT)
+			return PTR_ERR(adau->mclk);
+		/* Clock is optional (for the driver) */
+		adau->mclk = NULL;
+	} else if (adau->mclk) {
+		adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO;
+
+		/*
+		 * Any valid PLL output rate will work at this point, use one
+		 * that is likely to be chosen later as well. The register will
+		 * be written when the PLL is powered up for the first time.
+		 */
+		ret = adau_calc_pll_cfg(clk_get_rate(adau->mclk), 48000 * 1024,
+				adau->pll_regs);
+		if (ret < 0)
+			return ret;
+
+		ret = clk_prepare_enable(adau->mclk);
+		if (ret)
+			return ret;
+	}
+
 	adau->regmap = regmap;
 	adau->switch_mode = switch_mode;
 	adau->type = type;
@@ -910,6 +973,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
 }
 EXPORT_SYMBOL_GPL(adau17x1_probe);
 
+void adau17x1_remove(struct device *dev)
+{
+	struct adau *adau = dev_get_drvdata(dev);
+
+	snd_soc_unregister_codec(dev);
+	if (adau->mclk)
+		clk_disable_unprepare(adau->mclk);
+}
+EXPORT_SYMBOL_GPL(adau17x1_remove);
+
 MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 MODULE_LICENSE("GPL");

+ 6 - 0
sound/soc/codecs/adau17x1.h

@@ -22,13 +22,18 @@ enum adau17x1_pll_src {
 };
 
 enum adau17x1_clk_src {
+	/* Automatically configure PLL based on the sample rate */
+	ADAU17X1_CLK_SRC_PLL_AUTO,
 	ADAU17X1_CLK_SRC_MCLK,
 	ADAU17X1_CLK_SRC_PLL,
 };
 
+struct clk;
+
 struct adau {
 	unsigned int sysclk;
 	unsigned int pll_freq;
+	struct clk *mclk;
 
 	enum adau17x1_clk_src clk_src;
 	enum adau17x1_type type;
@@ -52,6 +57,7 @@ int adau17x1_add_routes(struct snd_soc_codec *codec);
 int adau17x1_probe(struct device *dev, struct regmap *regmap,
 	enum adau17x1_type type, void (*switch_mode)(struct device *dev),
 	const char *firmware_name);
+void adau17x1_remove(struct device *dev);
 int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
 	enum adau17x1_micbias_voltage micbias);
 bool adau17x1_readable_register(struct device *dev, unsigned int reg);

+ 80 - 0
sound/soc/codecs/adau7002.c

@@ -0,0 +1,80 @@
+/*
+ * ADAU7002 Stereo PDM-to-I2S/TDM converter driver
+ *
+ * Copyright 2014-2016 Analog Devices
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget adau7002_widgets[] = {
+	SND_SOC_DAPM_INPUT("PDM_DAT"),
+	SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7002_routes[] = {
+	{ "Capture", NULL, "PDM_DAT" },
+	{ "Capture", NULL, "IOVDD" },
+};
+
+static struct snd_soc_dai_driver adau7002_dai = {
+	.name = "adau7002-hifi",
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |
+			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
+			SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+		.sig_bits = 20,
+	},
+};
+
+static const struct snd_soc_codec_driver adau7002_codec_driver = {
+	.dapm_widgets = adau7002_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
+	.dapm_routes = adau7002_routes,
+	.num_dapm_routes = ARRAY_SIZE(adau7002_routes),
+};
+
+static int adau7002_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_codec(&pdev->dev, &adau7002_codec_driver,
+			&adau7002_dai, 1);
+}
+
+static int adau7002_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau7002_dt_ids[] = {
+	{ .compatible = "adi,adau7002", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adau7002_dt_ids);
+#endif
+
+static struct platform_driver adau7002_driver = {
+	.driver = {
+		.name = "adau7002",
+		.of_match_table	= of_match_ptr(adau7002_dt_ids),
+	},
+	.probe = adau7002_probe,
+	.remove = adau7002_remove,
+};
+module_platform_driver(adau7002_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADAU7002 Stereo PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL v2");

+ 11 - 1
sound/soc/codecs/ak4613.c

@@ -437,15 +437,25 @@ static struct snd_soc_dai_driver ak4613_dai = {
 	.symmetric_rates = 1,
 };
 
-static int ak4613_resume(struct snd_soc_codec *codec)
+static int ak4613_suspend(struct snd_soc_codec *codec)
 {
 	struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
 
+	regcache_cache_only(regmap, true);
 	regcache_mark_dirty(regmap);
+	return 0;
+}
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+	struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+	regcache_cache_only(regmap, false);
 	return regcache_sync(regmap);
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+	.suspend		= ak4613_suspend,
 	.resume			= ak4613_resume,
 	.set_bias_level		= ak4613_set_bias_level,
 	.controls		= ak4613_snd_controls,

+ 11 - 2
sound/soc/codecs/ak4642.c

@@ -523,15 +523,23 @@ static struct snd_soc_dai_driver ak4642_dai = {
 	.symmetric_rates = 1,
 };
 
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_codec *codec)
 {
 	struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
 
+	regcache_cache_only(regmap, true);
 	regcache_mark_dirty(regmap);
-	regcache_sync(regmap);
 	return 0;
 }
 
+static int ak4642_resume(struct snd_soc_codec *codec)
+{
+	struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+	regcache_cache_only(regmap, false);
+	regcache_sync(regmap);
+	return 0;
+}
 static int ak4642_probe(struct snd_soc_codec *codec)
 {
 	struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -544,6 +552,7 @@ static int ak4642_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
 	.probe			= ak4642_probe,
+	.suspend		= ak4642_suspend,
 	.resume			= ak4642_resume,
 	.set_bias_level		= ak4642_set_bias_level,
 	.controls		= ak4642_snd_controls,

+ 46 - 45
sound/soc/codecs/arizona.c

@@ -85,30 +85,9 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
 {
 	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 	struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
-	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
-	bool manual_ena = false;
 	int val;
 
-	switch (arizona->type) {
-	case WM5102:
-		switch (arizona->rev) {
-		case 0:
-			break;
-		default:
-			manual_ena = true;
-			break;
-		}
-	default:
-		break;
-	}
-
 	switch (event) {
-	case SND_SOC_DAPM_PRE_PMU:
-		if (!priv->spk_ena && manual_ena) {
-			regmap_write_async(arizona->regmap, 0x4f5, 0x25a);
-			priv->spk_ena_pending = true;
-		}
-		break;
 	case SND_SOC_DAPM_POST_PMU:
 		val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
 		if (val & ARIZONA_SPK_OVERHEAT_STS) {
@@ -120,33 +99,12 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
 		regmap_update_bits_async(arizona->regmap,
 					 ARIZONA_OUTPUT_ENABLES_1,
 					 1 << w->shift, 1 << w->shift);
-
-		if (priv->spk_ena_pending) {
-			msleep(75);
-			regmap_write_async(arizona->regmap, 0x4f5, 0xda);
-			priv->spk_ena_pending = false;
-			priv->spk_ena++;
-		}
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
-		if (manual_ena) {
-			priv->spk_ena--;
-			if (!priv->spk_ena)
-				regmap_write_async(arizona->regmap,
-						   0x4f5, 0x25a);
-		}
-
 		regmap_update_bits_async(arizona->regmap,
 					 ARIZONA_OUTPUT_ENABLES_1,
 					 1 << w->shift, 0);
 		break;
-	case SND_SOC_DAPM_POST_PMD:
-		if (manual_ena) {
-			if (!priv->spk_ena)
-				regmap_write_async(arizona->regmap,
-						   0x4f5, 0x0da);
-		}
-		break;
 	default:
 		break;
 	}
@@ -324,6 +282,17 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
 }
 EXPORT_SYMBOL_GPL(arizona_init_gpio);
 
+int arizona_init_notifiers(struct snd_soc_codec *codec)
+{
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona *arizona = priv->arizona;
+
+	BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_notifiers);
+
 const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
 	"None",
 	"Tone Generator 1",
@@ -619,7 +588,7 @@ const struct soc_enum arizona_asrc_rate1 =
 			      arizona_rate_text, arizona_rate_val);
 EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
 
-static const char *arizona_vol_ramp_text[] = {
+static const char * const arizona_vol_ramp_text[] = {
 	"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
 	"15ms/6dB", "30ms/6dB",
 };
@@ -648,7 +617,7 @@ SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
 		     arizona_vol_ramp_text);
 EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
 
-static const char *arizona_lhpf_mode_text[] = {
+static const char * const arizona_lhpf_mode_text[] = {
 	"Low-pass", "High-pass"
 };
 
@@ -676,7 +645,7 @@ SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
 		     arizona_lhpf_mode_text);
 EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
 
-static const char *arizona_ng_hold_text[] = {
+static const char * const arizona_ng_hold_text[] = {
 	"30ms", "120ms", "250ms", "500ms",
 };
 
@@ -810,6 +779,14 @@ const struct soc_enum arizona_output_anc_src[] = {
 };
 EXPORT_SYMBOL_GPL(arizona_output_anc_src);
 
+const struct snd_kcontrol_new arizona_voice_trigger_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 1, 1, 0),
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 2, 1, 0),
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 3, 1, 0),
+};
+EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch);
+
 static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
 {
 	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -2573,6 +2550,30 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
 
+int arizona_register_notifier(struct snd_soc_codec *codec,
+			      struct notifier_block *nb,
+			      int (*notify)(struct notifier_block *nb,
+					    unsigned long action, void *data))
+{
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona *arizona = priv->arizona;
+
+	nb->notifier_call = notify;
+
+	return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_register_notifier);
+
+int arizona_unregister_notifier(struct snd_soc_codec *codec,
+				struct notifier_block *nb)
+{
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona *arizona = priv->arizona;
+
+	return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
+
 MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");

+ 19 - 3
sound/soc/codecs/arizona.h

@@ -63,6 +63,9 @@
 #define ARIZONA_DVFS_SR1_RQ	0x001
 #define ARIZONA_DVFS_ADSP1_RQ	0x100
 
+/* Notifier events */
+#define ARIZONA_NOTIFY_VOICE_TRIGGER   0x1
+
 struct arizona;
 struct wm_adsp;
 
@@ -87,14 +90,15 @@ struct arizona_priv {
 	unsigned int out_down_pending;
 	unsigned int out_down_delay;
 
-	unsigned int spk_ena:2;
-	unsigned int spk_ena_pending:1;
-
 	unsigned int dvfs_reqs;
 	struct mutex dvfs_lock;
 	bool dvfs_cached;
 };
 
+struct arizona_voice_trigger_info {
+	int core;
+};
+
 #define ARIZONA_NUM_MIXER_INPUTS 104
 
 extern const unsigned int arizona_mixer_tlv[];
@@ -248,6 +252,8 @@ extern const struct soc_enum arizona_anc_input_src[];
 extern const struct soc_enum arizona_anc_ng_enum;
 extern const struct soc_enum arizona_output_anc_src[];
 
+extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
+
 extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
 			 struct snd_kcontrol *kcontrol,
 			 int event);
@@ -306,6 +312,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
 extern int arizona_init_spk(struct snd_soc_codec *codec);
 extern int arizona_init_gpio(struct snd_soc_codec *codec);
 extern int arizona_init_mono(struct snd_soc_codec *codec);
+extern int arizona_init_notifiers(struct snd_soc_codec *codec);
 
 extern int arizona_free_spk(struct snd_soc_codec *codec);
 
@@ -317,4 +324,13 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
 extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
 
 extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+extern int arizona_register_notifier(struct snd_soc_codec *codec,
+				     struct notifier_block *nb,
+				     int (*notify)(struct notifier_block *nb,
+						   unsigned long action,
+						   void *data));
+extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
+				       struct notifier_block *nb);
+
 #endif

+ 36 - 16
sound/soc/codecs/bt-sco.c

@@ -25,22 +25,41 @@ static const struct snd_soc_dapm_route bt_sco_routes[] = {
 	{ "TX", NULL, "Playback" },
 };
 
-static struct snd_soc_dai_driver bt_sco_dai = {
-	.name = "bt-sco-pcm",
-	.playback = {
-		.stream_name = "Playback",
-		.channels_min = 1,
-		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
-	},
-	.capture = {
-		 .stream_name = "Capture",
-		.channels_min = 1,
-		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+static struct snd_soc_dai_driver bt_sco_dai[] = {
+	{
+		.name = "bt-sco-pcm",
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = SNDRV_PCM_RATE_8000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			 .stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = SNDRV_PCM_RATE_8000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
 	},
+	{
+		.name = "bt-sco-pcm-wb",
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			 .stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	}
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
@@ -53,7 +72,7 @@ static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
 static int bt_sco_probe(struct platform_device *pdev)
 {
 	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
-			&bt_sco_dai, 1);
+				      bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
 }
 
 static int bt_sco_remove(struct platform_device *pdev)
@@ -77,6 +96,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
 #if defined(CONFIG_OF)
 static const struct of_device_id bt_sco_codec_of_match[] = {
 	{ .compatible = "delta,dfbmcs320", },
+	{ .compatible = "linux,bt-sco", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);

+ 1303 - 0
sound/soc/codecs/cs35l33.c

@@ -0,0 +1,1303 @@
+/*
+ * cs35l33.c -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <sound/cs35l33.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include "cs35l33.h"
+
+#define CS35L33_BOOT_DELAY	50
+
+struct cs35l33_private {
+	struct snd_soc_codec *codec;
+	struct cs35l33_pdata pdata;
+	struct regmap *regmap;
+	struct gpio_desc *reset_gpio;
+	bool amp_cal;
+	int mclk_int;
+	struct regulator_bulk_data core_supplies[2];
+	int num_core_supplies;
+	bool is_tdm_mode;
+	bool enable_soft_ramp;
+};
+
+static const struct reg_default cs35l33_reg[] = {
+	{CS35L33_PWRCTL1, 0x85},
+	{CS35L33_PWRCTL2, 0xFE},
+	{CS35L33_CLK_CTL, 0x0C},
+	{CS35L33_BST_PEAK_CTL, 0x90},
+	{CS35L33_PROTECT_CTL, 0x55},
+	{CS35L33_BST_CTL1, 0x00},
+	{CS35L33_BST_CTL2, 0x01},
+	{CS35L33_ADSP_CTL, 0x00},
+	{CS35L33_ADC_CTL, 0xC8},
+	{CS35L33_DAC_CTL, 0x14},
+	{CS35L33_DIG_VOL_CTL, 0x00},
+	{CS35L33_CLASSD_CTL, 0x04},
+	{CS35L33_AMP_CTL, 0x90},
+	{CS35L33_INT_MASK_1, 0xFF},
+	{CS35L33_INT_MASK_2, 0xFF},
+	{CS35L33_DIAG_LOCK, 0x00},
+	{CS35L33_DIAG_CTRL_1, 0x40},
+	{CS35L33_DIAG_CTRL_2, 0x00},
+	{CS35L33_HG_MEMLDO_CTL, 0x62},
+	{CS35L33_HG_REL_RATE, 0x03},
+	{CS35L33_LDO_DEL, 0x12},
+	{CS35L33_HG_HEAD, 0x0A},
+	{CS35L33_HG_EN, 0x05},
+	{CS35L33_TX_VMON, 0x00},
+	{CS35L33_TX_IMON, 0x03},
+	{CS35L33_TX_VPMON, 0x02},
+	{CS35L33_TX_VBSTMON, 0x05},
+	{CS35L33_TX_FLAG, 0x06},
+	{CS35L33_TX_EN1, 0x00},
+	{CS35L33_TX_EN2, 0x00},
+	{CS35L33_TX_EN3, 0x00},
+	{CS35L33_TX_EN4, 0x00},
+	{CS35L33_RX_AUD, 0x40},
+	{CS35L33_RX_SPLY, 0x03},
+	{CS35L33_RX_ALIVE, 0x04},
+	{CS35L33_BST_CTL4, 0x63},
+};
+
+static const struct reg_sequence cs35l33_patch[] = {
+	{ 0x00,  0x99, 0 },
+	{ 0x59,  0x02, 0 },
+	{ 0x52,  0x30, 0 },
+	{ 0x39,  0x45, 0 },
+	{ 0x57,  0x30, 0 },
+	{ 0x2C,  0x68, 0 },
+	{ 0x00,  0x00, 0 },
+};
+
+static bool cs35l33_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L33_DEVID_AB:
+	case CS35L33_DEVID_CD:
+	case CS35L33_DEVID_E:
+	case CS35L33_REV_ID:
+	case CS35L33_INT_STATUS_1:
+	case CS35L33_INT_STATUS_2:
+	case CS35L33_HG_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs35l33_writeable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	/* these are read only registers */
+	case CS35L33_DEVID_AB:
+	case CS35L33_DEVID_CD:
+	case CS35L33_DEVID_E:
+	case CS35L33_REV_ID:
+	case CS35L33_INT_STATUS_1:
+	case CS35L33_INT_STATUS_2:
+	case CS35L33_HG_STATUS:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static bool cs35l33_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L33_DEVID_AB:
+	case CS35L33_DEVID_CD:
+	case CS35L33_DEVID_E:
+	case CS35L33_REV_ID:
+	case CS35L33_PWRCTL1:
+	case CS35L33_PWRCTL2:
+	case CS35L33_CLK_CTL:
+	case CS35L33_BST_PEAK_CTL:
+	case CS35L33_PROTECT_CTL:
+	case CS35L33_BST_CTL1:
+	case CS35L33_BST_CTL2:
+	case CS35L33_ADSP_CTL:
+	case CS35L33_ADC_CTL:
+	case CS35L33_DAC_CTL:
+	case CS35L33_DIG_VOL_CTL:
+	case CS35L33_CLASSD_CTL:
+	case CS35L33_AMP_CTL:
+	case CS35L33_INT_MASK_1:
+	case CS35L33_INT_MASK_2:
+	case CS35L33_INT_STATUS_1:
+	case CS35L33_INT_STATUS_2:
+	case CS35L33_DIAG_LOCK:
+	case CS35L33_DIAG_CTRL_1:
+	case CS35L33_DIAG_CTRL_2:
+	case CS35L33_HG_MEMLDO_CTL:
+	case CS35L33_HG_REL_RATE:
+	case CS35L33_LDO_DEL:
+	case CS35L33_HG_HEAD:
+	case CS35L33_HG_EN:
+	case CS35L33_TX_VMON:
+	case CS35L33_TX_IMON:
+	case CS35L33_TX_VPMON:
+	case CS35L33_TX_VBSTMON:
+	case CS35L33_TX_FLAG:
+	case CS35L33_TX_EN1:
+	case CS35L33_TX_EN2:
+	case CS35L33_TX_EN3:
+	case CS35L33_TX_EN4:
+	case CS35L33_RX_AUD:
+	case CS35L33_RX_SPLY:
+	case CS35L33_RX_ALIVE:
+	case CS35L33_BST_CTL4:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 100, 0);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l33_snd_controls[] = {
+
+	SOC_SINGLE_TLV("SPK Amp Volume", CS35L33_AMP_CTL,
+		       4, 0x09, 0, classd_ctl_tlv),
+	SOC_SINGLE_SX_TLV("DAC Volume", CS35L33_DIG_VOL_CTL,
+			0, 0x34, 0xE4, dac_tlv),
+};
+
+static int cs35l33_spkrdrv_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 cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if (!priv->amp_cal) {
+			usleep_range(8000, 9000);
+			priv->amp_cal = true;
+			regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+				    CS35L33_AMP_CAL, 0);
+			dev_dbg(codec->dev, "Amp calibration done\n");
+		}
+		dev_dbg(codec->dev, "Amp turned on\n");
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		dev_dbg(codec->dev, "Amp turned off\n");
+		break;
+	default:
+		dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+		break;
+	}
+
+	return 0;
+}
+
+static int cs35l33_sdin_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 cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned int val;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+				    CS35L33_PDN_BST, 0);
+		val = priv->is_tdm_mode ? 0 : CS35L33_PDN_TDM;
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+				    CS35L33_PDN_TDM, val);
+		dev_dbg(codec->dev, "BST turned on\n");
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		dev_dbg(codec->dev, "SDIN turned on\n");
+		if (!priv->amp_cal) {
+			regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+				    CS35L33_AMP_CAL, CS35L33_AMP_CAL);
+			dev_dbg(codec->dev, "Amp calibration started\n");
+			usleep_range(10000, 11000);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+				    CS35L33_PDN_TDM, CS35L33_PDN_TDM);
+		usleep_range(4000, 4100);
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+				    CS35L33_PDN_BST, CS35L33_PDN_BST);
+		dev_dbg(codec->dev, "BST and SDIN turned off\n");
+		break;
+	default:
+		dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+
+	}
+
+	return 0;
+}
+
+static int cs35l33_sdout_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 cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned int mask = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+	unsigned int mask2 = CS35L33_SDOUT_3ST_TDM;
+	unsigned int val, val2;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (priv->is_tdm_mode) {
+			/* set sdout_3st_i2s and reset pdn_tdm */
+			val = CS35L33_SDOUT_3ST_I2S;
+			/* reset sdout_3st_tdm */
+			val2 = 0;
+		} else {
+			/* reset sdout_3st_i2s and set pdn_tdm */
+			val = CS35L33_PDN_TDM;
+			/* set sdout_3st_tdm */
+			val2 = CS35L33_SDOUT_3ST_TDM;
+		}
+		dev_dbg(codec->dev, "SDOUT turned on\n");
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		val = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+		val2 = CS35L33_SDOUT_3ST_TDM;
+		dev_dbg(codec->dev, "SDOUT turned off\n");
+		break;
+	default:
+		dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+		return 0;
+	}
+
+	regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+		mask, val);
+	regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+		mask2, val2);
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l33_dapm_widgets[] = {
+
+	SND_SOC_DAPM_OUTPUT("SPK"),
+	SND_SOC_DAPM_OUT_DRV_E("SPKDRV", CS35L33_PWRCTL1, 7, 1, NULL, 0,
+		cs35l33_spkrdrv_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L33_PWRCTL2,
+		2, 1, cs35l33_sdin_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_INPUT("MON"),
+
+	SND_SOC_DAPM_ADC("VMON", NULL,
+		CS35L33_PWRCTL2, CS35L33_PDN_VMON_SHIFT, 1),
+	SND_SOC_DAPM_ADC("IMON", NULL,
+		CS35L33_PWRCTL2, CS35L33_PDN_IMON_SHIFT, 1),
+	SND_SOC_DAPM_ADC("VPMON", NULL,
+		CS35L33_PWRCTL2, CS35L33_PDN_VPMON_SHIFT, 1),
+	SND_SOC_DAPM_ADC("VBSTMON", NULL,
+		CS35L33_PWRCTL2, CS35L33_PDN_VBSTMON_SHIFT, 1),
+
+	SND_SOC_DAPM_AIF_OUT_E("SDOUT", NULL, 0, SND_SOC_NOPM, 0, 0,
+		cs35l33_sdout_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l33_audio_map[] = {
+	{"SDIN", NULL, "CS35L33 Playback"},
+	{"SPKDRV", NULL, "SDIN"},
+	{"SPK", NULL, "SPKDRV"},
+
+	{"VMON", NULL, "MON"},
+	{"IMON", NULL, "MON"},
+
+	{"SDOUT", NULL, "VMON"},
+	{"SDOUT", NULL, "IMON"},
+	{"CS35L33 Capture", NULL, "SDOUT"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vphg_auto_route[] = {
+	{"SPKDRV", NULL, "VPMON"},
+	{"VPMON", NULL, "CS35L33 Playback"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vp_vbst_mon_route[] = {
+	{"SDOUT", NULL, "VPMON"},
+	{"VPMON", NULL, "MON"},
+	{"SDOUT", NULL, "VBSTMON"},
+	{"VBSTMON", NULL, "MON"},
+};
+
+static int cs35l33_set_bias_level(struct snd_soc_codec *codec,
+				  enum snd_soc_bias_level level)
+{
+	unsigned int val;
+	struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+				    CS35L33_PDN_ALL, 0);
+		regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+				    CS35L33_MCLKDIS, 0);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+				    CS35L33_PDN_ALL, CS35L33_PDN_ALL);
+		regmap_read(priv->regmap, CS35L33_INT_STATUS_2, &val);
+		usleep_range(1000, 1100);
+		if (val & CS35L33_PDN_DONE)
+			regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+					    CS35L33_MCLKDIS, CS35L33_MCLKDIS);
+		break;
+	case SND_SOC_BIAS_OFF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct cs35l33_mclk_div {
+	int mclk;
+	int srate;
+	u8 adsp_rate;
+	u8 int_fs_ratio;
+};
+
+static const struct cs35l33_mclk_div cs35l33_mclk_coeffs[] = {
+	/* MCLK, Sample Rate, adsp_rate, int_fs_ratio */
+	{5644800, 11025, 0x4, CS35L33_INT_FS_RATE},
+	{5644800, 22050, 0x8, CS35L33_INT_FS_RATE},
+	{5644800, 44100, 0xC, CS35L33_INT_FS_RATE},
+
+	{6000000,  8000, 0x1, 0},
+	{6000000, 11025, 0x2, 0},
+	{6000000, 11029, 0x3, 0},
+	{6000000, 12000, 0x4, 0},
+	{6000000, 16000, 0x5, 0},
+	{6000000, 22050, 0x6, 0},
+	{6000000, 22059, 0x7, 0},
+	{6000000, 24000, 0x8, 0},
+	{6000000, 32000, 0x9, 0},
+	{6000000, 44100, 0xA, 0},
+	{6000000, 44118, 0xB, 0},
+	{6000000, 48000, 0xC, 0},
+
+	{6144000,  8000, 0x1, CS35L33_INT_FS_RATE},
+	{6144000, 12000, 0x4, CS35L33_INT_FS_RATE},
+	{6144000, 16000, 0x5, CS35L33_INT_FS_RATE},
+	{6144000, 24000, 0x8, CS35L33_INT_FS_RATE},
+	{6144000, 32000, 0x9, CS35L33_INT_FS_RATE},
+	{6144000, 48000, 0xC, CS35L33_INT_FS_RATE},
+};
+
+static int cs35l33_get_mclk_coeff(int mclk, int srate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs35l33_mclk_coeffs); i++) {
+		if (cs35l33_mclk_coeffs[i].mclk == mclk &&
+			cs35l33_mclk_coeffs[i].srate == srate)
+			return i;
+	}
+	return -EINVAL;
+}
+
+static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+			CS35L33_MS_MASK, CS35L33_MS_MASK);
+		dev_dbg(codec->dev, "Audio port in master mode\n");
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+			CS35L33_MS_MASK, 0);
+		dev_dbg(codec->dev, "Audio port in slave mode\n");
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		/*
+		 * tdm mode in cs35l33 resembles dsp-a mode very
+		 * closely, it is dsp-a with fsync shifted left by half bclk
+		 */
+		priv->is_tdm_mode = true;
+		dev_dbg(codec->dev, "Audio port in TDM mode\n");
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		priv->is_tdm_mode = false;
+		dev_dbg(codec->dev, "Audio port in I2S mode\n");
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cs35l33_pcm_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 cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+	int sample_size = params_width(params);
+	int coeff = cs35l33_get_mclk_coeff(priv->mclk_int, params_rate(params));
+
+	if (coeff < 0)
+		return coeff;
+
+	regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+		CS35L33_ADSP_FS | CS35L33_INT_FS_RATE,
+		cs35l33_mclk_coeffs[coeff].int_fs_ratio
+		| cs35l33_mclk_coeffs[coeff].adsp_rate);
+
+	if (priv->is_tdm_mode) {
+		sample_size = (sample_size / 8) - 1;
+		if (sample_size > 2)
+			sample_size = 2;
+		regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+			CS35L33_AUDIN_RX_DEPTH,
+			sample_size << CS35L33_AUDIN_RX_DEPTH_SHIFT);
+	}
+
+	dev_dbg(codec->dev, "sample rate=%d, bits per sample=%d\n",
+		params_rate(params), params_width(params));
+
+	return 0;
+}
+
+static const unsigned int cs35l33_src_rates[] = {
+	8000, 11025, 11029, 12000, 16000, 22050,
+	22059, 24000, 32000, 44100, 44118, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l33_constraints = {
+	.count  = ARRAY_SIZE(cs35l33_src_rates),
+	.list   = cs35l33_src_rates,
+};
+
+static int cs35l33_pcm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+					SNDRV_PCM_HW_PARAM_RATE,
+					&cs35l33_constraints);
+	return 0;
+}
+
+static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	if (tristate) {
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+			CS35L33_SDOUT_3ST_I2S, CS35L33_SDOUT_3ST_I2S);
+		regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+			CS35L33_SDOUT_3ST_TDM, CS35L33_SDOUT_3ST_TDM);
+	} else {
+		regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+			CS35L33_SDOUT_3ST_I2S, 0);
+		regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+			CS35L33_SDOUT_3ST_TDM, 0);
+	}
+
+	return 0;
+}
+
+static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+				unsigned int rx_mask, int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+	unsigned int reg, bit_pos, i;
+	int slot, slot_num;
+
+	if (slot_width != 8)
+		return -EINVAL;
+
+	/* scan rx_mask for aud slot */
+	slot = ffs(rx_mask) - 1;
+	if (slot >= 0) {
+		regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+			CS35L33_X_LOC, slot);
+		dev_dbg(codec->dev, "Audio starts from slots %d", slot);
+	}
+
+	/*
+	 * scan tx_mask: vmon(2 slots); imon (2 slots);
+	 * vpmon (1 slot) vbstmon (1 slot)
+	 */
+	slot = ffs(tx_mask) - 1;
+	slot_num = 0;
+
+	for (i = 0; i < 2 ; i++) {
+		/* disable vpmon/vbstmon: enable later if set in tx_mask */
+		regmap_update_bits(priv->regmap, CS35L33_TX_VPMON + i,
+			CS35L33_X_STATE | CS35L33_X_LOC, CS35L33_X_STATE
+			| CS35L33_X_LOC);
+	}
+
+	/* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+	snd_soc_dapm_del_routes(dapm, cs35l33_vp_vbst_mon_route,
+		ARRAY_SIZE(cs35l33_vp_vbst_mon_route));
+
+	while (slot >= 0) {
+		/* configure VMON_TX_LOC */
+		if (slot_num == 0) {
+			regmap_update_bits(priv->regmap, CS35L33_TX_VMON,
+				CS35L33_X_STATE | CS35L33_X_LOC, slot);
+			dev_dbg(codec->dev, "VMON enabled in slots %d-%d",
+				slot, slot + 1);
+		}
+
+		/* configure IMON_TX_LOC */
+		if (slot_num == 3) {
+			regmap_update_bits(priv->regmap, CS35L33_TX_IMON,
+				CS35L33_X_STATE | CS35L33_X_LOC, slot);
+			dev_dbg(codec->dev, "IMON enabled in slots %d-%d",
+				slot, slot + 1);
+		}
+
+		/* configure VPMON_TX_LOC */
+		if (slot_num == 4) {
+			regmap_update_bits(priv->regmap, CS35L33_TX_VPMON,
+				CS35L33_X_STATE | CS35L33_X_LOC, slot);
+			snd_soc_dapm_add_routes(dapm,
+				&cs35l33_vp_vbst_mon_route[0], 2);
+			dev_dbg(codec->dev, "VPMON enabled in slots %d", slot);
+		}
+
+		/* configure VBSTMON_TX_LOC */
+		if (slot_num == 5) {
+			regmap_update_bits(priv->regmap, CS35L33_TX_VBSTMON,
+				CS35L33_X_STATE | CS35L33_X_LOC, slot);
+			snd_soc_dapm_add_routes(dapm,
+				&cs35l33_vp_vbst_mon_route[2], 2);
+			dev_dbg(codec->dev,
+				"VBSTMON enabled in slots %d", slot);
+		}
+
+		/* Enable the relevant tx slot */
+		reg = CS35L33_TX_EN4 - (slot/8);
+		bit_pos = slot - ((slot / 8) * (8));
+		regmap_update_bits(priv->regmap, reg,
+			1 << bit_pos, 1 << bit_pos);
+
+		tx_mask &= ~(1 << slot);
+		slot = ffs(tx_mask) - 1;
+		slot_num++;
+	}
+
+	return 0;
+}
+
+static int cs35l33_codec_set_sysclk(struct snd_soc_codec *codec,
+		int clk_id, int source, unsigned int freq, int dir)
+{
+	struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+	switch (freq) {
+	case CS35L33_MCLK_5644:
+	case CS35L33_MCLK_6:
+	case CS35L33_MCLK_6144:
+		regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+			CS35L33_MCLKDIV2, 0);
+		cs35l33->mclk_int = freq;
+		break;
+	case CS35L33_MCLK_11289:
+	case CS35L33_MCLK_12:
+	case CS35L33_MCLK_12288:
+		regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+			CS35L33_MCLKDIV2, CS35L33_MCLKDIV2);
+		cs35l33->mclk_int = freq/2;
+		break;
+	default:
+		cs35l33->mclk_int = 0;
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev, "external mclk freq=%d, internal mclk freq=%d\n",
+		freq, cs35l33->mclk_int);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l33_ops = {
+	.startup = cs35l33_pcm_startup,
+	.set_tristate = cs35l33_set_tristate,
+	.set_fmt = cs35l33_set_dai_fmt,
+	.hw_params = cs35l33_pcm_hw_params,
+	.set_tdm_slot = cs35l33_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l33_dai = {
+		.name = "cs35l33-dai",
+		.id = 0,
+		.playback = {
+			.stream_name = "CS35L33 Playback",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = CS35L33_RATES,
+			.formats = CS35L33_FORMATS,
+		},
+		.capture = {
+			.stream_name = "CS35L33 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = CS35L33_RATES,
+			.formats = CS35L33_FORMATS,
+		},
+		.ops = &cs35l33_ops,
+		.symmetric_rates = 1,
+};
+
+static int cs35l33_set_hg_data(struct snd_soc_codec *codec,
+			       struct cs35l33_pdata *pdata)
+{
+	struct cs35l33_hg *hg_config = &pdata->hg_config;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+	if (hg_config->enable_hg_algo) {
+		regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+			CS35L33_MEM_DEPTH_MASK,
+			hg_config->mem_depth << CS35L33_MEM_DEPTH_SHIFT);
+		regmap_write(priv->regmap, CS35L33_HG_REL_RATE,
+			hg_config->release_rate);
+		regmap_update_bits(priv->regmap, CS35L33_HG_HEAD,
+			CS35L33_HD_RM_MASK,
+			hg_config->hd_rm << CS35L33_HD_RM_SHIFT);
+		regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+			CS35L33_LDO_THLD_MASK,
+			hg_config->ldo_thld << CS35L33_LDO_THLD_SHIFT);
+		regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+			CS35L33_LDO_DISABLE_MASK,
+			hg_config->ldo_path_disable <<
+				CS35L33_LDO_DISABLE_SHIFT);
+		regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+			CS35L33_LDO_ENTRY_DELAY_MASK,
+			hg_config->ldo_entry_delay <<
+				CS35L33_LDO_ENTRY_DELAY_SHIFT);
+		if (hg_config->vp_hg_auto) {
+			regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+				CS35L33_VP_HG_AUTO_MASK,
+				CS35L33_VP_HG_AUTO_MASK);
+			snd_soc_dapm_add_routes(dapm, cs35l33_vphg_auto_route,
+				ARRAY_SIZE(cs35l33_vphg_auto_route));
+		}
+		regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+			CS35L33_VP_HG_MASK,
+			hg_config->vp_hg << CS35L33_VP_HG_SHIFT);
+		regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+			CS35L33_VP_HG_RATE_MASK,
+			hg_config->vp_hg_rate << CS35L33_VP_HG_RATE_SHIFT);
+		regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+			CS35L33_VP_HG_VA_MASK,
+			hg_config->vp_hg_va << CS35L33_VP_HG_VA_SHIFT);
+		regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+			CS35L33_CLASS_HG_EN_MASK, CS35L33_CLASS_HG_EN_MASK);
+	}
+	return 0;
+}
+
+static int cs35l33_set_bst_ipk(struct snd_soc_codec *codec, unsigned int bst)
+{
+	struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0, steps = 0;
+
+	/* Boost current in uA */
+	if (bst > 3600000 || bst < 1850000) {
+		dev_err(codec->dev, "Invalid boost current %d\n", bst);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (bst % 15625) {
+		dev_err(codec->dev, "Current not a multiple of 15625uA (%d)\n",
+			bst);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	while (bst > 1850000) {
+		bst -= 15625;
+		steps++;
+	}
+
+	regmap_write(cs35l33->regmap, CS35L33_BST_PEAK_CTL,
+		steps+0x70);
+
+err:
+	return ret;
+}
+
+static int cs35l33_probe(struct snd_soc_codec *codec)
+{
+	struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+	cs35l33->codec = codec;
+	pm_runtime_get_sync(codec->dev);
+
+	regmap_update_bits(cs35l33->regmap, CS35L33_PROTECT_CTL,
+		CS35L33_ALIVE_WD_DIS, 0x8);
+	regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL2,
+				CS35L33_ALIVE_WD_DIS2,
+				CS35L33_ALIVE_WD_DIS2);
+
+	/* Set Platform Data */
+	regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL1,
+		CS35L33_BST_CTL_MASK, cs35l33->pdata.boost_ctl);
+	regmap_update_bits(cs35l33->regmap, CS35L33_CLASSD_CTL,
+		CS35L33_AMP_DRV_SEL_MASK,
+		cs35l33->pdata.amp_drv_sel << CS35L33_AMP_DRV_SEL_SHIFT);
+
+	if (cs35l33->pdata.boost_ipk)
+		cs35l33_set_bst_ipk(codec, cs35l33->pdata.boost_ipk);
+
+	if (cs35l33->enable_soft_ramp) {
+		snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+			CS35L33_DIGSFT, CS35L33_DIGSFT);
+		snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+			CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate);
+	} else {
+		snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+			CS35L33_DIGSFT, 0);
+	}
+
+	/* update IMON scaling rate if different from default of 0x8 */
+	if (cs35l33->pdata.imon_adc_scale != 0x8)
+		snd_soc_update_bits(codec, CS35L33_ADC_CTL,
+			CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale);
+
+	cs35l33_set_hg_data(codec, &(cs35l33->pdata));
+
+	/*
+	 * unmask important interrupts that causes the chip to enter
+	 * speaker safe mode and hence deserves user attention
+	 */
+	regmap_update_bits(cs35l33->regmap, CS35L33_INT_MASK_1,
+		CS35L33_M_OTE | CS35L33_M_OTW | CS35L33_M_AMP_SHORT |
+		CS35L33_M_CAL_ERR, 0);
+
+	pm_runtime_put_sync(codec->dev);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l33 = {
+	.probe = cs35l33_probe,
+
+	.set_bias_level = cs35l33_set_bias_level,
+	.set_sysclk = cs35l33_codec_set_sysclk,
+
+	.dapm_widgets = cs35l33_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets),
+	.dapm_routes = cs35l33_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map),
+	.controls = cs35l33_snd_controls,
+	.num_controls = ARRAY_SIZE(cs35l33_snd_controls),
+
+	.idle_bias_off = true,
+};
+
+static const struct regmap_config cs35l33_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CS35L33_MAX_REGISTER,
+	.reg_defaults = cs35l33_reg,
+	.num_reg_defaults = ARRAY_SIZE(cs35l33_reg),
+	.volatile_reg = cs35l33_volatile_register,
+	.readable_reg = cs35l33_readable_register,
+	.writeable_reg = cs35l33_writeable_register,
+	.cache_type = REGCACHE_RBTREE,
+	.use_single_rw = true,
+};
+
+static int __maybe_unused cs35l33_runtime_resume(struct device *dev)
+{
+	struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (cs35l33->reset_gpio)
+		gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+	ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+		cs35l33->core_supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(cs35l33->regmap, false);
+
+	if (cs35l33->reset_gpio)
+		gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+	msleep(CS35L33_BOOT_DELAY);
+
+	ret = regcache_sync(cs35l33->regmap);
+	if (ret != 0) {
+		dev_err(dev, "Failed to restore register cache\n");
+		goto err;
+	}
+
+	return 0;
+
+err:
+	regcache_cache_only(cs35l33->regmap, true);
+	regulator_bulk_disable(cs35l33->num_core_supplies,
+		cs35l33->core_supplies);
+
+	return ret;
+}
+
+static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
+{
+	struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	/* redo the calibration in next power up */
+	cs35l33->amp_cal = false;
+
+	regcache_cache_only(cs35l33->regmap, true);
+	regcache_mark_dirty(cs35l33->regmap);
+	regulator_bulk_disable(cs35l33->num_core_supplies,
+		cs35l33->core_supplies);
+
+	return 0;
+}
+
+static const struct dev_pm_ops cs35l33_pm_ops = {
+	SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend,
+			   cs35l33_runtime_resume,
+			   NULL)
+};
+
+static int cs35l33_get_hg_data(const struct device_node *np,
+			       struct cs35l33_pdata *pdata)
+{
+	struct device_node *hg;
+	struct cs35l33_hg *hg_config = &pdata->hg_config;
+	u32 val32;
+
+	hg = of_get_child_by_name(np, "cirrus,hg-algo");
+	hg_config->enable_hg_algo = hg ? true : false;
+
+	if (hg_config->enable_hg_algo) {
+		if (of_property_read_u32(hg, "cirrus,mem-depth", &val32) >= 0)
+			hg_config->mem_depth = val32;
+		if (of_property_read_u32(hg, "cirrus,release-rate",
+				&val32) >= 0)
+			hg_config->release_rate = val32;
+		if (of_property_read_u32(hg, "cirrus,ldo-thld", &val32) >= 0)
+			hg_config->ldo_thld = val32;
+		if (of_property_read_u32(hg, "cirrus,ldo-path-disable",
+				&val32) >= 0)
+			hg_config->ldo_path_disable = val32;
+		if (of_property_read_u32(hg, "cirrus,ldo-entry-delay",
+				&val32) >= 0)
+			hg_config->ldo_entry_delay = val32;
+
+		hg_config->vp_hg_auto = of_property_read_bool(hg,
+			"cirrus,vp-hg-auto");
+
+		if (of_property_read_u32(hg, "cirrus,vp-hg", &val32) >= 0)
+			hg_config->vp_hg = val32;
+		if (of_property_read_u32(hg, "cirrus,vp-hg-rate", &val32) >= 0)
+			hg_config->vp_hg_rate = val32;
+		if (of_property_read_u32(hg, "cirrus,vp-hg-va", &val32) >= 0)
+			hg_config->vp_hg_va = val32;
+	}
+
+	of_node_put(hg);
+
+	return 0;
+}
+
+static irqreturn_t cs35l33_irq_thread(int irq, void *data)
+{
+	struct cs35l33_private *cs35l33 = data;
+	struct snd_soc_codec *codec = cs35l33->codec;
+	unsigned int sticky_val1, sticky_val2, current_val, mask1, mask2;
+
+	regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_2,
+		&sticky_val2);
+	regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+		&sticky_val1);
+	regmap_read(cs35l33->regmap, CS35L33_INT_MASK_2, &mask2);
+	regmap_read(cs35l33->regmap, CS35L33_INT_MASK_1, &mask1);
+
+	/* Check to see if the unmasked bits are active,
+	 *  if not then exit.
+	 */
+	if (!(sticky_val1 & ~mask1) && !(sticky_val2 & ~mask2))
+		return IRQ_NONE;
+
+	regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+		&current_val);
+
+	/* handle the interrupts */
+
+	if (sticky_val1 & CS35L33_AMP_SHORT) {
+		dev_crit(codec->dev, "Amp short error\n");
+		if (!(current_val & CS35L33_AMP_SHORT)) {
+			dev_dbg(codec->dev,
+				"Amp short error release\n");
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL,
+				CS35L33_AMP_SHORT_RLS, 0);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL,
+				CS35L33_AMP_SHORT_RLS,
+				CS35L33_AMP_SHORT_RLS);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_AMP_SHORT_RLS,
+				0);
+		}
+	}
+
+	if (sticky_val1 & CS35L33_CAL_ERR) {
+		dev_err(codec->dev, "Cal error\n");
+
+		/* redo the calibration in next power up */
+		cs35l33->amp_cal = false;
+
+		if (!(current_val & CS35L33_CAL_ERR)) {
+			dev_dbg(codec->dev, "Cal error release\n");
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+				0);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+				CS35L33_CAL_ERR_RLS);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+				0);
+		}
+	}
+
+	if (sticky_val1 & CS35L33_OTE) {
+		dev_crit(codec->dev, "Over temperature error\n");
+		if (!(current_val & CS35L33_OTE)) {
+			dev_dbg(codec->dev,
+				"Over temperature error release\n");
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_OTE_RLS,
+				CS35L33_OTE_RLS);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+		}
+	}
+
+	if (sticky_val1 & CS35L33_OTW) {
+		dev_err(codec->dev, "Over temperature warning\n");
+		if (!(current_val & CS35L33_OTW)) {
+			dev_dbg(codec->dev,
+				"Over temperature warning release\n");
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_OTW_RLS,
+				CS35L33_OTW_RLS);
+			regmap_update_bits(cs35l33->regmap,
+				CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+		}
+	}
+	if (CS35L33_ALIVE_ERR & sticky_val1)
+		dev_err(codec->dev, "ERROR: ADSPCLK Interrupt\n");
+
+	if (CS35L33_MCLK_ERR & sticky_val1)
+		dev_err(codec->dev, "ERROR: MCLK Interrupt\n");
+
+	if (CS35L33_VMON_OVFL & sticky_val2)
+		dev_err(codec->dev,
+			"ERROR: VMON Overflow Interrupt\n");
+
+	if (CS35L33_IMON_OVFL & sticky_val2)
+		dev_err(codec->dev,
+			"ERROR: IMON Overflow Interrupt\n");
+
+	if (CS35L33_VPMON_OVFL & sticky_val2)
+		dev_err(codec->dev,
+			"ERROR: VPMON Overflow Interrupt\n");
+
+	return IRQ_HANDLED;
+}
+
+static const char * const cs35l33_core_supplies[] = {
+	"VA",
+	"VP",
+};
+
+static int cs35l33_of_get_pdata(struct device *dev,
+				struct cs35l33_private *cs35l33)
+{
+	struct device_node *np = dev->of_node;
+	struct cs35l33_pdata *pdata = &cs35l33->pdata;
+	u32 val32;
+
+	if (!np)
+		return 0;
+
+	if (of_property_read_u32(np, "cirrus,boost-ctl", &val32) >= 0) {
+		pdata->boost_ctl = val32;
+		pdata->amp_drv_sel = 1;
+	}
+
+	if (of_property_read_u32(np, "cirrus,ramp-rate", &val32) >= 0) {
+		pdata->ramp_rate = val32;
+		cs35l33->enable_soft_ramp = true;
+	}
+
+	if (of_property_read_u32(np, "cirrus,boost-ipk", &val32) >= 0)
+		pdata->boost_ipk = val32;
+
+	if (of_property_read_u32(np, "cirrus,imon-adc-scale", &val32) >= 0) {
+		if ((val32 == 0x0) || (val32 == 0x7) || (val32 == 0x6))
+			pdata->imon_adc_scale = val32;
+		else
+			/* use default value */
+			pdata->imon_adc_scale = 0x8;
+	} else {
+		/* use default value */
+		pdata->imon_adc_scale = 0x8;
+	}
+
+	cs35l33_get_hg_data(np, pdata);
+
+	return 0;
+}
+
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
+				       const struct i2c_device_id *id)
+{
+	struct cs35l33_private *cs35l33;
+	struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
+	int ret, devid, i;
+	unsigned int reg;
+
+	cs35l33 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l33_private),
+			       GFP_KERNEL);
+	if (!cs35l33)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c_client, cs35l33);
+	cs35l33->regmap = devm_regmap_init_i2c(i2c_client, &cs35l33_regmap);
+	if (IS_ERR(cs35l33->regmap)) {
+		ret = PTR_ERR(cs35l33->regmap);
+		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+		return ret;
+	}
+
+	regcache_cache_only(cs35l33->regmap, true);
+
+	for (i = 0; i < ARRAY_SIZE(cs35l33_core_supplies); i++)
+		cs35l33->core_supplies[i].supply
+			= cs35l33_core_supplies[i];
+	cs35l33->num_core_supplies = ARRAY_SIZE(cs35l33_core_supplies);
+
+	ret = devm_regulator_bulk_get(&i2c_client->dev,
+			cs35l33->num_core_supplies,
+			cs35l33->core_supplies);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev,
+			"Failed to request core supplies: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (pdata) {
+		cs35l33->pdata = *pdata;
+	} else {
+		cs35l33_of_get_pdata(&i2c_client->dev, cs35l33);
+		pdata = &cs35l33->pdata;
+	}
+
+	ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+			cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+			"cs35l33", cs35l33);
+	if (ret != 0)
+		dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+	/* We could issue !RST or skip it based on AMP topology */
+	cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+			"reset-gpios", GPIOD_OUT_HIGH);
+	if (IS_ERR(cs35l33->reset_gpio)) {
+		dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n",
+			__func__);
+		return PTR_ERR(cs35l33->reset_gpio);
+	}
+
+	ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+					cs35l33->core_supplies);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev,
+			"Failed to enable core supplies: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (cs35l33->reset_gpio)
+		gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+	msleep(CS35L33_BOOT_DELAY);
+	regcache_cache_only(cs35l33->regmap, false);
+
+	/* initialize codec */
+	ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_AB, &reg);
+	devid = (reg & 0xFF) << 12;
+	ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_CD, &reg);
+	devid |= (reg & 0xFF) << 4;
+	ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_E, &reg);
+	devid |= (reg & 0xF0) >> 4;
+
+	if (devid != CS35L33_CHIP_ID) {
+		dev_err(&i2c_client->dev,
+			"CS35L33 Device ID (%X). Expected ID %X\n",
+			devid, CS35L33_CHIP_ID);
+		goto err_enable;
+	}
+
+	ret = regmap_read(cs35l33->regmap, CS35L33_REV_ID, &reg);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+		goto err_enable;
+	}
+
+	dev_info(&i2c_client->dev,
+		 "Cirrus Logic CS35L33, Revision: %02X\n", reg & 0xFF);
+
+	ret = regmap_register_patch(cs35l33->regmap,
+			cs35l33_patch, ARRAY_SIZE(cs35l33_patch));
+	if (ret < 0) {
+		dev_err(&i2c_client->dev,
+			"Error in applying regmap patch: %d\n", ret);
+		goto err_enable;
+	}
+
+	/* disable mclk and tdm */
+	regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+		CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM,
+		CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM);
+
+	pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+	pm_runtime_use_autosuspend(&i2c_client->dev);
+	pm_runtime_set_active(&i2c_client->dev);
+	pm_runtime_enable(&i2c_client->dev);
+
+	ret =  snd_soc_register_codec(&i2c_client->dev,
+			&soc_codec_dev_cs35l33, &cs35l33_dai, 1);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "%s: Register codec failed\n",
+			__func__);
+		goto err_enable;
+	}
+
+	return 0;
+
+err_enable:
+	regulator_bulk_disable(cs35l33->num_core_supplies,
+			       cs35l33->core_supplies);
+
+	return ret;
+}
+
+static int cs35l33_i2c_remove(struct i2c_client *client)
+{
+	struct cs35l33_private *cs35l33 = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+
+	if (cs35l33->reset_gpio)
+		gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+	pm_runtime_disable(&client->dev);
+	regulator_bulk_disable(cs35l33->num_core_supplies,
+		cs35l33->core_supplies);
+
+	return 0;
+}
+
+static const struct of_device_id cs35l33_of_match[] = {
+	{ .compatible = "cirrus,cs35l33", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, cs35l33_of_match);
+
+static const struct i2c_device_id cs35l33_id[] = {
+	{"cs35l33", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l33_id);
+
+static struct i2c_driver cs35l33_i2c_driver = {
+	.driver = {
+		.name = "cs35l33",
+		.pm = &cs35l33_pm_ops,
+		.of_match_table = cs35l33_of_match,
+
+		},
+	.id_table = cs35l33_id,
+	.probe = cs35l33_i2c_probe,
+	.remove = cs35l33_i2c_remove,
+
+};
+module_i2c_driver(cs35l33_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L33 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");

+ 221 - 0
sound/soc/codecs/cs35l33.h

@@ -0,0 +1,221 @@
+/*
+ * cs35l33.h -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L33_H__
+#define __CS35L33_H__
+
+#define CS35L33_CHIP_ID		0x00035A33
+#define CS35L33_DEVID_AB	0x01	/* Device ID A & B [RO] */
+#define CS35L33_DEVID_CD	0x02	/* Device ID C & D [RO] */
+#define CS35L33_DEVID_E		0x03	/* Device ID E [RO] */
+#define CS35L33_FAB_ID		0x04	/* Fab ID [RO] */
+#define CS35L33_REV_ID		0x05	/* Revision ID [RO] */
+#define CS35L33_PWRCTL1		0x06	/* Power Ctl 1 */
+#define CS35L33_PWRCTL2		0x07	/* Power Ctl 2 */
+#define CS35L33_CLK_CTL		0x08	/* Clock Ctl */
+#define CS35L33_BST_PEAK_CTL	0x09	/* Max Current for Boost */
+#define CS35L33_PROTECT_CTL	0x0A	/* Amp Protection Parameters */
+#define CS35L33_BST_CTL1	0x0B	/* Boost Converter CTL1 */
+#define CS35L33_BST_CTL2	0x0C	/* Boost Converter CTL2 */
+#define CS35L33_ADSP_CTL	0x0D	/* Serial Port Control */
+#define CS35L33_ADC_CTL		0x0E	/* ADC Control */
+#define CS35L33_DAC_CTL		0x0F	/* DAC Control */
+#define CS35L33_DIG_VOL_CTL	0x10	/* Digital Volume CTL */
+#define CS35L33_CLASSD_CTL	0x11	/* Class D Amp CTL */
+#define CS35L33_AMP_CTL		0x12	/* Amp Gain/Protecton Release CTL */
+#define CS35L33_INT_MASK_1	0x13	/* Interrupt Mask 1 */
+#define CS35L33_INT_MASK_2	0x14	/* Interrupt Mask 2 */
+#define CS35L33_INT_STATUS_1	0x15	/* Interrupt Status 1 [RO] */
+#define CS35L33_INT_STATUS_2	0x16	/* Interrupt Status 2 [RO] */
+#define CS35L33_DIAG_LOCK	0x17	/* Diagnostic Mode Register Lock */
+#define CS35L33_DIAG_CTRL_1	0x18	/* Diagnostic Mode Register Control */
+#define CS35L33_DIAG_CTRL_2	0x19	/* Diagnostic Mode Register Control 2 */
+#define CS35L33_HG_MEMLDO_CTL	0x23	/* H/G Memory/LDO CTL */
+#define CS35L33_HG_REL_RATE	0x24	/* H/G Release Rate */
+#define CS35L33_LDO_DEL		0x25	/* LDO Entry Delay/VPhg Control 1 */
+#define CS35L33_HG_HEAD		0x29	/* H/G Headroom */
+#define CS35L33_HG_EN		0x2A	/* H/G Enable/VPhg CNT2 */
+#define CS35L33_TX_VMON		0x2D	/* TDM TX Control 1 (VMON) */
+#define CS35L33_TX_IMON		0x2E	/* TDM TX Control 2 (IMON) */
+#define CS35L33_TX_VPMON	0x2F	/* TDM TX Control 3 (VPMON) */
+#define CS35L33_TX_VBSTMON	0x30	/* TDM TX Control 4 (VBSTMON) */
+#define CS35L33_TX_FLAG		0x31	/* TDM TX Control 5 (FLAG) */
+#define CS35L33_TX_EN1		0x32	/* TDM TX Enable 1 */
+#define CS35L33_TX_EN2		0x33	/* TDM TX Enable 2 */
+#define CS35L33_TX_EN3		0x34	/* TDM TX Enable 3 */
+#define CS35L33_TX_EN4		0x35	/* TDM TX Enable 4 */
+#define CS35L33_RX_AUD		0x36	/* TDM RX Control 1 */
+#define CS35L33_RX_SPLY		0x37	/* TDM RX Control 2 */
+#define CS35L33_RX_ALIVE	0x38	/* TDM RX Control 3 */
+#define CS35L33_BST_CTL4	0x39	/* Boost Converter Control 4 */
+#define CS35L33_HG_STATUS	0x3F	/* H/G Status */
+#define CS35L33_MAX_REGISTER	0x59
+
+#define CS35L33_MCLK_5644	5644800
+#define CS35L33_MCLK_6144	6144000
+#define CS35L33_MCLK_6		6000000
+#define CS35L33_MCLK_11289	11289600
+#define CS35L33_MCLK_12		12000000
+#define CS35L33_MCLK_12288	12288000
+
+/* CS35L33_PWRCTL1 */
+#define CS35L33_PDN_AMP			(1 << 7)
+#define CS35L33_PDN_BST			(1 << 2)
+#define CS35L33_PDN_ALL			1
+
+/* CS35L33_PWRCTL2 */
+#define CS35L33_PDN_VMON_SHIFT		7
+#define CS35L33_PDN_VMON		(1 << CS35L33_PDN_VMON_SHIFT)
+#define CS35L33_PDN_IMON_SHIFT		6
+#define CS35L33_PDN_IMON		(1 << CS35L33_PDN_IMON_SHIFT)
+#define CS35L33_PDN_VPMON_SHIFT		5
+#define CS35L33_PDN_VPMON		(1 << CS35L33_PDN_VPMON_SHIFT)
+#define CS35L33_PDN_VBSTMON_SHIFT	4
+#define CS35L33_PDN_VBSTMON		(1 << CS35L33_PDN_VBSTMON_SHIFT)
+#define CS35L33_SDOUT_3ST_I2S_SHIFT	3
+#define CS35L33_SDOUT_3ST_I2S		(1 << CS35L33_SDOUT_3ST_I2S_SHIFT)
+#define CS35L33_PDN_SDIN_SHIFT		2
+#define CS35L33_PDN_SDIN		(1 << CS35L33_PDN_SDIN_SHIFT)
+#define CS35L33_PDN_TDM_SHIFT		1
+#define CS35L33_PDN_TDM			(1 << CS35L33_PDN_TDM_SHIFT)
+
+/* CS35L33_CLK_CTL */
+#define CS35L33_MCLKDIS			(1 << 7)
+#define CS35L33_MCLKDIV2		(1 << 6)
+#define CS35L33_SDOUT_3ST_TDM		(1 << 5)
+#define CS35L33_INT_FS_RATE		(1 << 4)
+#define CS35L33_ADSP_FS			0xF
+
+/* CS35L33_PROTECT_CTL */
+#define CS35L33_ALIVE_WD_DIS		(3 << 2)
+
+/* CS35L33_BST_CTL1 */
+#define CS35L33_BST_CTL_SRC		(1 << 6)
+#define CS35L33_BST_CTL_SHIFT		(1 << 5)
+#define CS35L33_BST_CTL_MASK		0x3F
+
+/* CS35L33_BST_CTL2 */
+#define CS35L33_TDM_WD_SEL		(1 << 4)
+#define CS35L33_ALIVE_WD_DIS2		(1 << 3)
+#define CS35L33_VBST_SR_STEP		0x3
+
+/* CS35L33_ADSP_CTL */
+#define CS35L33_ADSP_DRIVE		(1 << 7)
+#define CS35L33_MS_MASK			(1 << 6)
+#define CS35L33_SDIN_LOC		(3 << 4)
+#define CS35L33_ALIVE_RATE		0x3
+
+/* CS35L33_ADC_CTL */
+#define CS35L33_INV_VMON		(1 << 7)
+#define CS35L33_INV_IMON		(1 << 6)
+#define CS35L33_ADC_NOTCH_DIS		(1 << 5)
+#define CS35L33_IMON_SCALE		0xF
+
+/* CS35L33_DAC_CTL */
+#define CS35L33_INV_DAC			(1 << 7)
+#define CS35L33_DAC_NOTCH_DIS		(1 << 5)
+#define CS35L33_DIGSFT			(1 << 4)
+#define CS35L33_DSR_RATE		0xF
+
+/* CS35L33_CLASSD_CTL */
+#define CS35L33_AMP_SD			(1 << 6)
+#define CS35L33_AMP_DRV_SEL_SRC		(1 << 5)
+#define CS35L33_AMP_DRV_SEL_MASK	0x10
+#define CS35L33_AMP_DRV_SEL_SHIFT	4
+#define CS35L33_AMP_CAL			(1 << 3)
+#define CS35L33_GAIN_CHG_ZC_MASK	0x04
+#define CS35L33_GAIN_CHG_ZC_SHIFT	2
+#define CS35L33_CLASS_D_CTL_MASK	0x3F
+
+/* CS35L33_AMP_CTL */
+#define CS35L33_AMP_GAIN		0xF0
+#define CS35L33_CAL_ERR_RLS		(1 << 3)
+#define CS35L33_AMP_SHORT_RLS		(1 << 2)
+#define CS35L33_OTW_RLS			(1 << 1)
+#define CS35L33_OTE_RLS			1
+
+/* CS35L33_INT_MASK_1 */
+#define CS35L33_M_CAL_ERR_SHIFT		6
+#define CS35L33_M_CAL_ERR		(1 << CS35L33_M_CAL_ERR_SHIFT)
+#define CS35L33_M_ALIVE_ERR_SHIFT	5
+#define CS35L33_M_ALIVE_ERR		(1 << CS35L33_M_ALIVE_ERR_SHIFT)
+#define CS35L33_M_AMP_SHORT_SHIFT	2
+#define CS35L33_M_AMP_SHORT		(1 << CS35L33_M_AMP_SHORT_SHIFT)
+#define CS35L33_M_OTW_SHIFT		1
+#define CS35L33_M_OTW			(1 << CS35L33_M_OTW_SHIFT)
+#define CS35L33_M_OTE_SHIFT		0
+#define CS35L33_M_OTE			(1 << CS35L33_M_OTE_SHIFT)
+
+/* CS35L33_INT_STATUS_1 */
+#define CS35L33_CAL_ERR			(1 << 6)
+#define CS35L33_ALIVE_ERR		(1 << 5)
+#define CS35L33_ADSPCLK_ERR		(1 << 4)
+#define CS35L33_MCLK_ERR		(1 << 3)
+#define CS35L33_AMP_SHORT		(1 << 2)
+#define CS35L33_OTW			(1 << 1)
+#define CS35L33_OTE			(1 << 0)
+
+/* CS35L33_INT_STATUS_2 */
+#define CS35L33_VMON_OVFL		(1 << 7)
+#define CS35L33_IMON_OVFL		(1 << 6)
+#define CS35L33_VPMON_OVFL		(1 << 5)
+#define CS35L33_VBSTMON_OVFL		(1 << 4)
+#define CS35L33_PDN_DONE		1
+
+/* CS35L33_BST_CTL4 */
+#define CS35L33_BST_RGS			0x70
+#define CS35L33_BST_COEFF3		0xF
+
+/* CS35L33_HG_MEMLDO_CTL */
+#define CS35L33_MEM_DEPTH_SHIFT		5
+#define CS35L33_MEM_DEPTH_MASK		(0x3 << CS35L33_MEM_DEPTH_SHIFT)
+#define CS35L33_LDO_THLD_SHIFT		1
+#define CS35L33_LDO_THLD_MASK		(0xF << CS35L33_LDO_THLD_SHIFT)
+#define CS35L33_LDO_DISABLE_SHIFT	0
+#define CS35L33_LDO_DISABLE_MASK	(0x1 << CS35L33_LDO_DISABLE_SHIFT)
+
+/* CS35L33_LDO_DEL */
+#define CS35L33_VP_HG_VA_SHIFT		5
+#define CS35L33_VP_HG_VA_MASK		(0x7 << CS35L33_VP_HG_VA_SHIFT)
+#define CS35L33_LDO_ENTRY_DELAY_SHIFT	2
+#define CS35L33_LDO_ENTRY_DELAY_MASK	(0x7 << CS35L33_LDO_ENTRY_DELAY_SHIFT)
+#define CS35L33_VP_HG_RATE_SHIFT	0
+#define CS35L33_VP_HG_RATE_MASK		(0x3 << CS35L33_VP_HG_RATE_SHIFT)
+
+/* CS35L33_HG_HEAD */
+#define CS35L33_HD_RM_SHIFT		0
+#define CS35L33_HD_RM_MASK		(0x7F << CS35L33_HD_RM_SHIFT)
+
+/* CS35L33_HG_EN */
+#define CS35L33_CLASS_HG_ENA_SHIFT	7
+#define CS35L33_CLASS_HG_EN_MASK	(0x1 << CS35L33_CLASS_HG_ENA_SHIFT)
+#define CS35L33_VP_HG_AUTO_SHIFT	6
+#define CS35L33_VP_HG_AUTO_MASK		(0x1 << 6)
+#define CS35L33_VP_HG_SHIFT		0
+#define CS35L33_VP_HG_MASK		(0x1F << CS35L33_VP_HG_SHIFT)
+
+#define CS35L33_RATES (SNDRV_PCM_RATE_8000_48000)
+#define CS35L33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+/* CS35L33_{RX,TX}_X */
+#define CS35L33_X_STATE_SHIFT		7
+#define CS35L33_X_STATE			(1 << CS35L33_X_STATE_SHIFT)
+#define CS35L33_X_LOC_SHIFT		0
+#define CS35L33_X_LOC			(0x1F << CS35L33_X_LOC_SHIFT)
+
+/* CS35L33_RX_AUD */
+#define CS35L33_AUDIN_RX_DEPTH_SHIFT	5
+#define CS35L33_AUDIN_RX_DEPTH		(0x7 << CS35L33_AUDIN_RX_DEPTH_SHIFT)
+
+#endif

部分文件因为文件数量过多而无法显示