summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-07-31 02:25:02 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-07-31 02:25:02 -0700
commitc9b95e5961c0294e0efffeaa847c1a1e6369204c (patch)
tree384daa5e36a795d48475d445b3857b47e0b3f8d3
parentbad60e6f259a01cf9f29a1ef8d435ab6c60b2de9 (diff)
parent0984d159c8ad6618c6ebd9f00bc3f374fa52bc35 (diff)
downloadlinux-sh-c9b95e5961c0294e0efffeaa847c1a1e6369204c.tar.gz
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 ...
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau17x1.txt8
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau7002.txt19
-rw-r--r--Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt67
-rw-r--r--Documentation/devicetree/bindings/sound/bt-sco.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/cs35l33.txt126
-rw-r--r--Documentation/devicetree/bindings/sound/cs53l30.txt44
-rw-r--r--Documentation/devicetree/bindings/sound/designware-i2s.txt4
-rw-r--r--Documentation/devicetree/bindings/sound/fsl-asoc-card.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/max98504.txt44
-rw-r--r--Documentation/devicetree/bindings/sound/max9860.txt28
-rw-r--r--Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt150
-rw-r--r--Documentation/devicetree/bindings/sound/mt2701-cs42448.txt43
-rw-r--r--Documentation/devicetree/bindings/sound/mt8173-rt5650.txt10
-rw-r--r--Documentation/devicetree/bindings/sound/omap-mcpdm.txt10
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip-i2s.txt5
-rw-r--r--Documentation/devicetree/bindings/sound/rt5514.txt5
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt35
-rw-r--r--Documentation/devicetree/bindings/sound/sgtl5000.txt18
-rw-r--r--Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt20
-rw-r--r--Documentation/devicetree/bindings/sound/sun4i-i2s.txt34
-rw-r--r--Documentation/sound/alsa/soc/machine.txt2
-rw-r--r--Documentation/sound/alsa/timestamping.txt12
-rw-r--r--MAINTAINERS11
-rw-r--r--drivers/base/property.c28
-rw-r--r--include/acpi/acpi_bus.h7
-rw-r--r--include/linux/acpi.h6
-rw-r--r--include/linux/mfd/arizona/core.h10
-rw-r--r--include/linux/of.h14
-rw-r--r--include/linux/property.h3
-rw-r--r--include/sound/compress_driver.h5
-rw-r--r--include/sound/cs35l33.h48
-rw-r--r--include/sound/hdmi-codec.h13
-rw-r--r--include/sound/simple_card.h11
-rw-r--r--include/sound/simple_card_utils.h36
-rw-r--r--include/sound/soc-dapm.h8
-rw-r--r--include/sound/soc.h11
-rw-r--r--sound/Makefile1
-rw-r--r--sound/core/compress_offload.c67
-rw-r--r--sound/core/control.c32
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c10
-rw-r--r--sound/core/seq/seq_timer.c23
-rw-r--r--sound/core/seq/seq_timer.h2
-rw-r--r--sound/hda/hdmi_chmap.c28
-rw-r--r--sound/i2c/other/ak4114.c2
-rw-r--r--sound/i2c/other/ak4117.c2
-rw-r--r--sound/isa/ad1848/ad1848.c13
-rw-r--r--sound/isa/adlib.c13
-rw-r--r--sound/isa/cmi8328.c13
-rw-r--r--sound/isa/cs423x/cs4231.c13
-rw-r--r--sound/isa/galaxy/galaxy.c13
-rw-r--r--sound/isa/gus/gusclassic.c13
-rw-r--r--sound/isa/gus/gusextreme.c13
-rw-r--r--sound/isa/gus/gusmax.c13
-rw-r--r--sound/isa/sb/jazz16.c13
-rw-r--r--sound/isa/sb/sb8.c13
-rw-r--r--sound/isa/sc6000.c13
-rw-r--r--sound/oss/ad1848.c2
-rw-r--r--sound/oss/aedsp16.c14
-rw-r--r--sound/oss/sound_firmware.h29
-rw-r--r--sound/oss/sound_timer.c2
-rw-r--r--sound/oss/sys_timer.c2
-rw-r--r--sound/pci/ctxfi/cthw20k2.c34
-rw-r--r--sound/pci/echoaudio/echoaudio.c6
-rw-r--r--sound/pci/hda/hda_codec.c8
-rw-r--r--sound/pci/hda/hda_generic.c4
-rw-r--r--sound/pci/hda/patch_hdmi.c5
-rw-r--r--sound/pci/hda/patch_realtek.c38
-rw-r--r--sound/pci/mixart/mixart_mixer.c2
-rw-r--r--sound/pci/riptide/riptide.c2
-rw-r--r--sound/ppc/awacs.c1
-rw-r--r--sound/sh/aica.c16
-rw-r--r--sound/soc/atmel/Kconfig1
-rw-r--r--sound/soc/atmel/atmel-classd.c5
-rw-r--r--sound/soc/atmel/atmel-pdmic.c5
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c2
-rw-r--r--sound/soc/bcm/Kconfig9
-rw-r--r--sound/soc/bcm/Makefile5
-rw-r--r--sound/soc/bcm/cygnus-pcm.c861
-rw-r--r--sound/soc/bcm/cygnus-ssp.c1529
-rw-r--r--sound/soc/bcm/cygnus-ssp.h139
-rw-r--r--sound/soc/codecs/Kconfig38
-rw-r--r--sound/soc/codecs/Makefile14
-rw-r--r--sound/soc/codecs/adau-utils.c61
-rw-r--r--sound/soc/codecs/adau-utils.h7
-rw-r--r--sound/soc/codecs/adau1373.c38
-rw-r--r--sound/soc/codecs/adau1761-i2c.c2
-rw-r--r--sound/soc/codecs/adau1761-spi.c2
-rw-r--r--sound/soc/codecs/adau1781-i2c.c2
-rw-r--r--sound/soc/codecs/adau1781-spi.c2
-rw-r--r--sound/soc/codecs/adau17x1.c251
-rw-r--r--sound/soc/codecs/adau17x1.h6
-rw-r--r--sound/soc/codecs/adau7002.c80
-rw-r--r--sound/soc/codecs/ak4613.c12
-rw-r--r--sound/soc/codecs/ak4642.c13
-rw-r--r--sound/soc/codecs/arizona.c91
-rw-r--r--sound/soc/codecs/arizona.h22
-rw-r--r--sound/soc/codecs/bt-sco.c52
-rw-r--r--sound/soc/codecs/cs35l33.c1303
-rw-r--r--sound/soc/codecs/cs35l33.h221
-rw-r--r--sound/soc/codecs/cs47l24.c19
-rw-r--r--sound/soc/codecs/cs53l30.c1143
-rw-r--r--sound/soc/codecs/cs53l30.h459
-rw-r--r--sound/soc/codecs/da7219-aad.c103
-rw-r--r--sound/soc/codecs/da7219.c34
-rw-r--r--sound/soc/codecs/hdac_hdmi.c7
-rw-r--r--sound/soc/codecs/hdmi-codec.c15
-rw-r--r--sound/soc/codecs/max98504.c383
-rw-r--r--sound/soc/codecs/max98504.h59
-rw-r--r--sound/soc/codecs/max9860.c753
-rw-r--r--sound/soc/codecs/max9860.h162
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/max9867.c0
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/max9867.h0
-rw-r--r--sound/soc/codecs/nau8825.c1256
-rw-r--r--sound/soc/codecs/nau8825.h127
-rw-r--r--sound/soc/codecs/pcm1681.c2
-rw-r--r--sound/soc/codecs/pcm179x.c2
-rw-r--r--sound/soc/codecs/pcm5102a.c1
-rw-r--r--sound/soc/codecs/rt286.c7
-rw-r--r--sound/soc/codecs/rt5514-spi.c447
-rw-r--r--sound/soc/codecs/rt5514-spi.h38
-rw-r--r--sound/soc/codecs/rt5514.c168
-rw-r--r--sound/soc/codecs/rt5514.h7
-rw-r--r--sound/soc/codecs/rt5645.c24
-rw-r--r--sound/soc/codecs/rt5645.h3
-rw-r--r--sound/soc/codecs/rt5670.c3
-rw-r--r--sound/soc/codecs/sgtl5000.c431
-rw-r--r--sound/soc/codecs/sgtl5000.h2
-rw-r--r--sound/soc/codecs/tas571x.c188
-rw-r--r--sound/soc/codecs/tas571x.h40
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h134
-rw-r--r--sound/soc/codecs/tpa6130a2.c394
-rw-r--r--sound/soc/codecs/tpa6130a2.h14
-rw-r--r--sound/soc/codecs/wm5102.c1
-rw-r--r--sound/soc/codecs/wm5110.c19
-rw-r--r--sound/soc/codecs/wm8731.c5
-rw-r--r--sound/soc/codecs/wm8753.c3
-rw-r--r--sound/soc/codecs/wm8985.c143
-rw-r--r--sound/soc/codecs/wm8985.h38
-rw-r--r--sound/soc/codecs/wm8998.c1
-rw-r--r--sound/soc/codecs/wm_adsp.c34
-rw-r--r--sound/soc/codecs/wm_adsp.h4
-rw-r--r--sound/soc/davinci/davinci-mcasp.c9
-rw-r--r--sound/soc/dwc/Kconfig9
-rw-r--r--sound/soc/dwc/Makefile4
-rw-r--r--sound/soc/dwc/designware_i2s.c231
-rw-r--r--sound/soc/dwc/designware_pcm.c225
-rw-r--r--sound/soc/dwc/local.h128
-rw-r--r--sound/soc/fsl/Kconfig1
-rw-r--r--sound/soc/fsl/fsl_spdif.c2
-rw-r--r--sound/soc/generic/Kconfig4
-rw-r--r--sound/soc/generic/Makefile2
-rw-r--r--sound/soc/generic/simple-card-utils.c97
-rw-r--r--sound/soc/generic/simple-card.c276
-rw-r--r--sound/soc/intel/Kconfig73
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c44
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c460
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c118
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c20
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c51
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c51
-rw-r--r--sound/soc/intel/boards/skl_rt286.c9
-rw-r--r--sound/soc/intel/common/Makefile4
-rw-r--r--sound/soc/intel/common/sst-acpi.h4
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h4
-rw-r--r--sound/soc/intel/common/sst-dsp.c69
-rw-r--r--sound/soc/intel/common/sst-dsp.h2
-rw-r--r--sound/soc/intel/common/sst-firmware.c68
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c1
-rw-r--r--sound/soc/intel/skylake/Makefile2
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c202
-rw-r--r--sound/soc/intel/skylake/skl-messages.c53
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c40
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.h22
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c93
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c260
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h102
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c4
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h18
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c256
-rw-r--r--sound/soc/intel/skylake/skl-sst.c124
-rw-r--r--sound/soc/intel/skylake/skl-topology.c230
-rw-r--r--sound/soc/intel/skylake/skl-topology.h7
-rw-r--r--sound/soc/intel/skylake/skl.c39
-rw-r--r--sound/soc/intel/skylake/skl.h10
-rw-r--r--sound/soc/mediatek/Kconfig38
-rw-r--r--sound/soc/mediatek/Makefile10
-rw-r--r--sound/soc/mediatek/common/Makefile16
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c379
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.h45
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c90
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.h23
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h104
-rw-r--r--sound/soc/mediatek/mt2701/Makefile19
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c464
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h38
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-common.h172
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c1656
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c412
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-reg.h186
-rw-r--r--sound/soc/mediatek/mt8173/Makefile7
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-common.h73
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c (renamed from sound/soc/mediatek/mtk-afe-pcm.c)925
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c (renamed from sound/soc/mediatek/mt8173-max98090.c)2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c (renamed from sound/soc/mediatek/mt8173-rt5650-rt5514.c)2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c (renamed from sound/soc/mediatek/mt8173-rt5650-rt5676.c)4
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c (renamed from sound/soc/mediatek/mt8173-rt5650.c)73
-rw-r--r--sound/soc/mediatek/mtk-afe-common.h101
-rw-r--r--sound/soc/omap/Kconfig3
-rw-r--r--sound/soc/omap/omap-mcpdm.c74
-rw-r--r--sound/soc/omap/rx51.c46
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c71
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h11
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c65
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c17
-rw-r--r--sound/soc/samsung/Kconfig8
-rw-r--r--sound/soc/samsung/Makefile2
-rw-r--r--sound/soc/samsung/ac97.c3
-rw-r--r--sound/soc/samsung/dma.h9
-rw-r--r--sound/soc/samsung/dmaengine.c31
-rw-r--r--sound/soc/samsung/i2s.c53
-rw-r--r--sound/soc/samsung/odroidx2_max98090.c185
-rw-r--r--sound/soc/samsung/pcm.c3
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c2
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c3
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c3
-rw-r--r--sound/soc/samsung/spdif.c3
-rw-r--r--sound/soc/sh/Kconfig1
-rw-r--r--sound/soc/sh/rcar/adg.c18
-rw-r--r--sound/soc/sh/rcar/gen.c12
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c99
-rw-r--r--sound/soc/soc-compress.c5
-rw-r--r--sound/soc/soc-dapm.c61
-rw-r--r--sound/soc/soc-pcm.c43
-rw-r--r--sound/soc/sti/uniperif_player.c4
-rw-r--r--sound/soc/sunxi/Kconfig9
-rw-r--r--sound/soc/sunxi/Makefile2
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c701
-rw-r--r--sound/sound_firmware.c77
-rw-r--r--sound/usb/mixer_maps.c6
241 files changed, 19420 insertions, 3301 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,adau17x1.txt b/Documentation/devicetree/bindings/sound/adi,adau17x1.txt
index 8dbce0e18dda..1447dec28125 100644
--- a/Documentation/devicetree/bindings/sound/adi,adau17x1.txt
+++ b/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>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7002.txt b/Documentation/devicetree/bindings/sound/adi,adau7002.txt
new file mode 100644
index 000000000000..f144ee1abf85
--- /dev/null
+++ b/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>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt b/Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt
new file mode 100644
index 000000000000..b139e66d2a11
--- /dev/null
+++ b/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";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/bt-sco.txt b/Documentation/devicetree/bindings/sound/bt-sco.txt
index 29b8e5d40203..641edf75e184 100644
--- a/Documentation/devicetree/bindings/sound/bt-sco.txt
+++ b/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:
diff --git a/Documentation/devicetree/bindings/sound/cs35l33.txt b/Documentation/devicetree/bindings/sound/cs35l33.txt
new file mode 100644
index 000000000000..acfb47525b49
--- /dev/null
+++ b/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>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/sound/cs53l30.txt b/Documentation/devicetree/bindings/sound/cs53l30.txt
new file mode 100644
index 000000000000..4dbfb8274cd9
--- /dev/null
+++ b/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>;
+};
diff --git a/Documentation/devicetree/bindings/sound/designware-i2s.txt b/Documentation/devicetree/bindings/sound/designware-i2s.txt
index 7bb54247f8e8..6a536d570e29 100644
--- a/Documentation/devicetree/bindings/sound/designware-i2s.txt
+++ b/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
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
index ceaef5126989..f749e2744824 100644
--- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
+++ b/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:
diff --git a/Documentation/devicetree/bindings/sound/max98504.txt b/Documentation/devicetree/bindings/sound/max98504.txt
new file mode 100644
index 000000000000..583ed5fdfb28
--- /dev/null
+++ b/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>;
+};
diff --git a/Documentation/devicetree/bindings/sound/max9860.txt b/Documentation/devicetree/bindings/sound/max9860.txt
new file mode 100644
index 000000000000..e0d4e95e31b3
--- /dev/null
+++ b/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>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
new file mode 100644
index 000000000000..3e623a724e55
--- /dev/null
+++ b/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";
+ };
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
new file mode 100644
index 000000000000..05574446ceb6
--- /dev/null
+++ b/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>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
index 5bfa6b60530b..29dce2ac8773 100644
--- a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
+++ b/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>;
};
diff --git a/Documentation/devicetree/bindings/sound/omap-mcpdm.txt b/Documentation/devicetree/bindings/sound/omap-mcpdm.txt
index 0741dff048dd..6f6c2f8e908d 100644
--- a/Documentation/devicetree/bindings/sound/omap-mcpdm.txt
+++ b/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";
+};
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
index c7b29df4a963..15a7316e4c91 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
+++ b/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
diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt
index 6e86d8aa29b4..4ea29aa9af59 100644
--- a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt
+++ b/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 {
diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt
index e24436fc5ea9..9cabfc18cb57 100644
--- a/Documentation/devicetree/bindings/sound/rt5514.txt
+++ b/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
diff --git a/Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt b/Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt
deleted file mode 100644
index 9148f72319e1..000000000000
--- a/Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt
+++ /dev/null
@@ -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";
-};
diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt
index 0e5e4eb3ef1b..5666da7b8605 100644
--- a/Documentation/devicetree/bindings/sound/sgtl5000.txt
+++ b/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 {
diff --git a/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt b/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
index 4d9a83d9a017..16bcdfb6760e 100644
--- a/Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
+++ b/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";
};
diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
new file mode 100644
index 000000000000..7b526ec64991
--- /dev/null
+++ b/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";
+};
diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt
index 74056dba52be..6bf2d2063b52 100644
--- a/Documentation/sound/alsa/soc/machine.txt
+++ b/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
diff --git a/Documentation/sound/alsa/timestamping.txt b/Documentation/sound/alsa/timestamping.txt
index 1b6473f393a8..9d579aefbffd 100644
--- a/Documentation/sound/alsa/timestamping.txt
+++ b/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
diff --git a/MAINTAINERS b/MAINTAINERS
index 8af18097e92c..8ca0b205759f 100644
--- a/MAINTAINERS
+++ b/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*
diff --git a/drivers/base/property.c b/drivers/base/property.c
index f38c21de29b7..43a36d68c3fd 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -888,6 +888,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.
*
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 788c6c35291a..c1a524de67c5 100644
--- a/include/acpi/acpi_bus.h
+++ b/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;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index db7c8bd39a3c..4d8452c2384b 100644
--- a/include/linux/acpi.h
+++ b/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;
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h
index d55a42297d49..58ab4c0fe761 100644
--- a/include/linux/mfd/arizona/core.h
+++ b/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);
diff --git a/include/linux/of.h b/include/linux/of.h
index eb8d4b4c5dfc..3d9ff8e9d803 100644
--- a/include/linux/of.h
+++ b/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
diff --git a/include/linux/property.h b/include/linux/property.h
index ecab11e40794..3a2f9ae25c86 100644
--- a/include/linux/property.h
+++ b/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);
diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h
index c0abcdc11470..cee8c00f3d3e 100644
--- a/include/sound/compress_driver.h
+++ b/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
diff --git a/include/sound/cs35l33.h b/include/sound/cs35l33.h
new file mode 100644
index 000000000000..b6eadce76fc8
--- /dev/null
+++ b/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 */
diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h
index fc3a481ad91e..530c57bdefa0 100644
--- a/include/sound/hdmi-codec.h
+++ b/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"
diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h
index 0399352f3a62..a6a2e1547092 100644
--- a/include/sound/simple_card.h
+++ b/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;
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
new file mode 100644
index 000000000000..86088aed9002
--- /dev/null
+++ b/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 */
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 3101d53468aa..f60d755f7ac6 100644
--- a/include/sound/soc-dapm.h
+++ b/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);
diff --git a/include/sound/soc.h b/include/sound/soc.h
index fd7b58a58d6f..6144882cc96a 100644
--- a/include/sound/soc.h
+++ b/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 | \
diff --git a/sound/Makefile b/sound/Makefile
index 77320709fd26..c41bdf5fdf24 100644
--- a/sound/Makefile
+++ b/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/ \
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 9b3334be9df2..2c498488af6c 100644
--- a/sound/core/compress_offload.c
+++ b/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;
diff --git a/sound/core/control.c b/sound/core/control.c
index b4fe9b002512..fb096cb20a80 100644
--- a/sound/core/control.c
+++ b/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;
/*
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index b16dbef04174..cd0e0ebbfdb1 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/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);
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 293104926098..dcc102813aef 100644
--- a/sound/core/seq/seq_timer.c
+++ b/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);
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 88dfb71805ae..9506b661fe5b 100644
--- a/sound/core/seq/seq_timer.h
+++ b/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;
};
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
index c6c75e7e0981..81acc20c2535 100644
--- a/sound/hda/hdmi_chmap.c
+++ b/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);
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 5a4cf3fab4ae..d53c9bb36281 100644
--- a/sound/i2c/other/ak4114.c
+++ b/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);
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index 48848909a5a9..0702f0552d19 100644
--- a/sound/i2c/other/ak4117.c
+++ b/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)
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index f159da4ec890..a302d1f8d14f 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/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);
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index 120c524bb2a0..8d3060fd7ad7 100644
--- a/sound/isa/adlib.c
+++ b/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);
diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c
index 2c89d95da674..787475084f46 100644
--- a/sound/isa/cmi8328.c
+++ b/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);
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index 282cd75d2235..ef7448e9f813 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/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);
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
index 32278847884f..379abe2cbeb2 100644
--- a/sound/isa/galaxy/galaxy.c
+++ b/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);
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index f0019715d82e..c169be49ed71 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/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);
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 693d95f46804..77ac2fd723b4 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/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);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index 8216e8d8f017..dd88c9d33492 100644
--- a/sound/isa/gus/gusmax.c
+++ b/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);
diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
index 6b4884d052a5..4d909971eedb 100644
--- a/sound/isa/sb/jazz16.c
+++ b/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);
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index b8e2391c33ff..ad42d2364199 100644
--- a/sound/isa/sb/sb8.c
+++ b/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);
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 51cfa7615f72..b61a6633d8f2 100644
--- a/sound/isa/sc6000.c
+++ b/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);
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
index 10c8de1f8d29..6368e5c7d0ba 100644
--- a/sound/oss/ad1848.c
+++ b/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.
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
index 35b5912cf3f8..bb477d5c8528 100644
--- a/sound/oss/aedsp16.c
+++ b/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
};
/*
diff --git a/sound/oss/sound_firmware.h b/sound/oss/sound_firmware.h
index 0a0cbfdfb855..da4c67e005ed 100644
--- a/sound/oss/sound_firmware.h
+++ b/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;
+}
diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c
index 8021c85f076d..3a444a6f10eb 100644
--- a/sound/oss/sound_timer.c
+++ b/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;
diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c
index 2226dda0eff0..d17019d25b99 100644
--- a/sound/oss/sys_timer.c
+++ b/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;
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 9dc2950e1ab7..6414ecf93efa 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/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) {
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 286f5e3686a3..937071760bc4 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/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;
}
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 83741887faa1..9913be8532ab 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/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)) {
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 79c7b340acc2..e7c8f4f076d5 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/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;
}
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index d0d5ad8beac5..56e5204ac9c1 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/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;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index abcb5a6a1cd9..ddd29b9819ba 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/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),
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 58fd79ebac20..51e53497f0ad 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/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 */
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 94639d6b5fb5..067a91207d8e 100644
--- a/sound/pci/riptide/riptide.c
+++ b/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
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 09da7b52bc2e..1468e4b7bf93 100644
--- a/sound/ppc/awacs.c
+++ b/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),
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index ad3d9ae38034..fbbc25279559 100644
--- a/sound/sh/aica.c
+++ b/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 */
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 06e099e802df..22aec9a1e9a4 100644
--- a/sound/soc/atmel/Kconfig
+++ b/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)
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index 6107de9c538b..6d9b8b44e2da 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/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);
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index aee4787a0b89..5f56da60c92f 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/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);
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 1267e1af0fae..54c09acd3fed 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/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;
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
index 6a834e109f1d..d528aaceaad9 100644
--- a/sound/soc/bcm/Kconfig
+++ b/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. \ No newline at end of file
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
index bc816b71e5a4..fc739d007884 100644
--- a/sound/soc/bcm/Makefile
+++ b/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
+
diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c
new file mode 100644
index 000000000000..d616e096462e
--- /dev/null
+++ b/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");
diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c
new file mode 100644
index 000000000000..e710bb0c5637
--- /dev/null
+++ b/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");
diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h
new file mode 100644
index 000000000000..33dd34305928
--- /dev/null
+++ b/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
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index f3fb98f0a995..1cd6ab344d67 100644
--- a/sound/soc/codecs/Kconfig
+++ b/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
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0f548fd34ca3..58036af2c7d9 100644
--- a/sound/soc/codecs/Makefile
+++ b/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
diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c
new file mode 100644
index 000000000000..19d6a6f41b12
--- /dev/null
+++ b/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");
diff --git a/sound/soc/codecs/adau-utils.h b/sound/soc/codecs/adau-utils.h
new file mode 100644
index 000000000000..939b5f37762f
--- /dev/null
+++ b/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
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index fe1353a797b9..1556b360fa15 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/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);
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 8de010f758cd..9e7f257f17f8 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/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;
}
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index d9171245bd9f..a0b214be759a 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/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;
}
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 06cbca84cf02..7b9d1802d159 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/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;
}
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 3d965a01b99c..9b233544d2e8 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/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;
}
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index fcf05b254ecd..439aa3ff1f99 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/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");
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index 5ae87a084d97..bf04b7efee40 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/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);
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
new file mode 100644
index 000000000000..9df72c6adcca
--- /dev/null
+++ b/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");
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 5013d2ba0c10..97798d250f08 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/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,
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 4d8b9e49e8d6..cc941d66ec3d 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/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,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 664a8c044ffb..ecfdbfcae366 100644
--- a/sound/soc/codecs/arizona.c
+++ b/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");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index ce0531b8c632..69da1ef3a17c 100644
--- a/sound/soc/codecs/arizona.h
+++ b/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
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad113e96..2a8d0ee141d4 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/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);
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
new file mode 100644
index 000000000000..6f9c1addcd7f
--- /dev/null
+++ b/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");
diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h
new file mode 100644
index 000000000000..c045737d1a5f
--- /dev/null
+++ b/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
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index 5ec5a682d186..954a4f5d3338 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -359,6 +359,11 @@ SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+ &arizona_voice_trigger_switch[2]),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -899,10 +904,16 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
+ { "DRC2 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
{ "DRC2 Signal Activity", NULL, "DRC2L" },
{ "DRC2 Signal Activity", NULL, "DRC2R" },
+
+ { "DSP Voice Trigger", NULL, "SYSCLK" },
+ { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+ { "DSP3 Voice Trigger", "Switch", "DSP3" },
};
static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1067,6 +1078,7 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
{
struct cs47l24_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
@@ -1074,6 +1086,12 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
if (ret != -ENODEV)
serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ info.core = i;
+ arizona_call_notifiers(arizona,
+ ARIZONA_NOTIFY_VOICE_TRIGGER,
+ &info);
+ }
}
if (!serviced) {
@@ -1096,6 +1114,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644
index 000000000000..2c0d9c430a8c
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.c
@@ -0,0 +1,1143 @@
+/*
+ * cs53l30.c -- CS53l30 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@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/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs53l30.h"
+
+#define CS53L30_NUM_SUPPLIES 2
+static const char *const cs53l30_supply_names[CS53L30_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+};
+
+struct cs53l30_private {
+ struct regulator_bulk_data supplies[CS53L30_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *mute_gpio;
+ struct clk *mclk;
+ bool use_sdout2;
+ u32 mclk_rate;
+};
+
+static const struct reg_default cs53l30_reg_defaults[] = {
+ { CS53L30_PWRCTL, CS53L30_PWRCTL_DEFAULT },
+ { CS53L30_MCLKCTL, CS53L30_MCLKCTL_DEFAULT },
+ { CS53L30_INT_SR_CTL, CS53L30_INT_SR_CTL_DEFAULT },
+ { CS53L30_MICBIAS_CTL, CS53L30_MICBIAS_CTL_DEFAULT },
+ { CS53L30_ASPCFG_CTL, CS53L30_ASPCFG_CTL_DEFAULT },
+ { CS53L30_ASP_CTL1, CS53L30_ASP_CTL1_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL1, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL2, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL3, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_CTL4, CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN1, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN2, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN3, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN4, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN5, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_TDMTX_EN6, CS53L30_ASP_TDMTX_ENx_DEFAULT },
+ { CS53L30_ASP_CTL2, CS53L30_ASP_CTL2_DEFAULT },
+ { CS53L30_SFT_RAMP, CS53L30_SFT_RMP_DEFAULT },
+ { CS53L30_LRCK_CTL1, CS53L30_LRCK_CTLx_DEFAULT },
+ { CS53L30_LRCK_CTL2, CS53L30_LRCK_CTLx_DEFAULT },
+ { CS53L30_MUTEP_CTL1, CS53L30_MUTEP_CTL1_DEFAULT },
+ { CS53L30_MUTEP_CTL2, CS53L30_MUTEP_CTL2_DEFAULT },
+ { CS53L30_INBIAS_CTL1, CS53L30_INBIAS_CTL1_DEFAULT },
+ { CS53L30_INBIAS_CTL2, CS53L30_INBIAS_CTL2_DEFAULT },
+ { CS53L30_DMIC1_STR_CTL, CS53L30_DMIC1_STR_CTL_DEFAULT },
+ { CS53L30_DMIC2_STR_CTL, CS53L30_DMIC2_STR_CTL_DEFAULT },
+ { CS53L30_ADCDMIC1_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
+ { CS53L30_ADCDMIC1_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
+ { CS53L30_ADC1_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
+ { CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
+ { CS53L30_ADC1A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC1A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADC1B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADCDMIC2_CTL1, CS53L30_ADCDMICx_CTL1_DEFAULT },
+ { CS53L30_ADCDMIC2_CTL2, CS53L30_ADCDMIC1_CTL2_DEFAULT },
+ { CS53L30_ADC2_CTL3, CS53L30_ADCx_CTL3_DEFAULT },
+ { CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_CTL_DEFAULT },
+ { CS53L30_ADC2A_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_AFE_CTL_DEFAULT },
+ { CS53L30_ADC2A_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_ADC2B_DIG_VOL, CS53L30_ADCxy_DIG_VOL_DEFAULT },
+ { CS53L30_INT_MASK, CS53L30_DEVICE_INT_MASK },
+};
+
+static bool cs53l30_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg == CS53L30_IS)
+ return true;
+ else
+ return false;
+}
+
+static bool cs53l30_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS53L30_DEVID_AB:
+ case CS53L30_DEVID_CD:
+ case CS53L30_DEVID_E:
+ case CS53L30_REVID:
+ case CS53L30_IS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool cs53l30_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS53L30_DEVID_AB:
+ case CS53L30_DEVID_CD:
+ case CS53L30_DEVID_E:
+ case CS53L30_REVID:
+ case CS53L30_PWRCTL:
+ case CS53L30_MCLKCTL:
+ case CS53L30_INT_SR_CTL:
+ case CS53L30_MICBIAS_CTL:
+ case CS53L30_ASPCFG_CTL:
+ case CS53L30_ASP_CTL1:
+ case CS53L30_ASP_TDMTX_CTL1:
+ case CS53L30_ASP_TDMTX_CTL2:
+ case CS53L30_ASP_TDMTX_CTL3:
+ case CS53L30_ASP_TDMTX_CTL4:
+ case CS53L30_ASP_TDMTX_EN1:
+ case CS53L30_ASP_TDMTX_EN2:
+ case CS53L30_ASP_TDMTX_EN3:
+ case CS53L30_ASP_TDMTX_EN4:
+ case CS53L30_ASP_TDMTX_EN5:
+ case CS53L30_ASP_TDMTX_EN6:
+ case CS53L30_ASP_CTL2:
+ case CS53L30_SFT_RAMP:
+ case CS53L30_LRCK_CTL1:
+ case CS53L30_LRCK_CTL2:
+ case CS53L30_MUTEP_CTL1:
+ case CS53L30_MUTEP_CTL2:
+ case CS53L30_INBIAS_CTL1:
+ case CS53L30_INBIAS_CTL2:
+ case CS53L30_DMIC1_STR_CTL:
+ case CS53L30_DMIC2_STR_CTL:
+ case CS53L30_ADCDMIC1_CTL1:
+ case CS53L30_ADCDMIC1_CTL2:
+ case CS53L30_ADC1_CTL3:
+ case CS53L30_ADC1_NG_CTL:
+ case CS53L30_ADC1A_AFE_CTL:
+ case CS53L30_ADC1B_AFE_CTL:
+ case CS53L30_ADC1A_DIG_VOL:
+ case CS53L30_ADC1B_DIG_VOL:
+ case CS53L30_ADCDMIC2_CTL1:
+ case CS53L30_ADCDMIC2_CTL2:
+ case CS53L30_ADC2_CTL3:
+ case CS53L30_ADC2_NG_CTL:
+ case CS53L30_ADC2A_AFE_CTL:
+ case CS53L30_ADC2B_AFE_CTL:
+ case CS53L30_ADC2A_DIG_VOL:
+ case CS53L30_ADC2B_DIG_VOL:
+ case CS53L30_INT_MASK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0);
+static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1);
+static DECLARE_TLV_DB_SCALE(pga_preamp_tlv, 0, 10000, 0);
+
+static const char * const input1_sel_text[] = {
+ "DMIC1 On AB In",
+ "DMIC1 On A In",
+ "DMIC1 On B In",
+ "ADC1 On AB In",
+ "ADC1 On A In",
+ "ADC1 On B In",
+ "DMIC1 Off ADC1 Off",
+};
+
+static unsigned int const input1_sel_values[] = {
+ CS53L30_CH_TYPE,
+ CS53L30_ADCxB_PDN | CS53L30_CH_TYPE,
+ CS53L30_ADCxA_PDN | CS53L30_CH_TYPE,
+ CS53L30_DMICx_PDN,
+ CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input2_sel_text[] = {
+ "DMIC2 On AB In",
+ "DMIC2 On A In",
+ "DMIC2 On B In",
+ "ADC2 On AB In",
+ "ADC2 On A In",
+ "ADC2 On B In",
+ "DMIC2 Off ADC2 Off",
+};
+
+static unsigned int const input2_sel_values[] = {
+ 0x0,
+ CS53L30_ADCxB_PDN,
+ CS53L30_ADCxA_PDN,
+ CS53L30_DMICx_PDN,
+ CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+ CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input1_route_sel_text[] = {
+ "ADC1_SEL", "DMIC1_SEL",
+};
+
+static const struct soc_enum input1_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, CS53L30_CH_TYPE_SHIFT,
+ ARRAY_SIZE(input1_route_sel_text),
+ input1_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0,
+ CS53L30_ADCDMICx_PDN_MASK, input1_sel_text,
+ input1_sel_values);
+
+static const struct snd_kcontrol_new input1_route_sel_mux =
+ SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum);
+
+static const char * const input2_route_sel_text[] = {
+ "ADC2_SEL", "DMIC2_SEL",
+};
+
+/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */
+static const struct soc_enum input2_route_sel_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+ ARRAY_SIZE(input2_route_sel_text),
+ input2_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0,
+ CS53L30_ADCDMICx_PDN_MASK, input2_sel_text,
+ input2_sel_values);
+
+static const struct snd_kcontrol_new input2_route_sel_mux =
+ SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum);
+
+/*
+ * TB = 6144*(MCLK(int) scaling factor)/MCLK(internal)
+ * TB - Time base
+ * NOTE: If MCLK_INT_SCALE = 0, then TB=1
+ */
+static const char * const cs53l30_ng_delay_text[] = {
+ "TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms",
+};
+
+static const struct soc_enum adc1_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_delay_text),
+ cs53l30_ng_delay_text);
+
+static const struct soc_enum adc2_ng_delay_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_delay_text),
+ cs53l30_ng_delay_text);
+
+/* The noise gate threshold selected will depend on NG Boost */
+static const char * const cs53l30_ng_thres_text[] = {
+ "-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB",
+ "-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB",
+};
+
+static const struct soc_enum adc1_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_thres_text),
+ cs53l30_ng_thres_text);
+
+static const struct soc_enum adc2_ng_thres_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+ ARRAY_SIZE(cs53l30_ng_thres_text),
+ cs53l30_ng_thres_text);
+
+/* Corner frequencies are with an Fs of 48kHz. */
+static const char * const hpf_corner_freq_text[] = {
+ "1.86Hz", "120Hz", "235Hz", "466Hz",
+};
+
+static const struct soc_enum adc1_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct soc_enum adc2_hpf_enum =
+ SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+ ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
+ SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP,
+ CS53L30_DIGSFT_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1 Noise Gate Ganging Switch", CS53L30_ADC1_CTL3,
+ CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2 Noise Gate Ganging Switch", CS53L30_ADC2_CTL3,
+ CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1A Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCxA_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1B Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCxB_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2A Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCxA_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2B Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCxB_NG_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1 Notch Filter Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+ SOC_SINGLE("ADC2 Notch Filter Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+ SOC_SINGLE("ADC1A Invert Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxA_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC1B Invert Switch", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxB_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2A Invert Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxA_INV_SHIFT, 1, 0),
+ SOC_SINGLE("ADC2B Invert Switch", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxB_INV_SHIFT, 1, 0),
+
+ SOC_SINGLE_TLV("ADC1A Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1B Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+ CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2A Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC2B Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+ CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+ SOC_SINGLE_TLV("ADC1 NG Boost Volume", CS53L30_ADC1_NG_CTL,
+ CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+ SOC_SINGLE_TLV("ADC2 NG Boost Volume", CS53L30_ADC2_NG_CTL,
+ CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+
+ SOC_DOUBLE_R_TLV("ADC1 Preamplifier Volume", CS53L30_ADC1A_AFE_CTL,
+ CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+ 2, 0, pga_preamp_tlv),
+ SOC_DOUBLE_R_TLV("ADC2 Preamplifier Volume", CS53L30_ADC2A_AFE_CTL,
+ CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+ 2, 0, pga_preamp_tlv),
+
+ SOC_ENUM("Input 1 Channel Select", input1_sel_enum),
+ SOC_ENUM("Input 2 Channel Select", input2_sel_enum),
+
+ SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum),
+ SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum),
+ SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum),
+ SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum),
+ SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum),
+ SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
+
+ SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
+ CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
+ CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
+ CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
+ CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+
+ SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
+ CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
+ CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
+ CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
+ CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN1_DMIC1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3_DMIC2"),
+ SND_SOC_DAPM_INPUT("IN4"),
+ SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC1_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC2_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC3_BIAS_PDN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL,
+ CS53L30_MIC4_BIAS_PDN_SHIFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASP_SDOUT1", NULL, 0, CS53L30_ASP_CTL1,
+ CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+ SND_SOC_DAPM_AIF_OUT("ASP_SDOUT2", NULL, 0, CS53L30_ASP_CTL2,
+ CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+
+ SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0,
+ &input1_route_sel_mux),
+ SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0,
+ &input2_route_sel_mux),
+
+ SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_ADCxA_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_ADCxB_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_ADCxA_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_ADCxB_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1,
+ CS53L30_DMICx_PDN_SHIFT, 1),
+ SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1,
+ CS53L30_DMICx_PDN_SHIFT, 1),
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes[] = {
+ /* ADC Input Paths */
+ {"ADC1A", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "ADC1_SEL", "ADC1A"},
+ {"ADC1B", NULL, "IN2"},
+
+ {"ADC2A", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "ADC2_SEL", "ADC2A"},
+ {"ADC2B", NULL, "IN4"},
+
+ /* MIC Bias Paths */
+ {"ADC1A", NULL, "MIC1 Bias"},
+ {"ADC1B", NULL, "MIC2 Bias"},
+ {"ADC2A", NULL, "MIC3 Bias"},
+ {"ADC2B", NULL, "MIC4 Bias"},
+
+ /* DMIC Paths */
+ {"DMIC1", NULL, "IN1_DMIC1"},
+ {"Input Mux 1", "DMIC1_SEL", "DMIC1"},
+
+ {"DMIC2", NULL, "IN3_DMIC2"},
+ {"Input Mux 2", "DMIC2_SEL", "DMIC2"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout1[] = {
+ /* Output Paths when using SDOUT1 only */
+ {"ASP_SDOUT1", NULL, "ADC1A" },
+ {"ASP_SDOUT1", NULL, "Input Mux 1"},
+ {"ASP_SDOUT1", NULL, "ADC1B"},
+
+ {"ASP_SDOUT1", NULL, "ADC2A"},
+ {"ASP_SDOUT1", NULL, "Input Mux 2"},
+ {"ASP_SDOUT1", NULL, "ADC2B"},
+
+ {"Capture", NULL, "ASP_SDOUT1"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout2[] = {
+ /* Output Paths when using both SDOUT1 and SDOUT2 */
+ {"ASP_SDOUT1", NULL, "ADC1A" },
+ {"ASP_SDOUT1", NULL, "Input Mux 1"},
+ {"ASP_SDOUT1", NULL, "ADC1B"},
+
+ {"ASP_SDOUT2", NULL, "ADC2A"},
+ {"ASP_SDOUT2", NULL, "Input Mux 2"},
+ {"ASP_SDOUT2", NULL, "ADC2B"},
+
+ {"Capture", NULL, "ASP_SDOUT1"},
+ {"Capture", NULL, "ASP_SDOUT2"},
+};
+
+struct cs53l30_mclk_div {
+ u32 mclk_rate;
+ u32 srate;
+ u8 asp_rate;
+ u8 internal_fs_ratio;
+ u8 mclk_int_scale;
+};
+
+static struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = {
+ /* NOTE: Enable MCLK_INT_SCALE to save power. */
+
+ /* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */
+ {5644800, 11025, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {5644800, 22050, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {5644800, 44100, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+ {6000000, 8000, 0x1, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 11025, 0x2, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 12000, 0x4, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 16000, 0x5, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 22050, 0x6, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 24000, 0x8, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 32000, 0x9, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 44100, 0xA, 0, CS53L30_MCLK_INT_SCALE},
+ {6000000, 48000, 0xC, 0, CS53L30_MCLK_INT_SCALE},
+
+ {6144000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6144000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+ {6400000, 8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+ {6400000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+};
+
+struct cs53l30_mclkx_div {
+ u32 mclkx;
+ u8 ratio;
+ u8 mclkdiv;
+};
+
+static struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = {
+ {5644800, 1, CS53L30_MCLK_DIV_BY_1},
+ {6000000, 1, CS53L30_MCLK_DIV_BY_1},
+ {6144000, 1, CS53L30_MCLK_DIV_BY_1},
+ {11289600, 2, CS53L30_MCLK_DIV_BY_2},
+ {12288000, 2, CS53L30_MCLK_DIV_BY_2},
+ {12000000, 2, CS53L30_MCLK_DIV_BY_2},
+ {19200000, 3, CS53L30_MCLK_DIV_BY_3},
+};
+
+static int cs53l30_get_mclkx_coeff(int mclkx)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) {
+ if (cs53l30_mclkx_coeffs[i].mclkx == mclkx)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int cs53l30_get_mclk_coeff(int mclk_rate, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) {
+ if (cs53l30_mclk_coeffs[i].mclk_rate == mclk_rate &&
+ cs53l30_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ int mclkx_coeff;
+ u32 mclk_rate;
+
+ /* MCLKX -> MCLK */
+ mclkx_coeff = cs53l30_get_mclkx_coeff(freq);
+ if (mclkx_coeff < 0)
+ return mclkx_coeff;
+
+ mclk_rate = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx /
+ cs53l30_mclkx_coeffs[mclkx_coeff].ratio;
+
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIV_MASK,
+ cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv);
+
+ priv->mclk_rate = mclk_rate;
+
+ return 0;
+}
+
+static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ u8 aspcfg = 0, aspctl1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aspcfg |= CS53L30_ASP_MS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* Set TDM_PDN to turn off TDM mode -- Reset default */
+ aspctl1 |= CS53L30_ASP_TDM_PDN;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /*
+ * Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0
+ * with SHIFT_LEFT = 1 combination as Figure 4-13 shows in
+ * the CS53L30 datasheet
+ */
+ aspctl1 |= CS53L30_SHIFT_LEFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Check to see if the SCLK is inverted */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ aspcfg ^= CS53L30_ASP_SCLK_INV;
+ break;
+ default:
+ break;
+ }
+
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_MS | CS53L30_ASP_SCLK_INV, aspcfg);
+
+ regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_TDM_PDN | CS53L30_SHIFT_LEFT, aspctl1);
+
+ return 0;
+}
+
+static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ int srate = params_rate(params);
+ int mclk_coeff;
+
+ /* MCLK -> srate */
+ mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk_rate, srate);
+ if (mclk_coeff < 0)
+ return -EINVAL;
+
+ regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL,
+ CS53L30_INTRNL_FS_RATIO_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio);
+
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_INT_SCALE_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale);
+
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_RATE_MASK,
+ cs53l30_mclk_coeffs[mclk_coeff].asp_rate);
+
+ return 0;
+}
+
+static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+ int i, inter_max_check, ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_LP_MASK, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (dapm->bias_level == SND_SOC_BIAS_OFF) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(codec->dev,
+ "failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS_MASK, 0);
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK, 0);
+ msleep(50);
+ } else {
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK,
+ CS53L30_PDN_ULP);
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, 0);
+ /*
+ * If digital softramp is set, the amount of time required
+ * for power down increases and depends on the digital
+ * volume setting.
+ */
+
+ /* Set the max possible time if digsft is set */
+ regmap_read(priv->regmap, CS53L30_SFT_RAMP, &reg);
+ if (reg & CS53L30_DIGSFT_MASK)
+ inter_max_check = CS53L30_PDN_POLL_MAX;
+ else
+ inter_max_check = 10;
+
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_ULP_MASK,
+ CS53L30_PDN_ULP);
+ /* PDN_DONE will take a min of 20ms to be set.*/
+ msleep(20);
+ /* Clr status */
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ for (i = 0; i < inter_max_check; i++) {
+ if (inter_max_check < 10) {
+ usleep_range(1000, 1100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ } else {
+ usleep_range(10000, 10100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ }
+ }
+ /* PDN_DONE is set. We now can disable the MCLK */
+ regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+ CS53L30_PDN_DONE, CS53L30_PDN_DONE);
+ regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+ CS53L30_MCLK_DIS_MASK,
+ CS53L30_MCLK_DIS);
+ clk_disable_unprepare(priv->mclk);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ u8 val = tristate ? CS53L30_ASP_3ST : 0;
+
+ return regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_3ST_MASK, val);
+}
+
+static unsigned int const cs53l30_src_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list src_constraints = {
+ .count = ARRAY_SIZE(cs53l30_src_rates),
+ .list = cs53l30_src_rates,
+};
+
+static int cs53l30_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, &src_constraints);
+
+ return 0;
+}
+
+/*
+ * Note: CS53L30 counts the slot number per byte while ASoC counts the slot
+ * number per slot_width. So there is a difference between the slots of ASoC
+ * and the slots of CS53L30.
+ */
+static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ unsigned int loc[CS53L30_TDM_SLOT_MAX] = {48, 48, 48, 48};
+ unsigned int slot_next, slot_step;
+ u64 tx_enable = 0;
+ int i;
+
+ if (!rx_mask) {
+ dev_err(dai->dev, "rx masks must not be 0\n");
+ return -EINVAL;
+ }
+
+ /* Assuming slot_width is not supposed to be greater than 64 */
+ if (slots <= 0 || slot_width <= 0 || slot_width > 64) {
+ dev_err(dai->dev, "invalid slot number or slot width\n");
+ return -EINVAL;
+ }
+
+ if (slot_width & 0x7) {
+ dev_err(dai->dev, "slot width must count in byte\n");
+ return -EINVAL;
+ }
+
+ /* How many bytes in each ASoC slot */
+ slot_step = slot_width >> 3;
+
+ for (i = 0; rx_mask && i < CS53L30_TDM_SLOT_MAX; i++) {
+ /* Find the first slot from LSB */
+ slot_next = __ffs(rx_mask);
+ /* Save the slot location by converting to CS53L30 slot */
+ loc[i] = slot_next * slot_step;
+ /* Create the mask of CS53L30 slot */
+ tx_enable |= (u64)((u64)(1 << slot_step) - 1) << (u64)loc[i];
+ /* Clear this slot from rx_mask */
+ rx_mask &= ~(1 << slot_next);
+ }
+
+ /* Error out to avoid slot shift */
+ if (rx_mask && i == CS53L30_TDM_SLOT_MAX) {
+ dev_err(dai->dev, "rx_mask exceeds max slot number: %d\n",
+ CS53L30_TDM_SLOT_MAX);
+ return -EINVAL;
+ }
+
+ /* Validate the last active CS53L30 slot */
+ slot_next = loc[i - 1] + slot_step - 1;
+ if (slot_next > 47) {
+ dev_err(dai->dev, "slot selection out of bounds: %u\n",
+ slot_next);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < CS53L30_TDM_SLOT_MAX && loc[i] != 48; i++) {
+ regmap_update_bits(priv->regmap, CS53L30_ASP_TDMTX_CTL(i),
+ CS53L30_ASP_CHx_TX_LOC_MASK, loc[i]);
+ dev_dbg(dai->dev, "loc[%d]=%x\n", i, loc[i]);
+ }
+
+ for (i = 0; i < CS53L30_ASP_TDMTX_ENx_MAX && tx_enable; i++) {
+ regmap_write(priv->regmap, CS53L30_ASP_TDMTX_ENx(i),
+ tx_enable & 0xff);
+ tx_enable >>= 8;
+ dev_dbg(dai->dev, "en_reg=%x, tx_enable=%llx\n",
+ CS53L30_ASP_TDMTX_ENx(i), tx_enable & 0xff);
+ }
+
+ return 0;
+}
+
+static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+
+ if (priv->mute_gpio)
+ gpiod_set_value_cansleep(priv->mute_gpio, mute);
+
+ return 0;
+}
+
+/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs53l30_ops = {
+ .startup = cs53l30_pcm_startup,
+ .hw_params = cs53l30_pcm_hw_params,
+ .set_fmt = cs53l30_set_dai_fmt,
+ .set_sysclk = cs53l30_set_sysclk,
+ .set_tristate = cs53l30_set_tristate,
+ .set_tdm_slot = cs53l30_set_dai_tdm_slot,
+ .mute_stream = cs53l30_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs53l30_dai = {
+ .name = "cs53l30",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS53L30_RATES,
+ .formats = CS53L30_FORMATS,
+ },
+ .ops = &cs53l30_ops,
+ .symmetric_rates = 1,
+};
+
+static int cs53l30_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ if (priv->use_sdout2)
+ snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout2,
+ ARRAY_SIZE(cs53l30_dapm_routes_sdout2));
+ else
+ snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout1,
+ ARRAY_SIZE(cs53l30_dapm_routes_sdout1));
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver cs53l30_driver = {
+ .probe = cs53l30_codec_probe,
+ .set_bias_level = cs53l30_set_bias_level,
+ .idle_bias_off = true,
+
+ .dapm_widgets = cs53l30_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets),
+ .dapm_routes = cs53l30_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes),
+
+ .controls = cs53l30_snd_controls,
+ .num_controls = ARRAY_SIZE(cs53l30_snd_controls),
+};
+
+static struct regmap_config cs53l30_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS53L30_MAX_REGISTER,
+ .reg_defaults = cs53l30_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
+ .volatile_reg = cs53l30_volatile_register,
+ .writeable_reg = cs53l30_writeable_register,
+ .readable_reg = cs53l30_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs53l30_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct device_node *np = client->dev.of_node;
+ struct device *dev = &client->dev;
+ struct cs53l30_private *cs53l30;
+ unsigned int devid = 0;
+ unsigned int reg;
+ int ret = 0, i;
+ u8 val;
+
+ cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL);
+ if (!cs53l30)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(cs53l30->supplies); i++)
+ cs53l30->supplies[i].supply = cs53l30_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to get supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs53l30->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs53l30->reset_gpio)) {
+ ret = PTR_ERR(cs53l30->reset_gpio);
+ goto error;
+ }
+
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ i2c_set_clientdata(client, cs53l30);
+
+ cs53l30->mclk_rate = 0;
+
+ cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap);
+ if (IS_ERR(cs53l30->regmap)) {
+ ret = PTR_ERR(cs53l30->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ goto error;
+ }
+
+ /* Initialize codec */
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, &reg);
+ devid = reg << 12;
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, &reg);
+ devid |= reg << 4;
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS53L30_DEVID) {
+ ret = -ENODEV;
+ dev_err(dev, "Device ID (%X). Expected %X\n",
+ devid, CS53L30_DEVID);
+ goto error;
+ }
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_REVID, &reg);
+ if (ret < 0) {
+ dev_err(dev, "failed to get Revision ID: %d\n", ret);
+ goto error;
+ }
+
+ /* Check if MCLK provided */
+ cs53l30->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(cs53l30->mclk)) {
+ if (PTR_ERR(cs53l30->mclk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto error;
+ }
+ /* Otherwise mark the mclk pointer to NULL */
+ cs53l30->mclk = NULL;
+ }
+
+ /* Fetch the MUTE control */
+ cs53l30->mute_gpio = devm_gpiod_get_optional(dev, "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs53l30->mute_gpio)) {
+ ret = PTR_ERR(cs53l30->mute_gpio);
+ goto error;
+ }
+
+ if (cs53l30->mute_gpio) {
+ /* Enable MUTE controls via MUTE pin */
+ regmap_write(cs53l30->regmap, CS53L30_MUTEP_CTL1,
+ CS53L30_MUTEP_CTL1_MUTEALL);
+ /* Flip the polarity of MUTE pin */
+ if (gpiod_is_active_low(cs53l30->mute_gpio))
+ regmap_update_bits(cs53l30->regmap, CS53L30_MUTEP_CTL2,
+ CS53L30_MUTE_PIN_POLARITY, 0);
+ }
+
+ if (!of_property_read_u8(np, "cirrus,micbias-lvl", &val))
+ regmap_update_bits(cs53l30->regmap, CS53L30_MICBIAS_CTL,
+ CS53L30_MIC_BIAS_CTRL_MASK, val);
+
+ if (of_property_read_bool(np, "cirrus,use-sdout2"))
+ cs53l30->use_sdout2 = true;
+
+ dev_info(dev, "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
+
+ ret = snd_soc_register_codec(dev, &cs53l30_driver, &cs53l30_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register codec: %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ return ret;
+}
+
+static int cs53l30_i2c_remove(struct i2c_client *client)
+{
+ struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ /* Hold down reset */
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs53l30_runtime_suspend(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs53l30->regmap, true);
+
+ /* Hold down reset */
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+
+ return 0;
+}
+
+static int cs53l30_runtime_resume(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ regcache_cache_only(cs53l30->regmap, false);
+ ret = regcache_sync(cs53l30->regmap);
+ if (ret) {
+ dev_err(dev, "failed to synchronize regcache: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs53l30_of_match[] = {
+ { .compatible = "cirrus,cs53l30", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs53l30_of_match);
+
+static const struct i2c_device_id cs53l30_id[] = {
+ { "cs53l30", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+ .driver = {
+ .name = "cs53l30",
+ .pm = &cs53l30_runtime_pm,
+ },
+ .id_table = cs53l30_id,
+ .probe = cs53l30_i2c_probe,
+ .remove = cs53l30_i2c_remove,
+};
+
+module_i2c_driver(cs53l30_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS53L30 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
new file mode 100644
index 000000000000..5e39da568749
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.h
@@ -0,0 +1,459 @@
+/*
+ * ALSA SoC CS53L30 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@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 __CS53L30_H__
+#define __CS53L30_H__
+
+/* I2C Registers */
+#define CS53L30_DEVID_AB 0x01 /* Device ID A & B [RO]. */
+#define CS53L30_DEVID_CD 0x02 /* Device ID C & D [RO]. */
+#define CS53L30_DEVID_E 0x03 /* Device ID E [RO]. */
+#define CS53L30_REVID 0x05 /* Revision ID [RO]. */
+#define CS53L30_PWRCTL 0x06 /* Power Control. */
+#define CS53L30_MCLKCTL 0x07 /* MCLK Control. */
+#define CS53L30_INT_SR_CTL 0x08 /* Internal Sample Rate Control. */
+#define CS53L30_MICBIAS_CTL 0x0A /* Mic Bias Control. */
+#define CS53L30_ASPCFG_CTL 0x0C /* ASP Config Control. */
+#define CS53L30_ASP_CTL1 0x0D /* ASP1 Control. */
+#define CS53L30_ASP_TDMTX_CTL1 0x0E /* ASP1 TDM TX Control 1 */
+#define CS53L30_ASP_TDMTX_CTL2 0x0F /* ASP1 TDM TX Control 2 */
+#define CS53L30_ASP_TDMTX_CTL3 0x10 /* ASP1 TDM TX Control 3 */
+#define CS53L30_ASP_TDMTX_CTL4 0x11 /* ASP1 TDM TX Control 4 */
+#define CS53L30_ASP_TDMTX_EN1 0x12 /* ASP1 TDM TX Enable 1 */
+#define CS53L30_ASP_TDMTX_EN2 0x13 /* ASP1 TDM TX Enable 2 */
+#define CS53L30_ASP_TDMTX_EN3 0x14 /* ASP1 TDM TX Enable 3 */
+#define CS53L30_ASP_TDMTX_EN4 0x15 /* ASP1 TDM TX Enable 4 */
+#define CS53L30_ASP_TDMTX_EN5 0x16 /* ASP1 TDM TX Enable 5 */
+#define CS53L30_ASP_TDMTX_EN6 0x17 /* ASP1 TDM TX Enable 6 */
+#define CS53L30_ASP_CTL2 0x18 /* ASP2 Control. */
+#define CS53L30_SFT_RAMP 0x1A /* Soft Ramp Control. */
+#define CS53L30_LRCK_CTL1 0x1B /* LRCK Control 1. */
+#define CS53L30_LRCK_CTL2 0x1C /* LRCK Control 2. */
+#define CS53L30_MUTEP_CTL1 0x1F /* Mute Pin Control 1. */
+#define CS53L30_MUTEP_CTL2 0x20 /* Mute Pin Control 2. */
+#define CS53L30_INBIAS_CTL1 0x21 /* Input Bias Control 1. */
+#define CS53L30_INBIAS_CTL2 0x22 /* Input Bias Control 2. */
+#define CS53L30_DMIC1_STR_CTL 0x23 /* DMIC1 Stereo Control. */
+#define CS53L30_DMIC2_STR_CTL 0x24 /* DMIC2 Stereo Control. */
+#define CS53L30_ADCDMIC1_CTL1 0x25 /* ADC1/DMIC1 Control 1. */
+#define CS53L30_ADCDMIC1_CTL2 0x26 /* ADC1/DMIC1 Control 2. */
+#define CS53L30_ADC1_CTL3 0x27 /* ADC1 Control 3. */
+#define CS53L30_ADC1_NG_CTL 0x28 /* ADC1 Noise Gate Control. */
+#define CS53L30_ADC1A_AFE_CTL 0x29 /* ADC1A AFE Control. */
+#define CS53L30_ADC1B_AFE_CTL 0x2A /* ADC1B AFE Control. */
+#define CS53L30_ADC1A_DIG_VOL 0x2B /* ADC1A Digital Volume. */
+#define CS53L30_ADC1B_DIG_VOL 0x2C /* ADC1B Digital Volume. */
+#define CS53L30_ADCDMIC2_CTL1 0x2D /* ADC2/DMIC2 Control 1. */
+#define CS53L30_ADCDMIC2_CTL2 0x2E /* ADC2/DMIC2 Control 2. */
+#define CS53L30_ADC2_CTL3 0x2F /* ADC2 Control 3. */
+#define CS53L30_ADC2_NG_CTL 0x30 /* ADC2 Noise Gate Control. */
+#define CS53L30_ADC2A_AFE_CTL 0x31 /* ADC2A AFE Control. */
+#define CS53L30_ADC2B_AFE_CTL 0x32 /* ADC2B AFE Control. */
+#define CS53L30_ADC2A_DIG_VOL 0x33 /* ADC2A Digital Volume. */
+#define CS53L30_ADC2B_DIG_VOL 0x34 /* ADC2B Digital Volume. */
+#define CS53L30_INT_MASK 0x35 /* Interrupt Mask. */
+#define CS53L30_IS 0x36 /* Interrupt Status. */
+#define CS53L30_MAX_REGISTER 0x36
+
+#define CS53L30_TDM_SLOT_MAX 4
+#define CS53L30_ASP_TDMTX_CTL(x) (CS53L30_ASP_TDMTX_CTL1 + (x))
+/* x : index for registers; n : index for slot; 8 slots per register */
+#define CS53L30_ASP_TDMTX_ENx(x) (CS53L30_ASP_TDMTX_EN6 - (x))
+#define CS53L30_ASP_TDMTX_ENn(n) CS53L30_ASP_TDMTX_ENx((n) >> 3)
+#define CS53L30_ASP_TDMTX_ENx_MAX 6
+
+/* Device ID */
+#define CS53L30_DEVID 0x53A30
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define CS53L30_PDN_POLL_MAX 90
+
+/* Bitfield Definitions */
+
+/* R6 (0x06) CS53L30_PWRCTL - Power Control */
+#define CS53L30_PDN_ULP_SHIFT 7
+#define CS53L30_PDN_ULP_MASK (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_ULP (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_LP_SHIFT 6
+#define CS53L30_PDN_LP_MASK (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_PDN_LP (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_DISCHARGE_FILT_SHIFT 5
+#define CS53L30_DISCHARGE_FILT_MASK (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_DISCHARGE_FILT (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_THMS_PDN_SHIFT 4
+#define CS53L30_THMS_PDN_MASK (1 << CS53L30_THMS_PDN_SHIFT)
+#define CS53L30_THMS_PDN (1 << CS53L30_THMS_PDN_SHIFT)
+
+#define CS53L30_PWRCTL_DEFAULT (CS53L30_THMS_PDN)
+
+/* R7 (0x07) CS53L30_MCLKCTL - MCLK Control */
+#define CS53L30_MCLK_DIS_SHIFT 7
+#define CS53L30_MCLK_DIS_MASK (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_DIS (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_INT_SCALE_SHIFT 6
+#define CS53L30_MCLK_INT_SCALE_MASK (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_MCLK_INT_SCALE (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_DMIC_DRIVE_SHIFT 5
+#define CS53L30_DMIC_DRIVE_MASK (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_DMIC_DRIVE (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_MCLK_DIV_SHIFT 2
+#define CS53L30_MCLK_DIV_WIDTH 2
+#define CS53L30_MCLK_DIV_MASK (((1 << CS53L30_MCLK_DIV_WIDTH) - 1) << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_1 (0x0 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_2 (0x1 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_3 (0x2 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_SYNC_EN_SHIFT 1
+#define CS53L30_SYNC_EN_MASK (1 << CS53L30_SYNC_EN_SHIFT)
+#define CS53L30_SYNC_EN (1 << CS53L30_SYNC_EN_SHIFT)
+
+#define CS53L30_MCLKCTL_DEFAULT (CS53L30_MCLK_DIV_BY_2)
+
+/* R8 (0x08) CS53L30_INT_SR_CTL - Internal Sample Rate Control */
+#define CS53L30_INTRNL_FS_RATIO_SHIFT 4
+#define CS53L30_INTRNL_FS_RATIO_MASK (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_INTRNL_FS_RATIO (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN_SHIFT 0
+#define CS53L30_MCLK_19MHZ_EN_MASK (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+
+/* 0x6 << 1 is reserved bits */
+#define CS53L30_INT_SR_CTL_DEFAULT (CS53L30_INTRNL_FS_RATIO | 0x6 << 1)
+
+/* R10 (0x0A) CS53L30_MICBIAS_CTL - Mic Bias Control */
+#define CS53L30_MIC4_BIAS_PDN_SHIFT 7
+#define CS53L30_MIC4_BIAS_PDN_MASK (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC4_BIAS_PDN (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN_SHIFT 6
+#define CS53L30_MIC3_BIAS_PDN_MASK (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN_SHIFT 5
+#define CS53L30_MIC2_BIAS_PDN_MASK (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN_SHIFT 4
+#define CS53L30_MIC1_BIAS_PDN_MASK (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MICx_BIAS_PDN (0xf << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_VP_MIN_SHIFT 2
+#define CS53L30_VP_MIN_MASK (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_VP_MIN (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_SHIFT 0
+#define CS53L30_MIC_BIAS_CTRL_WIDTH 2
+#define CS53L30_MIC_BIAS_CTRL_MASK (((1 << CS53L30_MIC_BIAS_CTRL_WIDTH) - 1) << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_HIZ (0 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_1V8 (1 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_2V75 (2 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+
+#define CS53L30_MICBIAS_CTL_DEFAULT (CS53L30_MICx_BIAS_PDN | CS53L30_VP_MIN)
+
+/* R12 (0x0C) CS53L30_ASPCFG_CTL - ASP Configuration Control */
+#define CS53L30_ASP_MS_SHIFT 7
+#define CS53L30_ASP_MS_MASK (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_MS (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_SCLK_INV_SHIFT 4
+#define CS53L30_ASP_SCLK_INV_MASK (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_SCLK_INV (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_RATE_SHIFT 0
+#define CS53L30_ASP_RATE_WIDTH 4
+#define CS53L30_ASP_RATE_MASK (((1 << CS53L30_ASP_RATE_WIDTH) - 1) << CS53L30_ASP_RATE_SHIFT)
+#define CS53L30_ASP_RATE_48K (0xc << CS53L30_ASP_RATE_SHIFT)
+
+#define CS53L30_ASPCFG_CTL_DEFAULT (CS53L30_ASP_RATE_48K)
+
+/* R13/R24 (0x0D/0x18) CS53L30_ASP_CTL1 & CS53L30_ASP_CTL2 - ASP Control 1~2 */
+#define CS53L30_ASP_TDM_PDN_SHIFT 7
+#define CS53L30_ASP_TDM_PDN_MASK (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_TDM_PDN (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN_SHIFT 6
+#define CS53L30_ASP_SDOUTx_PDN_MASK (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_3ST_SHIFT 5
+#define CS53L30_ASP_3ST_MASK (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_ASP_3ST (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_SHIFT_LEFT_SHIFT 4
+#define CS53L30_SHIFT_LEFT_MASK (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_SHIFT_LEFT (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE_SHIFT 0
+#define CS53L30_ASP_SDOUTx_DRIVE_MASK (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+
+#define CS53L30_ASP_CTL1_DEFAULT (CS53L30_ASP_TDM_PDN)
+#define CS53L30_ASP_CTL2_DEFAULT (0)
+
+/* R14 (0x0E) ~ R17 (0x11) CS53L30_ASP_TDMTX_CTLx - ASP TDM TX Control 1~4 */
+#define CS53L30_ASP_CHx_TX_STATE_SHIFT 7
+#define CS53L30_ASP_CHx_TX_STATE_MASK (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_STATE (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_SHIFT 0
+#define CS53L30_ASP_CHx_TX_LOC_WIDTH 6
+#define CS53L30_ASP_CHx_TX_LOC_MASK (((1 << CS53L30_ASP_CHx_TX_LOC_WIDTH) - 1) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_MAX (47 << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC(x) ((x) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+
+#define CS53L30_ASP_TDMTX_CTLx_DEFAULT (CS53L30_ASP_CHx_TX_LOC_MAX)
+
+/* R18 (0x12) ~ R23 (0x17) CS53L30_ASP_TDMTX_ENx - ASP TDM TX Enable 1~6 */
+#define CS53L30_ASP_TDMTX_ENx_DEFAULT (0)
+
+/* R26 (0x1A) CS53L30_SFT_RAMP - Soft Ramp Control */
+#define CS53L30_DIGSFT_SHIFT 5
+#define CS53L30_DIGSFT_MASK (1 << CS53L30_DIGSFT_SHIFT)
+#define CS53L30_DIGSFT (1 << CS53L30_DIGSFT_SHIFT)
+
+#define CS53L30_SFT_RMP_DEFAULT (0)
+
+/* R28 (0x1C) CS53L30_LRCK_CTL2 - LRCK Control 2 */
+#define CS53L30_LRCK_50_NPW_SHIFT 3
+#define CS53L30_LRCK_50_NPW_MASK (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_50_NPW (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_TPWH_SHIFT 0
+#define CS53L30_LRCK_TPWH_WIDTH 3
+#define CS53L30_LRCK_TPWH_MASK (((1 << CS53L30_LRCK_TPWH_WIDTH) - 1) << CS53L30_LRCK_TPWH_SHIFT)
+#define CS53L30_LRCK_TPWH(x) (((x) << CS53L30_LRCK_TPWH_SHIFT) & CS53L30_LRCK_TPWH_MASK)
+
+#define CS53L30_LRCK_CTLx_DEFAULT (0)
+
+/* R31 (0x1F) CS53L30_MUTEP_CTL1 - MUTE Pin Control 1 */
+#define CS53L30_MUTE_PDN_ULP_SHIFT 7
+#define CS53L30_MUTE_PDN_ULP_MASK (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_ULP (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_LP_SHIFT 6
+#define CS53L30_MUTE_PDN_LP_MASK (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_PDN_LP (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_M4B_PDN_SHIFT 4
+#define CS53L30_MUTE_M4B_PDN_MASK (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M4B_PDN (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN_SHIFT 3
+#define CS53L30_MUTE_M3B_PDN_MASK (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN_SHIFT 2
+#define CS53L30_MUTE_M2B_PDN_MASK (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN_SHIFT 1
+#define CS53L30_MUTE_M1B_PDN_MASK (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_MxB_PDN_SHIFT(x) (CS53L30_MUTE_M1B_PDN_SHIFT + (x))
+#define CS53L30_MUTE_MxB_PDN_MASK(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MxB_PDN(x) (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MB_ALL_PDN_SHIFT 0
+#define CS53L30_MUTE_MB_ALL_PDN_MASK (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+#define CS53L30_MUTE_MB_ALL_PDN (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL1_MUTEALL (0xdf)
+#define CS53L30_MUTEP_CTL1_DEFAULT (0)
+
+/* R32 (0x20) CS53L30_MUTEP_CTL2 - MUTE Pin Control 2 */
+#define CS53L30_MUTE_PIN_POLARITY_SHIFT 7
+#define CS53L30_MUTE_PIN_POLARITY_MASK (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_PIN_POLARITY (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN_SHIFT 6
+#define CS53L30_MUTE_ASP_TDM_PDN_MASK (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT 5
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT 4
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x) ((x) + CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_MASK(x) (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ASP_SDOUTx_PDN (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ADC2B_PDN_SHIFT 3
+#define CS53L30_MUTE_ADC2B_PDN_MASK (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2B_PDN (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN_SHIFT 2
+#define CS53L30_MUTE_ADC2A_PDN_MASK (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN_SHIFT 1
+#define CS53L30_MUTE_ADC1B_PDN_MASK (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN_SHIFT 0
+#define CS53L30_MUTE_ADC1A_PDN_MASK (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL2_DEFAULT (CS53L30_MUTE_PIN_POLARITY)
+
+/* R33 (0x21) CS53L30_INBIAS_CTL1 - Input Bias Control 1 */
+#define CS53L30_IN4M_BIAS_SHIFT 6
+#define CS53L30_IN4M_BIAS_WIDTH 2
+#define CS53L30_IN4M_BIAS_MASK (((1 << CS53L30_IN4M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_OPEN (0 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_PULL_DOWN (1 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_VCM (2 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_SHIFT 4
+#define CS53L30_IN4P_BIAS_WIDTH 2
+#define CS53L30_IN4P_BIAS_MASK (((1 << CS53L30_IN4P_BIAS_WIDTH) - 1) << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_OPEN (0 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_PULL_DOWN (1 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_VCM (2 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_SHIFT 2
+#define CS53L30_IN3M_BIAS_WIDTH 2
+#define CS53L30_IN3M_BIAS_MASK (((1 << CS53L30_IN3M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_OPEN (0 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_PULL_DOWN (1 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_VCM (2 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_SHIFT 0
+#define CS53L30_IN3P_BIAS_WIDTH 2
+#define CS53L30_IN3P_BIAS_MASK (((1 << CS53L30_IN3P_BIAS_WIDTH) - 1) << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_OPEN (0 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_PULL_DOWN (1 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_VCM (2 << CS53L30_IN3P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL1_DEFAULT (CS53L30_IN4M_BIAS_VCM | CS53L30_IN4P_BIAS_VCM |\
+ CS53L30_IN3M_BIAS_VCM | CS53L30_IN3P_BIAS_VCM)
+
+/* R34 (0x22) CS53L30_INBIAS_CTL2 - Input Bias Control 2 */
+#define CS53L30_IN2M_BIAS_SHIFT 6
+#define CS53L30_IN2M_BIAS_WIDTH 2
+#define CS53L30_IN2M_BIAS_MASK (((1 << CS53L30_IN2M_BIAS_WIDTH) - 1) << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_OPEN (0 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_PULL_DOWN (1 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_VCM (2 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_SHIFT 4
+#define CS53L30_IN2P_BIAS_WIDTH 2
+#define CS53L30_IN2P_BIAS_MASK (((1 << CS53L30_IN2P_BIAS_WIDTH) - 1) << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_OPEN (0 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_PULL_DOWN (1 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_VCM (2 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_SHIFT 2
+#define CS53L30_IN1M_BIAS_WIDTH 2
+#define CS53L30_IN1M_BIAS_MASK (((1 << CS53L30_IN1M_BIAS_WIDTH) - 1) << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_OPEN (0 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_PULL_DOWN (1 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_VCM (2 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_SHIFT 0
+#define CS53L30_IN1P_BIAS_WIDTH 2
+#define CS53L30_IN1P_BIAS_MASK (((1 << CS53L30_IN1P_BIAS_WIDTH) - 1) << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_OPEN (0 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_PULL_DOWN (1 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_VCM (2 << CS53L30_IN1P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL2_DEFAULT (CS53L30_IN2M_BIAS_VCM | CS53L30_IN2P_BIAS_VCM |\
+ CS53L30_IN1M_BIAS_VCM | CS53L30_IN1P_BIAS_VCM)
+
+/* R35 (0x23) & R36 (0x24) CS53L30_DMICx_STR_CTL - DMIC1 & DMIC2 Stereo Control */
+#define CS53L30_DMICx_STEREO_ENB_SHIFT 5
+#define CS53L30_DMICx_STEREO_ENB_MASK (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+#define CS53L30_DMICx_STEREO_ENB (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+
+/* 0x88 and 0xCC are reserved bits */
+#define CS53L30_DMIC1_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0x88)
+#define CS53L30_DMIC2_STR_CTL_DEFAULT (CS53L30_DMICx_STEREO_ENB | 0xCC)
+
+/* R37/R45 (0x25/0x2D) CS53L30_ADCDMICx_CTL1 - ADC1/DMIC1 & ADC2/DMIC2 Control 1 */
+#define CS53L30_ADCxB_PDN_SHIFT 7
+#define CS53L30_ADCxB_PDN_MASK (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxB_PDN (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN_SHIFT 6
+#define CS53L30_ADCxA_PDN_MASK (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_DMICx_PDN_SHIFT 2
+#define CS53L30_DMICx_PDN_MASK (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_PDN (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV_SHIFT 1
+#define CS53L30_DMICx_SCLK_DIV_MASK (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_CH_TYPE_SHIFT 0
+#define CS53L30_CH_TYPE_MASK (1 << CS53L30_CH_TYPE_SHIFT)
+#define CS53L30_CH_TYPE (1 << CS53L30_CH_TYPE_SHIFT)
+
+#define CS53L30_ADCDMICx_PDN_MASK 0xFF
+#define CS53L30_ADCDMICx_CTL1_DEFAULT (CS53L30_DMICx_PDN)
+
+/* R38/R46 (0x26/0x2E) CS53L30_ADCDMICx_CTL2 - ADC1/DMIC1 & ADC2/DMIC2 Control 2 */
+#define CS53L30_ADCx_NOTCH_DIS_SHIFT 7
+#define CS53L30_ADCx_NOTCH_DIS_MASK (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCx_NOTCH_DIS (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCxB_INV_SHIFT 5
+#define CS53L30_ADCxB_INV_MASK (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxB_INV (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxA_INV_SHIFT 4
+#define CS53L30_ADCxA_INV_MASK (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxA_INV (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST_SHIFT 1
+#define CS53L30_ADCxB_DIG_BOOST_MASK (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST_SHIFT 0
+#define CS53L30_ADCxA_DIG_BOOST_MASK (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+
+#define CS53L30_ADCDMIC1_CTL2_DEFAULT (0)
+
+/* R39/R47 (0x27/0x2F) CS53L30_ADCx_CTL3 - ADC1/ADC2 Control 3 */
+#define CS53L30_ADCx_HPF_EN_SHIFT 3
+#define CS53L30_ADCx_HPF_EN_MASK (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_EN (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_CF_SHIFT 1
+#define CS53L30_ADCx_HPF_CF_WIDTH 2
+#define CS53L30_ADCx_HPF_CF_MASK (((1 << CS53L30_ADCx_HPF_CF_WIDTH) - 1) << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_1HZ86 (0 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_120HZ (1 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_235HZ (2 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_466HZ (3 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_NG_ALL_SHIFT 0
+#define CS53L30_ADCx_NG_ALL_MASK (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+#define CS53L30_ADCx_NG_ALL (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+
+#define CS53L30_ADCx_CTL3_DEFAULT (CS53L30_ADCx_HPF_EN)
+
+/* R40/R48 (0x28/0x30) CS53L30_ADCx_NG_CTL - ADC1/ADC2 Noise Gate Control */
+#define CS53L30_ADCxB_NG_SHIFT 7
+#define CS53L30_ADCxB_NG_MASK (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxB_NG (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxA_NG_SHIFT 6
+#define CS53L30_ADCxA_NG_MASK (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCxA_NG (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCx_NG_BOOST_SHIFT 5
+#define CS53L30_ADCx_NG_BOOST_MASK (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_BOOST (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_THRESH_SHIFT 2
+#define CS53L30_ADCx_NG_THRESH_WIDTH 3
+#define CS53L30_ADCx_NG_THRESH_MASK (((1 << CS53L30_ADCx_NG_THRESH_WIDTH) - 1) << CS53L30_ADCx_NG_THRESH_SHIFT)
+#define CS53L30_ADCx_NG_DELAY_SHIFT 0
+#define CS53L30_ADCx_NG_DELAY_WIDTH 2
+#define CS53L30_ADCx_NG_DELAY_MASK (((1 << CS53L30_ADCx_NG_DELAY_WIDTH) - 1) << CS53L30_ADCx_NG_DELAY_SHIFT)
+
+#define CS53L30_ADCx_NG_CTL_DEFAULT (0)
+
+/* R41/R42/R49/R50 (0x29/0x2A/0x31/0x32) CS53L30_ADCxy_AFE_CTL - ADC1A/1B/2A/2B AFE Control */
+#define CS53L30_ADCxy_PREAMP_SHIFT 6
+#define CS53L30_ADCxy_PREAMP_WIDTH 2
+#define CS53L30_ADCxy_PREAMP_MASK (((1 << CS53L30_ADCxy_PREAMP_WIDTH) - 1) << CS53L30_ADCxy_PREAMP_SHIFT)
+#define CS53L30_ADCxy_PGA_VOL_SHIFT 0
+#define CS53L30_ADCxy_PGA_VOL_WIDTH 6
+#define CS53L30_ADCxy_PGA_VOL_MASK (((1 << CS53L30_ADCxy_PGA_VOL_WIDTH) - 1) << CS53L30_ADCxy_PGA_VOL_SHIFT)
+
+#define CS53L30_ADCxy_AFE_CTL_DEFAULT (0)
+
+/* R43/R44/R51/R52 (0x2B/0x2C/0x33/0x34) CS53L30_ADCxy_DIG_VOL - ADC1A/1B/2A/2B Digital Volume */
+#define CS53L30_ADCxy_VOL_MUTE (0x80)
+
+#define CS53L30_ADCxy_DIG_VOL_DEFAULT (0x0)
+
+/* CS53L30_INT */
+#define CS53L30_PDN_DONE (1 << 7)
+#define CS53L30_THMS_TRIP (1 << 6)
+#define CS53L30_SYNC_DONE (1 << 5)
+#define CS53L30_ADC2B_OVFL (1 << 4)
+#define CS53L30_ADC2A_OVFL (1 << 3)
+#define CS53L30_ADC1B_OVFL (1 << 2)
+#define CS53L30_ADC1A_OVFL (1 << 1)
+#define CS53L30_MUTE_PIN (1 << 0)
+#define CS53L30_DEVICE_INT_MASK 0xFF
+
+#endif /* __CS53L30_H__ */
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index 9459593eef13..f0057cd223a4 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -13,8 +13,8 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
#include <linux/pm_wakeirq.h>
#include <linux/slab.h>
#include <linux/delay.h>
@@ -382,11 +382,11 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
}
/*
- * DT to pdata conversion
+ * DT/ACPI to pdata conversion
*/
static enum da7219_aad_micbias_pulse_lvl
- da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 2800:
@@ -400,7 +400,7 @@ static enum da7219_aad_micbias_pulse_lvl
}
static enum da7219_aad_btn_cfg
- da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_cfg(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 2:
@@ -424,7 +424,7 @@ static enum da7219_aad_btn_cfg
}
static enum da7219_aad_mic_det_thr
- da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_mic_det_thr(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 200:
@@ -442,7 +442,7 @@ static enum da7219_aad_mic_det_thr
}
static enum da7219_aad_jack_ins_deb
- da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 5:
@@ -468,7 +468,7 @@ static enum da7219_aad_jack_ins_deb
}
static enum da7219_aad_jack_det_rate
- da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+ da7219_aad_fw_jack_det_rate(struct snd_soc_codec *codec, const char *str)
{
if (!strcmp(str, "32ms_64ms")) {
return DA7219_AAD_JACK_DET_RATE_32_64MS;
@@ -485,7 +485,7 @@ static enum da7219_aad_jack_det_rate
}
static enum da7219_aad_jack_rem_deb
- da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 1:
@@ -503,7 +503,7 @@ static enum da7219_aad_jack_rem_deb
}
static enum da7219_aad_btn_avg
- da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_avg(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 1:
@@ -521,7 +521,7 @@ static enum da7219_aad_btn_avg
}
static enum da7219_aad_adc_1bit_rpt
- da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 1:
@@ -538,97 +538,96 @@ static enum da7219_aad_adc_1bit_rpt
}
}
-static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec)
+static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_codec *codec)
{
- struct device_node *np = codec->dev->of_node;
- struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+ struct device *dev = codec->dev;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct fwnode_handle *aad_np;
struct da7219_aad_pdata *aad_pdata;
- const char *of_str;
- u32 of_val32;
+ const char *fw_str;
+ u32 fw_val32;
+ aad_np = device_get_named_child_node(dev, "da7219_aad");
if (!aad_np)
return NULL;
aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
if (!aad_pdata)
- goto out;
+ return NULL;
- aad_pdata->irq = irq_of_parse_and_map(np, 0);
+ aad_pdata->irq = i2c->irq;
- if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
- &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+ &fw_val32) >= 0)
aad_pdata->micbias_pulse_lvl =
- da7219_aad_of_micbias_pulse_lvl(codec, of_val32);
+ da7219_aad_fw_micbias_pulse_lvl(codec, fw_val32);
else
aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
- if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time",
- &of_val32) >= 0)
- aad_pdata->micbias_pulse_time = of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+ &fw_val32) >= 0)
+ aad_pdata->micbias_pulse_time = fw_val32;
- if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0)
- aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32);
+ if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
+ aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(codec, fw_val32);
else
aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
- if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
aad_pdata->mic_det_thr =
- da7219_aad_of_mic_det_thr(codec, of_val32);
+ da7219_aad_fw_mic_det_thr(codec, fw_val32);
else
aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
- if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
aad_pdata->jack_ins_deb =
- da7219_aad_of_jack_ins_deb(codec, of_val32);
+ da7219_aad_fw_jack_ins_deb(codec, fw_val32);
else
aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
- if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str))
+ if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
aad_pdata->jack_det_rate =
- da7219_aad_of_jack_det_rate(codec, of_str);
+ da7219_aad_fw_jack_det_rate(codec, fw_str);
else
aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
- if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
aad_pdata->jack_rem_deb =
- da7219_aad_of_jack_rem_deb(codec, of_val32);
+ da7219_aad_fw_jack_rem_deb(codec, fw_val32);
else
aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
- if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0)
- aad_pdata->a_d_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0)
+ aad_pdata->a_d_btn_thr = (u8) fw_val32;
else
aad_pdata->a_d_btn_thr = 0xA;
- if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0)
- aad_pdata->d_b_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0)
+ aad_pdata->d_b_btn_thr = (u8) fw_val32;
else
aad_pdata->d_b_btn_thr = 0x16;
- if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0)
- aad_pdata->b_c_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0)
+ aad_pdata->b_c_btn_thr = (u8) fw_val32;
else
aad_pdata->b_c_btn_thr = 0x21;
- if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0)
- aad_pdata->c_mic_btn_thr = (u8) of_val32;
+ if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0)
+ aad_pdata->c_mic_btn_thr = (u8) fw_val32;
else
aad_pdata->c_mic_btn_thr = 0x3E;
- if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0)
- aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32);
+ if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
+ aad_pdata->btn_avg = da7219_aad_fw_btn_avg(codec, fw_val32);
else
aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
- if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0)
+ if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
aad_pdata->adc_1bit_rpt =
- da7219_aad_of_adc_1bit_rpt(codec, of_val32);
+ da7219_aad_fw_adc_1bit_rpt(codec, fw_val32);
else
aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
-out:
- of_node_put(aad_np);
-
return aad_pdata;
}
@@ -769,9 +768,9 @@ int da7219_aad_init(struct snd_soc_codec *codec)
da7219->aad = da7219_aad;
da7219_aad->codec = codec;
- /* Handle any DT/platform data */
- if ((codec->dev->of_node) && (da7219->pdata))
- da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec);
+ /* Handle any DT/ACPI/platform data */
+ if (da7219->pdata && !da7219->pdata->aad_pdata)
+ da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(codec);
da7219_aad_handle_pdata(codec);
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 5c93899f1f0e..50ea94317cb3 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/of_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pm.h>
@@ -1418,7 +1419,7 @@ static struct snd_soc_dai_driver da7219_dai = {
/*
- * DT
+ * DT/ACPI
*/
static const struct of_device_id da7219_of_match[] = {
@@ -1434,7 +1435,7 @@ static const struct acpi_device_id da7219_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
static enum da7219_micbias_voltage
- da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+ da7219_fw_micbias_lvl(struct device *dev, u32 val)
{
switch (val) {
case 1600:
@@ -1450,13 +1451,13 @@ static enum da7219_micbias_voltage
case 2600:
return DA7219_MICBIAS_2_6V;
default:
- dev_warn(codec->dev, "Invalid micbias level");
+ dev_warn(dev, "Invalid micbias level");
return DA7219_MICBIAS_2_2V;
}
}
static enum da7219_mic_amp_in_sel
- da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+ da7219_fw_mic_amp_in_sel(struct device *dev, const char *str)
{
if (!strcmp(str, "diff")) {
return DA7219_MIC_AMP_IN_SEL_DIFF;
@@ -1465,29 +1466,29 @@ static enum da7219_mic_amp_in_sel
} else if (!strcmp(str, "se_n")) {
return DA7219_MIC_AMP_IN_SEL_SE_N;
} else {
- dev_warn(codec->dev, "Invalid mic input type selection");
+ dev_warn(dev, "Invalid mic input type selection");
return DA7219_MIC_AMP_IN_SEL_DIFF;
}
}
-static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
+static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_codec *codec)
{
- struct device_node *np = codec->dev->of_node;
+ struct device *dev = codec->dev;
struct da7219_pdata *pdata;
const char *of_str;
u32 of_val32;
- pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
- if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
- pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
+ if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
+ pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
else
pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
- if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str))
- pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str);
+ if (!device_property_read_string(dev, "dlg,mic-amp-in-sel", &of_str))
+ pdata->mic_amp_in_sel = da7219_fw_mic_amp_in_sel(dev, of_str);
else
pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
@@ -1662,11 +1663,10 @@ static int da7219_probe(struct snd_soc_codec *codec)
break;
}
- /* Handle DT/Platform data */
- if (codec->dev->of_node)
- da7219->pdata = da7219_of_to_pdata(codec);
- else
- da7219->pdata = dev_get_platdata(codec->dev);
+ /* Handle DT/ACPI/Platform data */
+ da7219->pdata = dev_get_platdata(codec->dev);
+ if (!da7219->pdata)
+ da7219->pdata = da7219_fw_to_pdata(codec);
da7219_handle_pdata(codec);
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 2abb742fc47b..4e181b270d95 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1124,8 +1124,10 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
}
hdac_hdmi_parse_eld(edev, pin);
- print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
- pin->eld.eld_buffer, pin->eld.eld_size);
+ print_hex_dump_debug("ELD: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ pin->eld.eld_buffer, pin->eld.eld_size,
+ true);
} else {
pin->eld.monitor_present = false;
pin->eld.eld_valid = false;
@@ -1816,6 +1818,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = {
static const struct hda_device_id hdmi_list[] = {
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
{}
};
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index 8e36e883e453..f27d115626db 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -112,7 +112,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
return ret;
if (hcp->hcd.ops->audio_startup) {
- ret = hcp->hcd.ops->audio_startup(dai->dev->parent);
+ ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data);
if (ret) {
mutex_lock(&hcp->current_stream_lock);
hcp->current_stream = NULL;
@@ -122,8 +122,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
}
if (hcp->hcd.ops->get_eld) {
- ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld,
- sizeof(hcp->eld));
+ ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
if (!ret) {
ret = snd_pcm_hw_constraint_eld(substream->runtime,
@@ -144,7 +144,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
WARN_ON(hcp->current_stream != substream);
- hcp->hcd.ops->audio_shutdown(dai->dev->parent);
+ hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
mutex_lock(&hcp->current_stream_lock);
hcp->current_stream = NULL;
@@ -195,8 +195,8 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
hp.sample_rate = params_rate(params);
hp.channels = params_channels(params);
- return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id],
- &hp);
+ return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
+ &hcp->daifmt[dai->id], &hp);
}
static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
@@ -280,7 +280,8 @@ static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
dev_dbg(dai->dev, "%s()\n", __func__);
if (hcp->hcd.ops->digital_mute)
- return hcp->hcd.ops->digital_mute(dai->dev->parent, mute);
+ return hcp->hcd.ops->digital_mute(dai->dev->parent,
+ hcp->hcd.data, mute);
return 0;
}
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
new file mode 100644
index 000000000000..a7320e709890
--- /dev/null
+++ b/sound/soc/codecs/max98504.c
@@ -0,0 +1,383 @@
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2013 - 2014 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+
+#include "max98504.h"
+
+static const char * const max98504_supply_names[] = {
+ "DVDD",
+ "DIOVDD",
+ "PVDD",
+};
+#define MAX98504_NUM_SUPPLIES ARRAY_SIZE(max98504_supply_names)
+
+struct max98504_priv {
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[MAX98504_NUM_SUPPLIES];
+ unsigned int pcm_rx_channels;
+ bool brownout_enable;
+ unsigned int brownout_threshold;
+ unsigned int brownout_attenuation;
+ unsigned int brownout_attack_hold;
+ unsigned int brownout_timed_hold;
+ unsigned int brownout_release_rate;
+};
+
+static struct reg_default max98504_reg_defaults[] = {
+ { 0x01, 0},
+ { 0x02, 0},
+ { 0x03, 0},
+ { 0x04, 0},
+ { 0x10, 0},
+ { 0x11, 0},
+ { 0x12, 0},
+ { 0x13, 0},
+ { 0x14, 0},
+ { 0x15, 0},
+ { 0x16, 0},
+ { 0x17, 0},
+ { 0x18, 0},
+ { 0x19, 0},
+ { 0x1A, 0},
+ { 0x20, 0},
+ { 0x21, 0},
+ { 0x22, 0},
+ { 0x23, 0},
+ { 0x24, 0},
+ { 0x25, 0},
+ { 0x26, 0},
+ { 0x27, 0},
+ { 0x28, 0},
+ { 0x30, 0},
+ { 0x31, 0},
+ { 0x32, 0},
+ { 0x33, 0},
+ { 0x34, 0},
+ { 0x35, 0},
+ { 0x36, 0},
+ { 0x37, 0},
+ { 0x38, 0},
+ { 0x39, 0},
+ { 0x40, 0},
+ { 0x41, 0},
+};
+
+static bool max98504_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_INTERRUPT_STATUS:
+ case MAX98504_INTERRUPT_FLAGS:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_GLOBAL_ENABLE:
+ case MAX98504_SOFTWARE_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98504_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_SOFTWARE_RESET:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE,
+ max98504->pcm_rx_channels);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int max98504_component_probe(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+ struct regmap *map = max98504->regmap;
+ int ret;
+
+ ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+ if (ret < 0)
+ return ret;
+
+ regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1);
+ msleep(20);
+
+ if (!max98504->brownout_enable)
+ return 0;
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1,
+ (max98504->brownout_threshold & 0x1f) << 3 |
+ (max98504->brownout_attenuation & 0x3));
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2,
+ max98504->brownout_attack_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3,
+ max98504->brownout_timed_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4,
+ max98504->brownout_release_rate & 0xff);
+
+ return 0;
+}
+
+static void max98504_component_remove(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+}
+
+static const char *spk_source_mux_text[] = {
+ "PCM Monomix", "Analog In", "PDM Left", "PDM Right"
+};
+
+static const struct soc_enum spk_source_mux_enum =
+ SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT,
+ 0, ARRAY_SIZE(spk_source_mux_text),
+ spk_source_mux_text);
+
+static const struct snd_kcontrol_new spk_source_mux =
+ SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum);
+
+static const struct snd_soc_dapm_route max98504_dapm_routes[] = {
+ { "SPKOUT", NULL, "Global Enable" },
+ { "SPK Source", "PCM Monomix", "DAC PCM" },
+ { "SPK Source", "Analog In", "AIN" },
+ { "SPK Source", "PDM Left", "DAC PDM" },
+ { "SPK Source", "PDM Right", "DAC PDM" },
+};
+
+static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_INPUT("AIN"),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0,
+ max98504_pcm_rx_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0),
+ SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux),
+ SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT",
+ MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0),
+};
+
+static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask);
+ max98504->pcm_rx_channels = rx_mask;
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ return 0;
+}
+static int max98504_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+ unsigned int i, sources = 0;
+
+ for (i = 0; i < tx_num; i++)
+ if (tx_slot[i])
+ sources |= (1 << i);
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES,
+ sources);
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_CONTROL, sources);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98504_dai_ops = {
+ .set_tdm_slot = max98504_set_tdm_slot,
+ .set_channel_map = max98504_set_channel_map,
+};
+
+#define MAX98504_FORMATS (SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\
+ SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE)
+#define MAX98504_PDM_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
+ SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\
+ SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\
+ SNDRV_PCM_RATE_96000)
+
+static struct snd_soc_dai_driver max98504_dai[] = {
+ /* TODO: Add the PCM interface definitions */
+ {
+ .name = "max98504-aif2",
+ .id = MAX98504_DAI_ID_PDM,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .ops = &max98504_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver max98504_component_driver = {
+ .probe = max98504_component_probe,
+ .remove = max98504_component_remove,
+ .dapm_widgets = max98504_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets),
+ .dapm_routes = max98504_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes),
+};
+
+static const struct regmap_config max98504_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98504_MAX_REGISTER,
+ .reg_defaults = max98504_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max98504_reg_defaults),
+ .volatile_reg = max98504_volatile_register,
+ .readable_reg = max98504_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98504_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct device_node *node = dev->of_node;
+ struct max98504_priv *max98504;
+ int i, ret;
+
+ max98504 = devm_kzalloc(dev, sizeof(*max98504), GFP_KERNEL);
+ if (!max98504)
+ return -ENOMEM;
+
+ if (node) {
+ if (!of_property_read_u32(node, "maxim,brownout-threshold",
+ &max98504->brownout_threshold))
+ max98504->brownout_enable = true;
+
+ of_property_read_u32(node, "maxim,brownout-attenuation",
+ &max98504->brownout_attenuation);
+ of_property_read_u32(node, "maxim,brownout-attack-hold-ms",
+ &max98504->brownout_attack_hold);
+ of_property_read_u32(node, "maxim,brownout-timed-hold-ms",
+ &max98504->brownout_timed_hold);
+ of_property_read_u32(node, "maxim,brownout-release-rate-ms",
+ &max98504->brownout_release_rate);
+ }
+
+ max98504->regmap = devm_regmap_init_i2c(client, &max98504_regmap);
+ if (IS_ERR(max98504->regmap)) {
+ ret = PTR_ERR(max98504->regmap);
+ dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < MAX98504_NUM_SUPPLIES; i++)
+ max98504->supplies[i].supply = max98504_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, MAX98504_NUM_SUPPLIES,
+ max98504->supplies);
+ if (ret < 0)
+ return ret;
+
+ i2c_set_clientdata(client, max98504);
+
+ return devm_snd_soc_register_component(dev, &max98504_component_driver,
+ max98504_dai, ARRAY_SIZE(max98504_dai));
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98504_of_match[] = {
+ { .compatible = "maxim,max98504" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max98504_of_match);
+#endif
+
+static const struct i2c_device_id max98504_i2c_id[] = {
+ { "max98504" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
+
+static struct i2c_driver max98504_i2c_driver = {
+ .driver = {
+ .name = "max98504",
+ .of_match_table = of_match_ptr(max98504_of_match),
+ },
+ .probe = max98504_i2c_probe,
+ .id_table = max98504_i2c_id,
+};
+module_i2c_driver(max98504_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98504 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98504.h b/sound/soc/codecs/max98504.h
new file mode 100644
index 000000000000..afbefad2d5ce
--- /dev/null
+++ b/sound/soc/codecs/max98504.h
@@ -0,0 +1,59 @@
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2011 - 2012 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ *
+ * 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 MAX98504_H_
+#define MAX98504_H_
+
+/*
+ * MAX98504 Register Definitions
+ */
+#define MAX98504_INTERRUPT_STATUS 0x01
+#define MAX98504_INTERRUPT_FLAGS 0x02
+#define MAX98504_INTERRUPT_ENABLE 0x03
+#define MAX98504_INTERRUPT_FLAG_CLEARS 0x04
+#define MAX98504_GPIO_ENABLE 0x10
+#define MAX98504_GPIO_CONFIG 0x11
+#define MAX98504_WATCHDOG_ENABLE 0x12
+#define MAX98504_WATCHDOG_CONFIG 0x13
+#define MAX98504_WATCHDOG_CLEAR 0x14
+#define MAX98504_CLOCK_MONITOR_ENABLE 0x15
+#define MAX98504_PVDD_BROWNOUT_ENABLE 0x16
+#define MAX98504_PVDD_BROWNOUT_CONFIG_1 0x17
+#define MAX98504_PVDD_BROWNOUT_CONFIG_2 0x18
+#define MAX98504_PVDD_BROWNOUT_CONFIG_3 0x19
+#define MAX98504_PVDD_BROWNOUT_CONFIG_4 0x1a
+#define MAX98504_PCM_RX_ENABLE 0x20
+#define MAX98504_PCM_TX_ENABLE 0x21
+#define MAX98504_PCM_TX_HIZ_CONTROL 0x22
+#define MAX98504_PCM_TX_CHANNEL_SOURCES 0x23
+#define MAX98504_PCM_MODE_CONFIG 0x24
+#define MAX98504_PCM_DSP_CONFIG 0x25
+#define MAX98504_PCM_CLOCK_SETUP 0x26
+#define MAX98504_PCM_SAMPLE_RATE_SETUP 0x27
+#define MAX98504_PCM_TO_SPEAKER_MONOMIX 0x28
+#define MAX98504_PDM_TX_ENABLE 0x30
+#define MAX98504_PDM_TX_HIZ_CONTROL 0x31
+#define MAX98504_PDM_TX_CONTROL 0x32
+#define MAX98504_PDM_RX_ENABLE 0x33
+#define MAX98504_SPEAKER_ENABLE 0x34
+#define MAX98504_SPEAKER_SOURCE_SELECT 0x35
+#define MAX98504_MEASUREMENT_ENABLE 0x36
+#define MAX98504_ANALOGUE_INPUT_GAIN 0x37
+#define MAX98504_TEMPERATURE_LIMIT_CONFIG 0x38
+#define MAX98504_GLOBAL_ENABLE 0x40
+#define MAX98504_SOFTWARE_RESET 0x41
+#define MAX98504_REV_ID 0x7fff
+
+#define MAX98504_MAX_REGISTER 0x7fff
+
+#define MAX98504_DAI_ID_PCM 1
+#define MAX98504_DAI_ID_PDM 2
+
+#endif /* MAX98504_H_ */
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
new file mode 100644
index 000000000000..68074c92a7c0
--- /dev/null
+++ b/sound/soc/codecs/max9860.c
@@ -0,0 +1,753 @@
+/*
+ * Driver for the MAX9860 Mono Audio Voice Codec
+ *
+ * https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf
+ *
+ * The driver does not support sidetone since the DVST register field is
+ * backwards with the mute near the maximum level instead of the minimum.
+ *
+ * Author: Peter Rosin <peda@axentia.s>
+ * Copyright 2016 Axentia Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "max9860.h"
+
+struct max9860_priv {
+ struct regmap *regmap;
+ struct regulator *dvddio;
+ struct notifier_block dvddio_nb;
+ u8 psclk;
+ unsigned long pclk_rate;
+ int fmt;
+};
+
+static int max9860_dvddio_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct max9860_priv *max9860 = container_of(nb, struct max9860_priv,
+ dvddio_nb);
+ if (event & REGULATOR_EVENT_DISABLE) {
+ regcache_mark_dirty(max9860->regmap);
+ regcache_cache_only(max9860->regmap, true);
+ }
+
+ return 0;
+}
+
+static const struct reg_default max9860_reg_defaults[] = {
+ { MAX9860_PWRMAN, 0x00 },
+ { MAX9860_INTEN, 0x00 },
+ { MAX9860_SYSCLK, 0x00 },
+ { MAX9860_AUDIOCLKHIGH, 0x00 },
+ { MAX9860_AUDIOCLKLOW, 0x00 },
+ { MAX9860_IFC1A, 0x00 },
+ { MAX9860_IFC1B, 0x00 },
+ { MAX9860_VOICEFLTR, 0x00 },
+ { MAX9860_DACATTN, 0x00 },
+ { MAX9860_ADCLEVEL, 0x00 },
+ { MAX9860_DACGAIN, 0x00 },
+ { MAX9860_MICGAIN, 0x00 },
+ { MAX9860_MICADC, 0x00 },
+ { MAX9860_NOISEGATE, 0x00 },
+};
+
+static bool max9860_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS ... MAX9860_MICGAIN:
+ case MAX9860_MICADC ... MAX9860_PWRMAN:
+ case MAX9860_REVISION:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTEN ... MAX9860_MICGAIN:
+ case MAX9860_MICADC ... MAX9860_PWRMAN:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS:
+ case MAX9860_MICREADBACK:
+ return true;
+ }
+
+ return false;
+}
+
+static bool max9860_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9860_INTRSTATUS:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config max9860_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = max9860_readable,
+ .writeable_reg = max9860_writeable,
+ .volatile_reg = max9860_volatile,
+ .precious_reg = max9860_precious,
+
+ .max_register = MAX9860_MAX_REGISTER,
+ .reg_defaults = max9860_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(max9860_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const DECLARE_TLV_DB_SCALE(dva_tlv, -9100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(dvg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_RANGE(pam_tlv,
+ 0, MAX9860_PAM_MAX - 1, TLV_DB_SCALE_ITEM(-2000, 2000, 1),
+ MAX9860_PAM_MAX, MAX9860_PAM_MAX, TLV_DB_SCALE_ITEM(3000, 0, 0));
+static const DECLARE_TLV_DB_SCALE(pgam_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(anth_tlv, -7600, 400, 1);
+static const DECLARE_TLV_DB_SCALE(agcth_tlv, -1800, 100, 0);
+
+static const char * const agchld_text[] = {
+ "AGC Disabled", "50ms", "100ms", "400ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agchld_enum, MAX9860_MICADC,
+ MAX9860_AGCHLD_SHIFT, agchld_text);
+
+static const char * const agcsrc_text[] = {
+ "Left ADC", "Left/Right ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcsrc_enum, MAX9860_MICADC,
+ MAX9860_AGCSRC_SHIFT, agcsrc_text);
+
+static const char * const agcatk_text[] = {
+ "3ms", "12ms", "50ms", "200ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcatk_enum, MAX9860_MICADC,
+ MAX9860_AGCATK_SHIFT, agcatk_text);
+
+static const char * const agcrls_text[] = {
+ "78ms", "156ms", "312ms", "625ms",
+ "1.25s", "2.5s", "5s", "10s"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcrls_enum, MAX9860_MICADC,
+ MAX9860_AGCRLS_SHIFT, agcrls_text);
+
+static const char * const filter_text[] = {
+ "Disabled",
+ "Elliptical HP 217Hz notch (16kHz)",
+ "Butterworth HP 500Hz (16kHz)",
+ "Elliptical HP 217Hz notch (8kHz)",
+ "Butterworth HP 500Hz (8kHz)",
+ "Butterworth HP 200Hz (48kHz)"
+};
+
+static SOC_ENUM_SINGLE_DECL(avflt_enum, MAX9860_VOICEFLTR,
+ MAX9860_AVFLT_SHIFT, filter_text);
+
+static SOC_ENUM_SINGLE_DECL(dvflt_enum, MAX9860_VOICEFLTR,
+ MAX9860_DVFLT_SHIFT, filter_text);
+
+static const struct snd_kcontrol_new max9860_controls[] = {
+SOC_SINGLE_TLV("Master Playback Volume", MAX9860_DACATTN,
+ MAX9860_DVA_SHIFT, MAX9860_DVA_MUTE, 1, dva_tlv),
+SOC_SINGLE_TLV("DAC Gain Volume", MAX9860_DACGAIN,
+ MAX9860_DVG_SHIFT, MAX9860_DVG_MAX, 0, dvg_tlv),
+SOC_DOUBLE_TLV("Line Capture Volume", MAX9860_ADCLEVEL,
+ MAX9860_ADCLL_SHIFT, MAX9860_ADCRL_SHIFT, MAX9860_ADCxL_MIN, 1,
+ adc_tlv),
+
+SOC_ENUM("AGC Hold Time", agchld_enum),
+SOC_ENUM("AGC/Noise Gate Source", agcsrc_enum),
+SOC_ENUM("AGC Attack Time", agcatk_enum),
+SOC_ENUM("AGC Release Time", agcrls_enum),
+
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MAX9860_NOISEGATE,
+ MAX9860_ANTH_SHIFT, MAX9860_ANTH_MAX, 0, anth_tlv),
+SOC_SINGLE_TLV("AGC Signal Threshold Volume", MAX9860_NOISEGATE,
+ MAX9860_AGCTH_SHIFT, MAX9860_AGCTH_MIN, 1, agcth_tlv),
+
+SOC_SINGLE_TLV("Mic PGA Volume", MAX9860_MICGAIN,
+ MAX9860_PGAM_SHIFT, MAX9860_PGAM_MIN, 1, pgam_tlv),
+SOC_SINGLE_TLV("Mic Preamp Volume", MAX9860_MICGAIN,
+ MAX9860_PAM_SHIFT, MAX9860_PAM_MAX, 0, pam_tlv),
+
+SOC_ENUM("ADC Filter", avflt_enum),
+SOC_ENUM("DAC Filter", dvflt_enum),
+};
+
+static const struct snd_soc_dapm_widget max9860_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICL"),
+SND_SOC_DAPM_INPUT("MICR"),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, MAX9860_PWRMAN, MAX9860_ADCLEN_SHIFT, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, MAX9860_PWRMAN, MAX9860_ADCREN_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_DAC("DAC", NULL, MAX9860_PWRMAN, MAX9860_DACEN_SHIFT, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT"),
+
+SND_SOC_DAPM_SUPPLY("Supply", SND_SOC_NOPM, 0, 0,
+ NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DVDD", 0, 0),
+SND_SOC_DAPM_CLOCK_SUPPLY("mclk"),
+};
+
+static const struct snd_soc_dapm_route max9860_dapm_routes[] = {
+ { "ADCL", NULL, "MICL" },
+ { "ADCR", NULL, "MICR" },
+ { "AIFOUTL", NULL, "ADCL" },
+ { "AIFOUTR", NULL, "ADCR" },
+
+ { "DAC", NULL, "AIFINL" },
+ { "DAC", NULL, "AIFINR" },
+ { "OUT", NULL, "DAC" },
+
+ { "Supply", NULL, "AVDD" },
+ { "Supply", NULL, "DVDD" },
+ { "Supply", NULL, "mclk" },
+
+ { "DAC", NULL, "Supply" },
+ { "ADCL", NULL, "Supply" },
+ { "ADCR", NULL, "Supply" },
+};
+
+static int max9860_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 max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+ u8 master;
+ u8 ifc1a = 0;
+ u8 ifc1b = 0;
+ u8 sysclk = 0;
+ unsigned long n;
+ int ret;
+
+ dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n",
+ params_rate(params),
+ params_channels(params));
+
+ if (params_channels(params) == 2)
+ ifc1b |= MAX9860_ST;
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ master = MAX9860_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ifc1a |= master;
+
+ if (master) {
+ if (params_width(params) * params_channels(params) > 48)
+ ifc1b |= MAX9860_BSEL_64X;
+ else
+ ifc1b |= MAX9860_BSEL_48X;
+ }
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ifc1a |= MAX9860_DDLY;
+ ifc1b |= MAX9860_ADLY;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ifc1a |= MAX9860_WCI;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ if (params_width(params) != 16) {
+ dev_err(codec->dev,
+ "DSP_A works for 16 bits per sample only.\n");
+ return -EINVAL;
+ }
+ ifc1a |= MAX9860_DDLY | MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+ ifc1b |= MAX9860_ADLY;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if (params_width(params) != 16) {
+ dev_err(codec->dev,
+ "DSP_B works for 16 bits per sample only.\n");
+ return -EINVAL;
+ }
+ ifc1a |= MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (max9860->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ return -EINVAL;
+ }
+ ifc1a ^= MAX9860_WCI;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ return -EINVAL;
+ }
+ ifc1a ^= MAX9860_WCI;
+ /* fall through */
+ case SND_SOC_DAIFMT_IB_NF:
+ ifc1a ^= MAX9860_DBCI;
+ ifc1b ^= MAX9860_ABCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "IFC1A %02x\n", ifc1a);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set IFC1A: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(codec->dev, "IFC1B %02x\n", ifc1b);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set IFC1B: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Check if Integer Clock Mode is possible, but avoid it in slave mode
+ * since we then do not know if lrclk is derived from pclk and the
+ * datasheet mentions that the frequencies have to match exactly in
+ * order for this to work.
+ */
+ if (params_rate(params) == 8000 || params_rate(params) == 16000) {
+ if (master) {
+ switch (max9860->pclk_rate) {
+ case 12000000:
+ sysclk = MAX9860_FREQ_12MHZ;
+ break;
+ case 13000000:
+ sysclk = MAX9860_FREQ_13MHZ;
+ break;
+ case 19200000:
+ sysclk = MAX9860_FREQ_19_2MHZ;
+ break;
+ default:
+ /*
+ * Integer Clock Mode not possible. Leave
+ * sysclk at zero and fall through to the
+ * code below for PLL mode.
+ */
+ break;
+ }
+
+ if (sysclk && params_rate(params) == 16000)
+ sysclk |= MAX9860_16KHZ;
+ }
+ }
+
+ /*
+ * Largest possible n:
+ * 65536 * 96 * 48kHz / 10MHz -> 30199
+ * Smallest possible n:
+ * 65536 * 96 * 8kHz / 20MHz -> 2517
+ * Both fit nicely in the available 15 bits, no need to apply any mask.
+ */
+ n = DIV_ROUND_CLOSEST_ULL(65536ULL * 96 * params_rate(params),
+ max9860->pclk_rate);
+
+ if (!sysclk) {
+ /* PLL mode */
+ if (params_rate(params) > 24000)
+ sysclk |= MAX9860_16KHZ;
+
+ if (!master)
+ n |= 1; /* trigger rapid pll lock mode */
+ }
+
+ sysclk |= max9860->psclk;
+ dev_dbg(codec->dev, "SYSCLK %02x\n", sysclk);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_SYSCLK, sysclk);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(codec->dev, "N %lu\n", n);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKHIGH, n >> 8);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set NHI: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKLOW, n & 0xff);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set NLO: %d\n", ret);
+ return ret;
+ }
+
+ if (!master) {
+ dev_dbg(codec->dev, "Enable PLL\n");
+ ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH,
+ MAX9860_PLL, MAX9860_PLL);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable PLL: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBS_CFS:
+ max9860->fmt = fmt;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops max9860_dai_ops = {
+ .hw_params = max9860_hw_params,
+ .set_fmt = max9860_set_fmt,
+};
+
+static struct snd_soc_dai_driver max9860_dai = {
+ .name = "max9860-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &max9860_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static int max9860_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(codec->dev);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+ MAX9860_SHDN, MAX9860_SHDN);
+ if (ret) {
+ dev_err(codec->dev, "Failed to remove SHDN: %d\n", ret);
+ return ret;
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+ MAX9860_SHDN, 0);
+ if (ret) {
+ dev_err(codec->dev, "Failed to request SHDN: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver max9860_codec_driver = {
+ .set_bias_level = max9860_set_bias_level,
+ .idle_bias_off = true,
+
+ .controls = max9860_controls,
+ .num_controls = ARRAY_SIZE(max9860_controls),
+ .dapm_widgets = max9860_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets),
+ .dapm_routes = max9860_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes),
+};
+
+#ifdef CONFIG_PM
+static int max9860_suspend(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, MAX9860_PSCLK_OFF);
+ if (ret) {
+ dev_err(dev, "Failed to disable clock: %d\n", ret);
+ return ret;
+ }
+
+ regulator_disable(max9860->dvddio);
+
+ return 0;
+}
+
+static int max9860_resume(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(max9860->dvddio);
+ if (ret) {
+ dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(max9860->regmap, false);
+ ret = regcache_sync(max9860->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, max9860->psclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max9860_pm_ops = {
+ SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL)
+};
+
+static int max9860_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct max9860_priv *max9860;
+ int ret;
+ struct clk *mclk;
+ unsigned long mclk_rate;
+ int i;
+ int intr;
+
+ max9860 = devm_kzalloc(dev, sizeof(struct max9860_priv), GFP_KERNEL);
+ if (!max9860)
+ return -ENOMEM;
+
+ max9860->dvddio = devm_regulator_get(dev, "DVDDIO");
+ if (IS_ERR(max9860->dvddio)) {
+ ret = PTR_ERR(max9860->dvddio);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret);
+ return ret;
+ }
+
+ max9860->dvddio_nb.notifier_call = max9860_dvddio_event;
+
+ ret = regulator_register_notifier(max9860->dvddio, &max9860->dvddio_nb);
+ if (ret)
+ dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret);
+
+ ret = regulator_enable(max9860->dvddio);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+ return ret;
+ }
+
+ max9860->regmap = devm_regmap_init_i2c(i2c, &max9860_regmap);
+ if (IS_ERR(max9860->regmap)) {
+ ret = PTR_ERR(max9860->regmap);
+ goto err_regulator;
+ }
+
+ dev_set_drvdata(dev, max9860);
+
+ /*
+ * mclk has to be in the 10MHz to 60MHz range.
+ * psclk is used to scale mclk into pclk so that
+ * pclk is in the 10MHz to 20MHz range.
+ */
+ mclk = clk_get(dev, "mclk");
+
+ if (IS_ERR(mclk)) {
+ ret = PTR_ERR(mclk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get MCLK: %d\n", ret);
+ goto err_regulator;
+ }
+
+ mclk_rate = clk_get_rate(mclk);
+ clk_put(mclk);
+
+ if (mclk_rate > 60000000 || mclk_rate < 10000000) {
+ dev_err(dev, "Bad mclk %luHz (needs 10MHz - 60MHz)\n",
+ mclk_rate);
+ ret = -EINVAL;
+ goto err_regulator;
+ }
+ if (mclk_rate >= 40000000)
+ max9860->psclk = 3;
+ else if (mclk_rate >= 20000000)
+ max9860->psclk = 2;
+ else
+ max9860->psclk = 1;
+ max9860->pclk_rate = mclk_rate >> (max9860->psclk - 1);
+ max9860->psclk <<= MAX9860_PSCLK_SHIFT;
+ dev_dbg(dev, "mclk %lu pclk %lu\n", mclk_rate, max9860->pclk_rate);
+
+ regcache_cache_bypass(max9860->regmap, true);
+ for (i = 0; i < max9860_regmap.num_reg_defaults; ++i) {
+ ret = regmap_write(max9860->regmap,
+ max9860_regmap.reg_defaults[i].reg,
+ max9860_regmap.reg_defaults[i].def);
+ if (ret) {
+ dev_err(dev, "Failed to initialize register %u: %d\n",
+ max9860_regmap.reg_defaults[i].reg, ret);
+ goto err_regulator;
+ }
+ }
+ regcache_cache_bypass(max9860->regmap, false);
+
+ ret = regmap_read(max9860->regmap, MAX9860_INTRSTATUS, &intr);
+ if (ret) {
+ dev_err(dev, "Failed to clear INTRSTATUS: %d\n", ret);
+ goto err_regulator;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ ret = snd_soc_register_codec(dev, &max9860_codec_driver,
+ &max9860_dai, 1);
+ if (ret) {
+ dev_err(dev, "Failed to register CODEC: %d\n", ret);
+ goto err_pm;
+ }
+
+ return 0;
+
+err_pm:
+ pm_runtime_disable(dev);
+err_regulator:
+ regulator_disable(max9860->dvddio);
+ return ret;
+}
+
+static int max9860_remove(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+
+ snd_soc_unregister_codec(dev);
+ pm_runtime_disable(dev);
+ regulator_disable(max9860->dvddio);
+ return 0;
+}
+
+static const struct i2c_device_id max9860_i2c_id[] = {
+ { "max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9860_i2c_id);
+
+static const struct of_device_id max9860_of_match[] = {
+ { .compatible = "maxim,max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9860_of_match);
+
+static struct i2c_driver max9860_i2c_driver = {
+ .probe = max9860_probe,
+ .remove = max9860_remove,
+ .id_table = max9860_i2c_id,
+ .driver = {
+ .name = "max9860",
+ .of_match_table = max9860_of_match,
+ .pm = &max9860_pm_ops,
+ },
+};
+
+module_i2c_driver(max9860_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h
new file mode 100644
index 000000000000..22041bd67a7d
--- /dev/null
+++ b/sound/soc/codecs/max9860.h
@@ -0,0 +1,162 @@
+/*
+ * Driver for the MAX9860 Mono Audio Voice Codec
+ *
+ * Author: Peter Rosin <peda@axentia.s>
+ * Copyright 2016 Axentia Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _SND_SOC_MAX9860
+#define _SND_SOC_MAX9860
+
+#define MAX9860_INTRSTATUS 0x00
+#define MAX9860_MICREADBACK 0x01
+#define MAX9860_INTEN 0x02
+#define MAX9860_SYSCLK 0x03
+#define MAX9860_AUDIOCLKHIGH 0x04
+#define MAX9860_AUDIOCLKLOW 0x05
+#define MAX9860_IFC1A 0x06
+#define MAX9860_IFC1B 0x07
+#define MAX9860_VOICEFLTR 0x08
+#define MAX9860_DACATTN 0x09
+#define MAX9860_ADCLEVEL 0x0a
+#define MAX9860_DACGAIN 0x0b
+#define MAX9860_MICGAIN 0x0c
+#define MAX9860_RESERVED 0x0d
+#define MAX9860_MICADC 0x0e
+#define MAX9860_NOISEGATE 0x0f
+#define MAX9860_PWRMAN 0x10
+#define MAX9860_REVISION 0xff
+
+#define MAX9860_MAX_REGISTER 0xff
+
+/* INTRSTATUS */
+#define MAX9860_CLD 0x80
+#define MAX9860_SLD 0x40
+#define MAX9860_ULK 0x20
+
+/* MICREADBACK */
+#define MAX9860_NG 0xe0
+#define MAX9860_AGC 0x1f
+
+/* INTEN */
+#define MAX9860_ICLD 0x80
+#define MAX9860_ISLD 0x40
+#define MAX9860_IULK 0x20
+
+/* SYSCLK */
+#define MAX9860_PSCLK 0x30
+#define MAX9860_PSCLK_OFF 0x00
+#define MAX9860_PSCLK_SHIFT 4
+#define MAX9860_FREQ 0x06
+#define MAX9860_FREQ_NORMAL 0x00
+#define MAX9860_FREQ_12MHZ 0x02
+#define MAX9860_FREQ_13MHZ 0x04
+#define MAX9860_FREQ_19_2MHZ 0x06
+#define MAX9860_16KHZ 0x01
+
+/* AUDIOCLKHIGH */
+#define MAX9860_PLL 0x80
+#define MAX9860_NHI 0x7f
+
+/* AUDIOCLKLOW */
+#define MAX9860_NLO 0xff
+
+/* IFC1A */
+#define MAX9860_MASTER 0x80
+#define MAX9860_WCI 0x40
+#define MAX9860_DBCI 0x20
+#define MAX9860_DDLY 0x10
+#define MAX9860_HIZ 0x08
+#define MAX9860_TDM 0x04
+
+/* IFC1B */
+#define MAX9860_ABCI 0x20
+#define MAX9860_ADLY 0x10
+#define MAX9860_ST 0x08
+#define MAX9860_BSEL 0x07
+#define MAX9860_BSEL_OFF 0x00
+#define MAX9860_BSEL_64X 0x01
+#define MAX9860_BSEL_48X 0x02
+#define MAX9860_BSEL_PCLK_2 0x04
+#define MAX9860_BSEL_PCLK_4 0x05
+#define MAX9860_BSEL_PCLK_8 0x06
+#define MAX9860_BSEL_PCLK_16 0x07
+
+/* VOICEFLTR */
+#define MAX9860_AVFLT 0xf0
+#define MAX9860_AVFLT_SHIFT 4
+#define MAX9860_AVFLT_COUNT 6
+#define MAX9860_DVFLT 0x0f
+#define MAX9860_DVFLT_SHIFT 0
+#define MAX9860_DVFLT_COUNT 6
+
+/* DACATTN */
+#define MAX9860_DVA 0xfe
+#define MAX9860_DVA_SHIFT 1
+#define MAX9860_DVA_MUTE 0x5e
+
+/* ADCLEVEL */
+#define MAX9860_ADCRL 0xf0
+#define MAX9860_ADCRL_SHIFT 4
+#define MAX9860_ADCLL 0x0f
+#define MAX9860_ADCLL_SHIFT 0
+#define MAX9860_ADCxL_MIN 15
+
+/* DACGAIN */
+#define MAX9860_DVG 0x60
+#define MAX9860_DVG_SHIFT 5
+#define MAX9860_DVG_MAX 3
+#define MAX9860_DVST 0x1f
+#define MAX9860_DVST_SHIFT 0
+#define MAX9860_DVST_MIN 31
+
+/* MICGAIN */
+#define MAX9860_PAM 0x60
+#define MAX9860_PAM_SHIFT 5
+#define MAX9860_PAM_MAX 3
+#define MAX9860_PGAM 0x1f
+#define MAX9860_PGAM_SHIFT 0
+#define MAX9860_PGAM_MIN 20
+
+/* MICADC */
+#define MAX9860_AGCSRC 0x80
+#define MAX9860_AGCSRC_SHIFT 7
+#define MAX9860_AGCSRC_COUNT 2
+#define MAX9860_AGCRLS 0x70
+#define MAX9860_AGCRLS_SHIFT 4
+#define MAX9860_AGCRLS_COUNT 8
+#define MAX9860_AGCATK 0x0c
+#define MAX9860_AGCATK_SHIFT 2
+#define MAX9860_AGCATK_COUNT 4
+#define MAX9860_AGCHLD 0x03
+#define MAX9860_AGCHLD_OFF 0x00
+#define MAX9860_AGCHLD_SHIFT 0
+#define MAX9860_AGCHLD_COUNT 4
+
+/* NOISEGATE */
+#define MAX9860_ANTH 0xf0
+#define MAX9860_ANTH_SHIFT 4
+#define MAX9860_ANTH_MAX 15
+#define MAX9860_AGCTH 0x0f
+#define MAX9860_AGCTH_SHIFT 0
+#define MAX9860_AGCTH_MIN 15
+
+/* PWRMAN */
+#define MAX9860_SHDN 0x80
+#define MAX9860_DACEN 0x08
+#define MAX9860_DACEN_SHIFT 3
+#define MAX9860_ADCLEN 0x02
+#define MAX9860_ADCLEN_SHIFT 1
+#define MAX9860_ADCREN 0x01
+#define MAX9860_ADCREN_SHIFT 0
+
+#endif /* _SND_SOC_MAX9860 */
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 2a22fddeb6af..2a22fddeb6af 100755..100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h
index 65590b4ad62a..65590b4ad62a 100755..100644
--- a/sound/soc/codecs/max9867.h
+++ b/sound/soc/codecs/max9867.h
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index 683769f0f246..5c9707ac4bbf 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/acpi.h>
#include <linux/math64.h>
+#include <linux/semaphore.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -30,10 +31,22 @@
#include "nau8825.h"
+
+#define NUVOTON_CODEC_DAI "nau8825-hifi"
+
#define NAU_FREF_MAX 13500000
-#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MAX 124000000
#define NAU_FVCO_MIN 90000000
+/* cross talk suppression detection */
+#define LOG10_MAGIC 646456993
+#define GAIN_AUGMENT 22500
+#define SIDETONE_BASE 207000
+
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825,
+ int clk_id, unsigned int freq);
+
struct nau8825_fll {
int mclk_src;
int ratio;
@@ -156,6 +169,661 @@ static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_CHARGE_PUMP, 0x0 },
};
+/* register backup table when cross talk detection */
+static struct reg_default nau8825_xtalk_baktab[] = {
+ { NAU8825_REG_ADC_DGAIN_CTRL, 0 },
+ { NAU8825_REG_HSVOL_CTRL, 0 },
+ { NAU8825_REG_DACL_CTRL, 0 },
+ { NAU8825_REG_DACR_CTRL, 0 },
+};
+
+static const unsigned short logtable[256] = {
+ 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
+ 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
+ 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
+ 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
+ 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
+ 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
+ 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
+ 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
+ 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
+ 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
+ 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
+ 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
+ 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
+ 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
+ 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
+ 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
+ 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
+ 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
+ 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
+ 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
+ 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
+ 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
+ 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
+ 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
+ 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
+ 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
+ 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
+ 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
+ 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
+ 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
+ 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
+ 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47
+};
+
+static struct snd_soc_dai *nau8825_get_codec_dai(struct nau8825 *nau8825)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(nau8825->dapm);
+ struct snd_soc_component *component = &codec->component;
+ struct snd_soc_dai *codec_dai, *_dai;
+
+ list_for_each_entry_safe(codec_dai, _dai, &component->dai_list, list) {
+ if (!strncmp(codec_dai->name, NUVOTON_CODEC_DAI,
+ strlen(NUVOTON_CODEC_DAI)))
+ return codec_dai;
+ }
+ return NULL;
+}
+
+static bool nau8825_dai_is_active(struct nau8825 *nau8825)
+{
+ struct snd_soc_dai *codec_dai = nau8825_get_codec_dai(nau8825);
+
+ if (codec_dai) {
+ if (codec_dai->playback_active || codec_dai->capture_active)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * nau8825_sema_acquire - acquire the semaphore of nau88l25
+ * @nau8825: component to register the codec private data with
+ * @timeout: how long in jiffies to wait before failure or zero to wait
+ * until release
+ *
+ * Attempts to acquire the semaphore with number of jiffies. If no more
+ * tasks are allowed to acquire the semaphore, calling this function will
+ * put the task to sleep. If the semaphore is not released within the
+ * specified number of jiffies, this function returns.
+ * Acquires the semaphore without jiffies. If no more tasks are allowed
+ * to acquire the semaphore, calling this function will put the task to
+ * sleep until the semaphore is released.
+ * It returns if the semaphore was acquired.
+ */
+static void nau8825_sema_acquire(struct nau8825 *nau8825, long timeout)
+{
+ int ret;
+
+ if (timeout)
+ ret = down_timeout(&nau8825->xtalk_sem, timeout);
+ else
+ ret = down_interruptible(&nau8825->xtalk_sem);
+
+ if (ret < 0)
+ dev_warn(nau8825->dev, "Acquire semaphone fail\n");
+}
+
+/**
+ * nau8825_sema_release - release the semaphore of nau88l25
+ * @nau8825: component to register the codec private data with
+ *
+ * Release the semaphore which may be called from any context and
+ * even by tasks which have never called down().
+ */
+static inline void nau8825_sema_release(struct nau8825 *nau8825)
+{
+ up(&nau8825->xtalk_sem);
+}
+
+/**
+ * nau8825_sema_reset - reset the semaphore for nau88l25
+ * @nau8825: component to register the codec private data with
+ *
+ * Reset the counter of the semaphore. Call this function to restart
+ * a new round task management.
+ */
+static inline void nau8825_sema_reset(struct nau8825 *nau8825)
+{
+ nau8825->xtalk_sem.count = 1;
+}
+
+/**
+ * Ramp up the headphone volume change gradually to target level.
+ *
+ * @nau8825: component to register the codec private data with
+ * @vol_from: the volume to start up
+ * @vol_to: the target volume
+ * @step: the volume span to move on
+ *
+ * The headphone volume is from 0dB to minimum -54dB and -1dB per step.
+ * If the volume changes sharp, there is a pop noise heard in headphone. We
+ * provide the function to ramp up the volume up or down by delaying 10ms
+ * per step.
+ */
+static void nau8825_hpvol_ramp(struct nau8825 *nau8825,
+ unsigned int vol_from, unsigned int vol_to, unsigned int step)
+{
+ unsigned int value, volume, ramp_up, from, to;
+
+ if (vol_from == vol_to || step == 0) {
+ return;
+ } else if (vol_from < vol_to) {
+ ramp_up = true;
+ from = vol_from;
+ to = vol_to;
+ } else {
+ ramp_up = false;
+ from = vol_to;
+ to = vol_from;
+ }
+ /* only handle volume from 0dB to minimum -54dB */
+ if (to > NAU8825_HP_VOL_MIN)
+ to = NAU8825_HP_VOL_MIN;
+
+ for (volume = from; volume < to; volume += step) {
+ if (ramp_up)
+ value = volume;
+ else
+ value = to - volume + from;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+ NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+ (value << NAU8825_HPL_VOL_SFT) | value);
+ usleep_range(10000, 10500);
+ }
+ if (ramp_up)
+ value = to;
+ else
+ value = from;
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+ NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+ (value << NAU8825_HPL_VOL_SFT) | value);
+}
+
+/**
+ * Computes log10 of a value; the result is round off to 3 decimal. This func-
+ * tion takes reference to dvb-math. The source code locates as the following.
+ * Linux/drivers/media/dvb-core/dvb_math.c
+ *
+ * return log10(value) * 1000
+ */
+static u32 nau8825_intlog10_dec3(u32 value)
+{
+ u32 msb, logentry, significand, interpolation, log10val;
+ u64 log2val;
+
+ /* first detect the msb (count begins at 0) */
+ msb = fls(value) - 1;
+ /**
+ * now we use a logtable after the following method:
+ *
+ * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
+ * where x = msb and therefore 1 <= y < 2
+ * first y is determined by shifting the value left
+ * so that msb is bit 31
+ * 0x00231f56 -> 0x8C7D5800
+ * the result is y * 2^31 -> "significand"
+ * then the highest 9 bits are used for a table lookup
+ * the highest bit is discarded because it's always set
+ * the highest nine bits in our example are 100011000
+ * so we would use the entry 0x18
+ */
+ significand = value << (31 - msb);
+ logentry = (significand >> 23) & 0xff;
+ /**
+ * last step we do is interpolation because of the
+ * limitations of the log table the error is that part of
+ * the significand which isn't used for lookup then we
+ * compute the ratio between the error and the next table entry
+ * and interpolate it between the log table entry used and the
+ * next one the biggest error possible is 0x7fffff
+ * (in our example it's 0x7D5800)
+ * needed value for next table entry is 0x800000
+ * so the interpolation is
+ * (error / 0x800000) * (logtable_next - logtable_current)
+ * in the implementation the division is moved to the end for
+ * better accuracy there is also an overflow correction if
+ * logtable_next is 256
+ */
+ interpolation = ((significand & 0x7fffff) *
+ ((logtable[(logentry + 1) & 0xff] -
+ logtable[logentry]) & 0xffff)) >> 15;
+
+ log2val = ((msb << 24) + (logtable[logentry] << 8) + interpolation);
+ /**
+ * log10(x) = log2(x) * log10(2)
+ */
+ log10val = (log2val * LOG10_MAGIC) >> 31;
+ /**
+ * the result is round off to 3 decimal
+ */
+ return log10val / ((1 << 24) / 1000);
+}
+
+/**
+ * computes cross talk suppression sidetone gain.
+ *
+ * @sig_org: orignal signal level
+ * @sig_cros: cross talk signal level
+ *
+ * The orignal and cross talk signal vlues need to be characterized.
+ * Once these values have been characterized, this sidetone value
+ * can be converted to decibel with the equation below.
+ * sidetone = 20 * log (original signal level / crosstalk signal level)
+ *
+ * return cross talk sidetone gain
+ */
+static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros)
+{
+ u32 gain, sidetone;
+
+ if (unlikely(sig_org == 0) || unlikely(sig_cros == 0)) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ sig_org = nau8825_intlog10_dec3(sig_org);
+ sig_cros = nau8825_intlog10_dec3(sig_cros);
+ if (sig_org >= sig_cros)
+ gain = (sig_org - sig_cros) * 20 + GAIN_AUGMENT;
+ else
+ gain = (sig_cros - sig_org) * 20 + GAIN_AUGMENT;
+ sidetone = SIDETONE_BASE - gain * 2;
+ sidetone /= 1000;
+
+ return sidetone;
+}
+
+static int nau8825_xtalk_baktab_index_by_reg(unsigned int reg)
+{
+ int index;
+
+ for (index = 0; index < ARRAY_SIZE(nau8825_xtalk_baktab); index++)
+ if (nau8825_xtalk_baktab[index].reg == reg)
+ return index;
+ return -EINVAL;
+}
+
+static void nau8825_xtalk_backup(struct nau8825 *nau8825)
+{
+ int i;
+
+ /* Backup some register values to backup table */
+ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++)
+ regmap_read(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+ &nau8825_xtalk_baktab[i].def);
+}
+
+static void nau8825_xtalk_restore(struct nau8825 *nau8825)
+{
+ int i, volume;
+
+ /* Restore register values from backup table; When the driver restores
+ * the headphone volumem, it needs recover to original level gradually
+ * with 3dB per step for less pop noise.
+ */
+ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) {
+ if (nau8825_xtalk_baktab[i].reg == NAU8825_REG_HSVOL_CTRL) {
+ /* Ramping up the volume change to reduce pop noise */
+ volume = nau8825_xtalk_baktab[i].def &
+ NAU8825_HPR_VOL_MASK;
+ nau8825_hpvol_ramp(nau8825, 0, volume, 3);
+ continue;
+ }
+ regmap_write(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+ nau8825_xtalk_baktab[i].def);
+ }
+}
+
+static void nau8825_xtalk_prepare_dac(struct nau8825 *nau8825)
+{
+ /* Enable power of DAC path */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+ NAU8825_ENABLE_ADC | NAU8825_ENABLE_ADC_CLK |
+ NAU8825_ENABLE_DAC_CLK, NAU8825_ENABLE_DACR |
+ NAU8825_ENABLE_DACL | NAU8825_ENABLE_ADC |
+ NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK);
+ /* Prevent startup click by letting charge pump to ramp up and
+ * change bump enable
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN);
+ /* Enable clock sync of DAC and DAC clock */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN |
+ NAU8825_RDAC_FS_BCLK_ENB,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN);
+ /* Power up output driver with 2 stage */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L);
+ /* HP outputs not shouted to ground */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, 0);
+ /* Enable HP boost driver */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_HP_BOOST_DIS, NAU8825_HP_BOOST_DIS);
+ /* Enable class G compare path to supply 1.8V or 0.9V. */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLASSG_CTRL,
+ NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN,
+ NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN);
+}
+
+static void nau8825_xtalk_prepare_adc(struct nau8825 *nau8825)
+{
+ /* Power up left ADC and raise 5dB than Vmid for Vref */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB);
+}
+
+static void nau8825_xtalk_clock(struct nau8825 *nau8825)
+{
+ /* Recover FLL default value */
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL1, 0x0);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL2, 0x3126);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL3, 0x0008);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL4, 0x0010);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL5, 0x0);
+ regmap_write(nau8825->regmap, NAU8825_REG_FLL6, 0x6000);
+ /* Enable internal VCO clock for detection signal generated */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+ NAU8825_DCO_EN);
+ /* Given specific clock frequency of internal clock to
+ * generate signal.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK, 0x10);
+}
+
+static void nau8825_xtalk_prepare(struct nau8825 *nau8825)
+{
+ int volume, index;
+
+ /* Backup those registers changed by cross talk detection */
+ nau8825_xtalk_backup(nau8825);
+ /* Config IIS as master to output signal by codec */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK | NAU8825_I2S_DRV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_MASTER |
+ (0x2 << NAU8825_I2S_DRV_SFT) | 0x1);
+ /* Ramp up headphone volume to 0dB to get better performance and
+ * avoid pop noise in headphone.
+ */
+ index = nau8825_xtalk_baktab_index_by_reg(NAU8825_REG_HSVOL_CTRL);
+ if (index != -EINVAL) {
+ volume = nau8825_xtalk_baktab[index].def &
+ NAU8825_HPR_VOL_MASK;
+ nau8825_hpvol_ramp(nau8825, volume, 0, 3);
+ }
+ nau8825_xtalk_clock(nau8825);
+ nau8825_xtalk_prepare_dac(nau8825);
+ nau8825_xtalk_prepare_adc(nau8825);
+ /* Config channel path and digital gain */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_MASK | NAU8825_DACL_CH_VOL_MASK,
+ NAU8825_DACL_CH_SEL_L | 0xab);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACR_CH_SEL_MASK | NAU8825_DACR_CH_VOL_MASK,
+ NAU8825_DACR_CH_SEL_R | 0xab);
+ /* Config cross talk parameters and generate the 23Hz sine wave with
+ * 1/16 full scale of signal level for impedance measurement.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+ NAU8825_IMM_THD_MASK | NAU8825_IMM_GEN_VOL_MASK |
+ NAU8825_IMM_CYC_MASK | NAU8825_IMM_DAC_SRC_MASK,
+ (0x9 << NAU8825_IMM_THD_SFT) | NAU8825_IMM_GEN_VOL_1_16th |
+ NAU8825_IMM_CYC_8192 | NAU8825_IMM_DAC_SRC_SIN);
+ /* RMS intrruption enable */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0);
+ /* Power up left and right DAC */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+}
+
+static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
+{
+ /* Disable HP boost driver */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+ NAU8825_HP_BOOST_DIS, 0);
+ /* HP outputs shouted to ground */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+ NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+ /* Power down left and right DAC */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ /* Enable the TESTDAC and disable L/R HP impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP |
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ /* Power down output driver with 2 stage */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, 0);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+ NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+ NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L, 0);
+ /* Disable clock sync of DAC and DAC clock */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN, 0);
+ /* Disable charge pump ramp up function and change bump */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, 0);
+ /* Disable power of DAC path */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+ NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK, 0);
+ if (!nau8825->irq)
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+}
+
+static void nau8825_xtalk_clean_adc(struct nau8825 *nau8825)
+{
+ /* Power down left ADC and restore voltage to Vmid */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+ NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, 0);
+}
+
+static void nau8825_xtalk_clean(struct nau8825 *nau8825)
+{
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+ nau8825_xtalk_clean_dac(nau8825);
+ nau8825_xtalk_clean_adc(nau8825);
+ /* Clear cross talk parameters and disable */
+ regmap_write(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL, 0);
+ /* RMS intrruption disable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_RMS_EN, NAU8825_IRQ_RMS_EN);
+ /* Recover default value for IIS */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK | NAU8825_I2S_DRV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE);
+ /* Restore value of specific register for cross talk */
+ nau8825_xtalk_restore(nau8825);
+}
+
+static void nau8825_xtalk_imm_start(struct nau8825 *nau8825, int vol)
+{
+ /* Apply ADC volume for better cross talk performance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ADC_DGAIN_CTRL,
+ NAU8825_ADC_DIG_VOL_MASK, vol);
+ /* Disables JKTIP(HPL) DAC channel for right to left measurement.
+ * Do it before sending signal in order to erase pop noise.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDACR_EN | NAU8825_BIAS_TESTDACL_EN,
+ NAU8825_BIAS_TESTDACL_EN);
+ switch (nau8825->xtalk_state) {
+ case NAU8825_XTALK_HPR_R2L:
+ /* Enable right headphone impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+ NAU8825_BIAS_HPR_IMP);
+ break;
+ case NAU8825_XTALK_HPL_R2L:
+ /* Enable left headphone impedance */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+ NAU8825_BIAS_HPL_IMP);
+ break;
+ default:
+ break;
+ }
+ msleep(100);
+ /* Impedance measurement mode enable */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+ NAU8825_IMM_EN, NAU8825_IMM_EN);
+}
+
+static void nau8825_xtalk_imm_stop(struct nau8825 *nau8825)
+{
+ /* Impedance measurement mode disable */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_IMM_MODE_CTRL, NAU8825_IMM_EN, 0);
+}
+
+/* The cross talk measurement function can reduce cross talk across the
+ * JKTIP(HPL) and JKR1(HPR) outputs which measures the cross talk signal
+ * level to determine what cross talk reduction gain is. This system works by
+ * sending a 23Hz -24dBV sine wave into the headset output DAC and through
+ * the PGA. The output of the PGA is then connected to an internal current
+ * sense which measures the attenuated 23Hz signal and passing the output to
+ * an ADC which converts the measurement to a binary code. With two separated
+ * measurement, one for JKR1(HPR) and the other JKTIP(HPL), measurement data
+ * can be separated read in IMM_RMS_L for HSR and HSL after each measurement.
+ * Thus, the measurement function has four states to complete whole sequence.
+ * 1. Prepare state : Prepare the resource for detection and transfer to HPR
+ * IMM stat to make JKR1(HPR) impedance measure.
+ * 2. HPR IMM state : Read out orignal signal level of JKR1(HPR) and transfer
+ * to HPL IMM state to make JKTIP(HPL) impedance measure.
+ * 3. HPL IMM state : Read out cross talk signal level of JKTIP(HPL) and
+ * transfer to IMM state to determine suppression sidetone gain.
+ * 4. IMM state : Computes cross talk suppression sidetone gain with orignal
+ * and cross talk signal level. Apply this gain and then restore codec
+ * configuration. Then transfer to Done state for ending.
+ */
+static void nau8825_xtalk_measure(struct nau8825 *nau8825)
+{
+ u32 sidetone;
+
+ switch (nau8825->xtalk_state) {
+ case NAU8825_XTALK_PREPARE:
+ /* In prepare state, set up clock, intrruption, DAC path, ADC
+ * path and cross talk detection parameters for preparation.
+ */
+ nau8825_xtalk_prepare(nau8825);
+ msleep(280);
+ /* Trigger right headphone impedance detection */
+ nau8825->xtalk_state = NAU8825_XTALK_HPR_R2L;
+ nau8825_xtalk_imm_start(nau8825, 0x00d2);
+ break;
+ case NAU8825_XTALK_HPR_R2L:
+ /* In right headphone IMM state, read out right headphone
+ * impedance measure result, and then start up left side.
+ */
+ regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+ &nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+ dev_dbg(nau8825->dev, "HPR_R2L imm: %x\n",
+ nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+ /* Disable then re-enable IMM mode to update */
+ nau8825_xtalk_imm_stop(nau8825);
+ /* Trigger left headphone impedance detection */
+ nau8825->xtalk_state = NAU8825_XTALK_HPL_R2L;
+ nau8825_xtalk_imm_start(nau8825, 0x00ff);
+ break;
+ case NAU8825_XTALK_HPL_R2L:
+ /* In left headphone IMM state, read out left headphone
+ * impedance measure result, and delay some time to wait
+ * detection sine wave output finish. Then, we can calculate
+ * the cross talk suppresstion side tone according to the L/R
+ * headphone imedance.
+ */
+ regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+ &nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ dev_dbg(nau8825->dev, "HPL_R2L imm: %x\n",
+ nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ nau8825_xtalk_imm_stop(nau8825);
+ msleep(150);
+ nau8825->xtalk_state = NAU8825_XTALK_IMM;
+ break;
+ case NAU8825_XTALK_IMM:
+ /* In impedance measure state, the orignal and cross talk
+ * signal level vlues are ready. The side tone gain is deter-
+ * mined with these signal level. After all, restore codec
+ * configuration.
+ */
+ sidetone = nau8825_xtalk_sidetone(
+ nau8825->imp_rms[NAU8825_XTALK_HPR_R2L],
+ nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+ dev_dbg(nau8825->dev, "cross talk sidetone: %x\n", sidetone);
+ regmap_write(nau8825->regmap, NAU8825_REG_DAC_DGAIN_CTRL,
+ (sidetone << 8) | sidetone);
+ nau8825_xtalk_clean(nau8825);
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ break;
+ default:
+ break;
+ }
+}
+
+static void nau8825_xtalk_work(struct work_struct *work)
+{
+ struct nau8825 *nau8825 = container_of(
+ work, struct nau8825, xtalk_work);
+
+ nau8825_xtalk_measure(nau8825);
+ /* To determine the cross talk side tone gain when reach
+ * the impedance measure state.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_IMM)
+ nau8825_xtalk_measure(nau8825);
+
+ /* Delay jack report until cross talk detection process
+ * completed. It can avoid application to do playback
+ * preparation before cross talk detection is still working.
+ * Meanwhile, the protection of the cross talk detection
+ * is released.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_DONE) {
+ snd_soc_jack_report(nau8825->jack, nau8825->xtalk_event,
+ nau8825->xtalk_event_mask);
+ nau8825_sema_release(nau8825);
+ nau8825->xtalk_protect = false;
+ }
+}
+
+static void nau8825_xtalk_cancel(struct nau8825 *nau8825)
+{
+ /* If the xtalk_protect is true, that means the process is still
+ * on going. The driver forces to cancel the cross talk task and
+ * restores the configuration to original status.
+ */
+ if (nau8825->xtalk_protect) {
+ cancel_work_sync(&nau8825->xtalk_work);
+ nau8825_xtalk_clean(nau8825);
+ }
+ /* Reset parameters for cross talk suppression function */
+ nau8825_sema_reset(nau8825);
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ nau8825->xtalk_protect = false;
+}
+
static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -217,12 +885,36 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
case NAU8825_REG_SARDOUT_RAM_STATUS:
case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
case NAU8825_REG_GENERAL_STATUS:
+ case NAU8825_REG_BIQ_CTRL ... NAU8825_REG_BIQ_COF10:
return true;
default:
return false;
}
}
+static int nau8825_adc_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 nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!nau8825->irq)
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -270,6 +962,54 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int nau8825_biq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ regmap_raw_read(component->regmap, NAU8825_REG_BIQ_COF1,
+ ucontrol->value.bytes.data, params->max);
+ return 0;
+}
+
+static int nau8825_biq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+ NAU8825_BIQ_WRT_EN, 0);
+ regmap_raw_write(component->regmap, NAU8825_REG_BIQ_COF1,
+ data, params->max);
+ regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+ NAU8825_BIQ_WRT_EN, NAU8825_BIQ_WRT_EN);
+
+ kfree(data);
+ return 0;
+}
+
+static const char * const nau8825_biq_path[] = {
+ "ADC", "DAC"
+};
+
+static const struct soc_enum nau8825_biq_path_enum =
+ SOC_ENUM_SINGLE(NAU8825_REG_BIQ_CTRL, NAU8825_BIQ_PATH_SFT,
+ ARRAY_SIZE(nau8825_biq_path), nau8825_biq_path);
+
static const char * const nau8825_adc_decimation[] = {
"32", "64", "128", "256"
};
@@ -306,6 +1046,10 @@ static const struct snd_kcontrol_new nau8825_controls[] = {
SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+ /* programmable biquad filter */
+ SOC_ENUM("BIQ Path Select", nau8825_biq_path_enum),
+ SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
+ nau8825_biq_coeff_get, nau8825_biq_coeff_put),
};
/* DAC Mux 0x33[9] and 0x34[9] */
@@ -338,7 +1082,9 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
NULL, 0),
- SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0,
+ nau8825_adc_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
0),
@@ -592,9 +1338,6 @@ int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
- regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
- NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
-
return 0;
}
EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
@@ -602,24 +1345,21 @@ EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
static bool nau8825_is_jack_inserted(struct regmap *regmap)
{
- int status;
+ bool active_high, is_high;
+ int status, jkdet;
+ regmap_read(regmap, NAU8825_REG_JACK_DET_CTRL, &jkdet);
+ active_high = jkdet & NAU8825_JACK_POLARITY;
regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
- return !(status & NAU8825_GPIO2JD1);
+ is_high = status & NAU8825_GPIO2JD1;
+ /* return jack connection status according to jack insertion logic
+ * active high or active low.
+ */
+ return active_high == is_high;
}
static void nau8825_restart_jack_detection(struct regmap *regmap)
{
- /* Chip needs one FSCLK cycle in order to generate interrupts,
- * as we cannot guarantee one will be provided by the system. Turning
- * master mode on then off enables us to generate that FSCLK cycle
- * with a minimum of contention on the clock bus.
- */
- regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
- NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
- regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
- NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
-
/* this will restart the entire jack detection process including MIC/GND
* switching and create interrupts. We have to go from 0 to 1 and back
* to 0 to restart.
@@ -630,11 +1370,30 @@ static void nau8825_restart_jack_detection(struct regmap *regmap)
NAU8825_JACK_DET_RESTART, 0);
}
+static void nau8825_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+ for (i = 0; i < NAU8825_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+ }
+}
+
static void nau8825_eject_jack(struct nau8825 *nau8825)
{
struct snd_soc_dapm_context *dapm = nau8825->dapm;
struct regmap *regmap = nau8825->regmap;
+ /* Force to cancel the cross talk detection process */
+ nau8825_xtalk_cancel(nau8825);
+
snd_soc_dapm_disable_pin(dapm, "SAR");
snd_soc_dapm_disable_pin(dapm, "MICBIAS");
/* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
@@ -644,6 +1403,69 @@ static void nau8825_eject_jack(struct nau8825 *nau8825)
regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
snd_soc_dapm_sync(dapm);
+
+ /* Clear all interruption status */
+ nau8825_int_status_clear_all(regmap);
+
+ /* Enable the insertion interruption, disable the ejection inter-
+ * ruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_EJECT_DIS | NAU8825_IRQ_INSERT_DIS,
+ NAU8825_IRQ_EJECT_DIS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_INSERT_EN,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+ NAU8825_IRQ_HEADSET_COMPLETE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+
+ /* Disable ADC needed for interruptions at audo mode */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, 0);
+
+ /* Close clock for jack type detection at manual mode */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+}
+
+/* Enable audo mode interruptions with internal clock. */
+static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Enable headset jack type detection complete interruption and
+ * jack ejection interruption.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+ /* Enable internal VCO needed for interruptions */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+
+ /* Enable ADC needed for interruptions */
+ regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+
+ /* Chip needs one FSCLK cycle in order to generate interruptions,
+ * as we cannot guarantee one will be provided by the system. Turning
+ * master mode on then off enables us to generate that FSCLK cycle
+ * with a minimum of contention on the clock bus.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+ regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+ NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+ /* Not bypass de-bounce circuit */
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, 0);
+
+ /* Unmask all interruptions */
+ regmap_write(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+
+ /* Restart the jack detection process at auto mode */
+ nau8825_restart_jack_detection(regmap);
}
static int nau8825_button_decode(int value)
@@ -676,6 +1498,11 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
mic_detected = (jack_status_reg >> 10) & 3;
+ /* The JKSLV and JKR2 all detected in high impedance headset */
+ if (mic_detected == 0x3)
+ nau8825->high_imped = true;
+ else
+ nau8825->high_imped = false;
switch (mic_detected) {
case 0:
@@ -773,6 +1600,33 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
} else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
if (nau8825_is_jack_inserted(regmap)) {
event |= nau8825_jack_insert(nau8825);
+ if (!nau8825->high_imped) {
+ /* Apply the cross talk suppression in the
+ * headset without high impedance.
+ */
+ if (!nau8825->xtalk_protect) {
+ /* Raise protection for cross talk de-
+ * tection if no protection before.
+ * The driver has to cancel the pro-
+ * cess and restore changes if process
+ * is ongoing when ejection.
+ */
+ nau8825->xtalk_protect = true;
+ nau8825_sema_acquire(nau8825, 0);
+ }
+ /* Startup cross talk detection process */
+ nau8825->xtalk_state = NAU8825_XTALK_PREPARE;
+ schedule_work(&nau8825->xtalk_work);
+ } else {
+ /* The cross talk suppression shouldn't apply
+ * in the headset with high impedance. Thus,
+ * relieve the protection raised before.
+ */
+ if (nau8825->xtalk_protect) {
+ nau8825_sema_release(nau8825);
+ nau8825->xtalk_protect = false;
+ }
+ }
} else {
dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
nau8825_eject_jack(nau8825);
@@ -780,6 +1634,37 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
event_mask |= SND_JACK_HEADSET;
clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+ /* Record the interruption report event for driver to report
+ * the event later. The jack report will delay until cross
+ * talk detection process is done.
+ */
+ if (nau8825->xtalk_state == NAU8825_XTALK_PREPARE) {
+ nau8825->xtalk_event = event;
+ nau8825->xtalk_event_mask = event_mask;
+ }
+ } else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) {
+ schedule_work(&nau8825->xtalk_work);
+ clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ;
+ } else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) ==
+ NAU8825_JACK_INSERTION_DETECTED) {
+ /* One more step to check GPIO status directly. Thus, the
+ * driver can confirm the real insertion interruption because
+ * the intrruption at manual mode has bypassed debounce
+ * circuit which can get rid of unstable status.
+ */
+ if (nau8825_is_jack_inserted(regmap)) {
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_INSERT_DIS,
+ NAU8825_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_INSERT_EN, NAU8825_IRQ_INSERT_EN);
+ /* Enable interruption for jack type detection at audo
+ * mode which can detect microphone and jack type.
+ */
+ nau8825_setup_auto_irq(nau8825);
+ }
}
if (!clear_irq)
@@ -787,7 +1672,12 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
/* clears the rightmost interruption */
regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
- if (event_mask)
+ /* Delay jack report until cross talk detection is done. It can avoid
+ * application to do playback preparation when cross talk detection
+ * process is still working. Otherwise, the resource like clock and
+ * power will be issued by them at the same time and conflict happens.
+ */
+ if (event_mask && nau8825->xtalk_state == NAU8825_XTALK_DONE)
snd_soc_jack_report(nau8825->jack, event, event_mask);
return IRQ_HANDLED;
@@ -921,11 +1811,16 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK,
(0x2 << NAU8825_RDAC_CLK_DELAY_SFT) |
(0x3 << NAU8825_RDAC_VREF_SFT));
+ /* Config L/R channel */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+ NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+ NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R);
}
static const struct regmap_config nau8825_regmap_config = {
- .val_bits = 16,
- .reg_bits = 16,
+ .val_bits = NAU8825_REG_DATA_LEN,
+ .reg_bits = NAU8825_REG_ADDR_LEN,
.max_register = NAU8825_REG_MAX,
.readable_reg = nau8825_readable_reg,
@@ -944,18 +1839,15 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
nau8825->dapm = dapm;
- /* The interrupt clock is gated by x1[10:8],
- * one of them needs to be enabled all the time for
- * interrupts to happen.
- */
- snd_soc_dapm_force_enable_pin(dapm, "DDACR");
- snd_soc_dapm_sync(dapm);
+ return 0;
+}
- /* Unmask interruptions. Handler uses dapm object so we can enable
- * interruptions only after dapm is fully initialized.
- */
- regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
- nau8825_restart_jack_detection(nau8825->regmap);
+static int nau8825_codec_remove(struct snd_soc_codec *codec)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+ /* Cancel and reset cross tak suppresstion detection funciton */
+ nau8825_xtalk_cancel(nau8825);
return 0;
}
@@ -973,8 +1865,8 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
struct nau8825_fll *fll_param)
{
- u64 fvco;
- unsigned int fref, i;
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
/* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
* freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
@@ -999,18 +1891,23 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
fll_param->ratio = fll_ratio[i].val;
/* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
- * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
* guaranteed across the full range of operation.
* FDCO = freq_out * 2 * mclk_src_scaling
*/
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
- if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
- break;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
}
- if (i == ARRAY_SIZE(mclk_src_scaling))
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
return -EINVAL;
- fll_param->mclk_src = mclk_src_scaling[i].val;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
/* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
* input based on FDCO, FREF and FLL ratio.
@@ -1025,7 +1922,8 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
struct nau8825_fll *fll_param)
{
regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
- NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
+ NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK,
+ NAU8825_CLK_SRC_MCLK | fll_param->mclk_src);
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
NAU8825_FLL_RATIO_MASK, fll_param->ratio);
/* FLL 16-bit fractional input */
@@ -1038,10 +1936,25 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
/* select divided VCO input */
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
- NAU8825_FLL_FILTER_SW_MASK, 0x0000);
- /* FLL sigma delta modulator enable */
- regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
- NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
+ NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_MASK,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_FILTER);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN, NAU8825_SDM_EN);
+ } else {
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+ NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+ NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU);
+ regmap_update_bits(nau8825->regmap,
+ NAU8825_REG_FLL6, NAU8825_SDM_EN, 0);
+ }
}
/* freq_out must be 256*Fs in order to achieve the best performance */
@@ -1069,6 +1982,45 @@ static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
return 0;
}
+static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq)
+{
+ int ret = 0;
+
+ nau8825->mclk = devm_clk_get(nau8825->dev, "mclk");
+ if (IS_ERR(nau8825->mclk)) {
+ dev_info(nau8825->dev, "No 'mclk' clock found, assume MCLK is managed externally");
+ return 0;
+ }
+
+ if (!nau8825->mclk_freq) {
+ ret = clk_prepare_enable(nau8825->mclk);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+ return ret;
+ }
+ }
+
+ if (nau8825->mclk_freq != freq) {
+ freq = clk_round_rate(nau8825->mclk, freq);
+ ret = clk_set_rate(nau8825->mclk, freq);
+ if (ret) {
+ dev_err(nau8825->dev, "Unable to set mclk rate\n");
+ return ret;
+ }
+ nau8825->mclk_freq = freq;
+ }
+
+ return 0;
+}
+
+static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap)
+{
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_DCO_EN, 0);
+}
+
static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
unsigned int freq)
{
@@ -1076,40 +2028,106 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
int ret;
switch (clk_id) {
+ case NAU8825_CLK_DIS:
+ /* Clock provided externally and disable internal VCO clock */
+ nau8825_configure_mclk_as_sysclk(regmap);
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
+ }
+
+ break;
case NAU8825_CLK_MCLK:
+ /* Acquire the semaphone to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 2 * HZ);
+ nau8825_configure_mclk_as_sysclk(regmap);
+ /* MCLK not changed by clock tree */
regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
- NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
- regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+ NAU8825_CLK_MCLK_SRC_MASK, 0);
+ /* Release the semaphone. */
+ nau8825_sema_release(nau8825);
- /* We selected MCLK source but the clock itself managed externally */
- if (!nau8825->mclk)
- break;
+ ret = nau8825_mclk_prepare(nau8825, freq);
+ if (ret)
+ return ret;
- if (!nau8825->mclk_freq) {
- ret = clk_prepare_enable(nau8825->mclk);
- if (ret) {
- dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
- return ret;
- }
+ break;
+ case NAU8825_CLK_INTERNAL:
+ if (nau8825_is_jack_inserted(nau8825->regmap)) {
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_DCO_EN, NAU8825_DCO_EN);
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ /* Decrease the VCO frequency for power saving */
+ regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+ NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+ regmap_update_bits(regmap, NAU8825_REG_FLL1,
+ NAU8825_FLL_RATIO_MASK, 0x10);
+ regmap_update_bits(regmap, NAU8825_REG_FLL6,
+ NAU8825_SDM_EN, NAU8825_SDM_EN);
+ } else {
+ /* The clock turns off intentionally for power saving
+ * when no headset connected.
+ */
+ nau8825_configure_mclk_as_sysclk(regmap);
+ dev_warn(nau8825->dev, "Disable clock for power saving when no headset connected\n");
+ }
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
}
- if (nau8825->mclk_freq != freq) {
- nau8825->mclk_freq = freq;
+ break;
+ case NAU8825_CLK_FLL_MCLK:
+ /* Acquire the semaphone to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 2 * HZ);
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_MCLK);
+ /* Release the semaphone. */
+ nau8825_sema_release(nau8825);
- freq = clk_round_rate(nau8825->mclk, freq);
- ret = clk_set_rate(nau8825->mclk, freq);
- if (ret) {
- dev_err(nau8825->dev, "Unable to set mclk rate\n");
- return ret;
- }
+ ret = nau8825_mclk_prepare(nau8825, freq);
+ if (ret)
+ return ret;
+
+ break;
+ case NAU8825_CLK_FLL_BLK:
+ /* Acquire the semaphone to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 2 * HZ);
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_BLK);
+ /* Release the semaphone. */
+ nau8825_sema_release(nau8825);
+
+ if (nau8825->mclk_freq) {
+ clk_disable_unprepare(nau8825->mclk);
+ nau8825->mclk_freq = 0;
}
break;
- case NAU8825_CLK_INTERNAL:
- regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
- NAU8825_DCO_EN);
- regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
- NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+ case NAU8825_CLK_FLL_FS:
+ /* Acquire the semaphone to synchronize the playback and
+ * interrupt handler. In order to avoid the playback inter-
+ * fered by cross talk process, the driver make the playback
+ * preparation halted until cross talk process finish.
+ */
+ nau8825_sema_acquire(nau8825, 2 * HZ);
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_FS);
+ /* Release the semaphone. */
+ nau8825_sema_release(nau8825);
if (nau8825->mclk_freq) {
clk_disable_unprepare(nau8825->mclk);
@@ -1135,6 +2153,31 @@ static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
return nau8825_configure_sysclk(nau8825, clk_id, freq);
}
+static int nau8825_resume_setup(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Close clock when jack type detection at manual mode */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+
+ /* Clear all interruption status */
+ nau8825_int_status_clear_all(regmap);
+
+ /* Enable both insertion and ejection interruptions, and then
+ * bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN |
+ NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0);
+
+ return 0;
+}
+
static int nau8825_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -1157,10 +2200,22 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
+ /* Setup codec configuration after resume */
+ nau8825_resume_setup(nau8825);
}
break;
case SND_SOC_BIAS_OFF:
+ /* Cancel and reset cross talk detection funciton */
+ nau8825_xtalk_cancel(nau8825);
+ /* Turn off all interruptions before system shutdown. Keep the
+ * interruption quiet before resume setup completes.
+ */
+ regmap_write(nau8825->regmap,
+ NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff);
+ /* Disable ADC needed for interruptions at audo mode */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, 0);
if (nau8825->mclk_freq)
clk_disable_unprepare(nau8825->mclk);
break;
@@ -1168,57 +2223,46 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-#ifdef CONFIG_PM
-static int nau8825_suspend(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec)
{
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
disable_irq(nau8825->irq);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
regcache_cache_only(nau8825->regmap, true);
regcache_mark_dirty(nau8825->regmap);
return 0;
}
-static int nau8825_resume(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_resume(struct snd_soc_codec *codec)
{
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
- /* The chip may lose power and reset in S3. regcache_sync restores
- * register values including configurations for sysclk, irq, and
- * jack/button detection.
- */
regcache_cache_only(nau8825->regmap, false);
regcache_sync(nau8825->regmap);
-
- /* Check the jack plug status directly. If the headset is unplugged
- * during S3 when the chip has no power, there will be no jack
- * detection irq even after the nau8825_restart_jack_detection below,
- * because the chip just thinks no headset has ever been plugged in.
- */
- if (!nau8825_is_jack_inserted(nau8825->regmap)) {
- nau8825_eject_jack(nau8825);
- snd_soc_jack_report(nau8825->jack, 0, SND_JACK_HEADSET);
+ if (nau8825_is_jack_inserted(nau8825->regmap)) {
+ /* If the jack is inserted, we need to check whether the play-
+ * back is active before suspend. If active, the driver has to
+ * raise the protection for cross talk function to avoid the
+ * playback recovers before cross talk process finish. Other-
+ * wise, the playback will be interfered by cross talk func-
+ * tion. It is better to apply hardware related parameters
+ * before starting playback or record.
+ */
+ if (nau8825_dai_is_active(nau8825)) {
+ nau8825->xtalk_protect = true;
+ nau8825_sema_acquire(nau8825, 0);
+ }
}
-
enable_irq(nau8825->irq);
- /* Run jack detection to check the type (OMTP or CTIA) of the headset
- * if there is one. This handles the case where a different type of
- * headset is plugged in during S3. This triggers an IRQ iff a headset
- * is already plugged in.
- */
- nau8825_restart_jack_detection(nau8825->regmap);
-
return 0;
}
-#else
-#define nau8825_suspend NULL
-#define nau8825_resume NULL
-#endif
static struct snd_soc_codec_driver nau8825_codec_driver = {
.probe = nau8825_codec_probe,
+ .remove = nau8825_codec_remove,
.set_sysclk = nau8825_set_sysclk,
.set_pll = nau8825_set_pll,
.set_bias_level = nau8825_set_bias_level,
@@ -1318,22 +2362,8 @@ static int nau8825_read_device_properties(struct device *dev,
static int nau8825_setup_irq(struct nau8825 *nau8825)
{
- struct regmap *regmap = nau8825->regmap;
int ret;
- /* IRQ Output Enable */
- regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
- NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
-
- /* Enable internal VCO needed for interruptions */
- nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
-
- /* Enable DDACR needed for interrupts
- * It is the same as force_enable_pin("DDACR") we do later
- */
- regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
- NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR);
-
ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"nau8825", nau8825);
@@ -1370,6 +2400,13 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
return PTR_ERR(nau8825->regmap);
nau8825->dev = dev;
nau8825->irq = i2c->irq;
+ /* Initiate parameters, semaphone and work queue which are needed in
+ * cross talk suppression measurment function.
+ */
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ nau8825->xtalk_protect = false;
+ sema_init(&nau8825->xtalk_sem, 1);
+ INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work);
nau8825_print_device_properties(nau8825);
@@ -1405,6 +2442,7 @@ static const struct i2c_device_id nau8825_i2c_ids[] = {
{ "nau8825", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id nau8825_of_ids[] = {
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index 8ceb5f385478..1c63e2abafa9 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -93,12 +93,21 @@
#define NAU8825_REG_CHARGE_PUMP_INPUT_READ 0x81
#define NAU8825_REG_GENERAL_STATUS 0x82
#define NAU8825_REG_MAX NAU8825_REG_GENERAL_STATUS
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8825_REG_ADDR_LEN 16
+#define NAU8825_REG_DATA_LEN 16
/* ENA_CTRL (0x1) */
#define NAU8825_ENABLE_DACR_SFT 10
#define NAU8825_ENABLE_DACR (1 << NAU8825_ENABLE_DACR_SFT)
#define NAU8825_ENABLE_DACL_SFT 9
+#define NAU8825_ENABLE_DACL (1 << NAU8825_ENABLE_DACL_SFT)
#define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_ADC (1 << NAU8825_ENABLE_ADC_SFT)
+#define NAU8825_ENABLE_ADC_CLK_SFT 7
+#define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT)
+#define NAU8825_ENABLE_DAC_CLK_SFT 6
+#define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT)
#define NAU8825_ENABLE_SAR_SFT 1
/* CLK_DIVIDER (0x3) */
@@ -113,20 +122,28 @@
/* FLL3 (0x06) */
#define NAU8825_FLL_INTEGER_MASK (0x3ff << 0)
+#define NAU8825_FLL_CLK_SRC_SFT 10
+#define NAU8825_FLL_CLK_SRC_MASK (0x3 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_MCLK (0 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_BLK (0x2 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_FS (0x3 << NAU8825_FLL_CLK_SRC_SFT)
/* FLL4 (0x07) */
#define NAU8825_FLL_REF_DIV_MASK (0x3 << 10)
/* FLL5 (0x08) */
-#define NAU8825_FLL_FILTER_SW_MASK (0x1 << 14)
+#define NAU8825_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8825_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8825_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8825_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_FILTER (0x0 << 12)
/* FLL6 (0x9) */
-#define NAU8825_DCO_EN_MASK (0x1 << 15)
#define NAU8825_DCO_EN (0x1 << 15)
-#define NAU8825_DCO_DIS (0x0 << 15)
-#define NAU8825_SDM_EN_MASK (0x1 << 14)
#define NAU8825_SDM_EN (0x1 << 14)
-#define NAU8825_SDM_DIS (0x0 << 14)
/* HSD_CTRL (0xc) */
#define NAU8825_HSD_AUTO_MODE (1 << 6)
@@ -136,6 +153,7 @@
/* JACK_DET_CTRL (0xd) */
#define NAU8825_JACK_DET_RESTART (1 << 9)
+#define NAU8825_JACK_DET_DB_BYPASS (1 << 8)
#define NAU8825_JACK_INSERT_DEBOUNCE_SFT 5
#define NAU8825_JACK_INSERT_DEBOUNCE_MASK (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
#define NAU8825_JACK_EJECT_DEBOUNCE_SFT 2
@@ -145,9 +163,11 @@
/* INTERRUPT_MASK (0xf) */
#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_RMS_EN (1 << 8)
#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
#define NAU8825_IRQ_EJECT_EN (1 << 2)
+#define NAU8825_IRQ_INSERT_EN (1 << 0)
/* IRQ_STATUS (0x10) */
#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
@@ -168,6 +188,7 @@
#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
#define NAU8825_IRQ_EJECT_DIS (1 << 2)
+#define NAU8825_IRQ_INSERT_DIS (1 << 0)
/* SAR_CTRL (0x13) */
#define NAU8825_SAR_ADC_EN_SFT 12
@@ -217,10 +238,21 @@
/* I2S_PCM_CTRL2 (0x1d) */
#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_DRV_SFT 12
+#define NAU8825_I2S_DRV_MASK (0x3 << NAU8825_I2S_DRV_SFT)
#define NAU8825_I2S_MS_SFT 3
#define NAU8825_I2S_MS_MASK (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_MASTER (1 << NAU8825_I2S_MS_SFT)
#define NAU8825_I2S_MS_SLAVE (0 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_BLK_DIV_MASK 0x7
+
+/* BIQ_CTRL (0x20) */
+#define NAU8825_BIQ_WRT_SFT 4
+#define NAU8825_BIQ_WRT_EN (1 << NAU8825_BIQ_WRT_SFT)
+#define NAU8825_BIQ_PATH_SFT 0
+#define NAU8825_BIQ_PATH_MASK (1 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_ADC (0 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_DAC (1 << NAU8825_BIQ_PATH_SFT)
/* ADC_RATE (0x2b) */
#define NAU8825_ADC_SYNC_DOWN_SFT 0
@@ -239,22 +271,72 @@
#define NAU8825_DAC_OVERSAMPLE_128 2
#define NAU8825_DAC_OVERSAMPLE_32 4
+/* ADC_DGAIN_CTRL (0x30) */
+#define NAU8825_ADC_DIG_VOL_MASK 0xff
+
/* MUTE_CTRL (0x31) */
#define NAU8825_DAC_ZERO_CROSSING_EN (1 << 9)
#define NAU8825_DAC_SOFT_MUTE (1 << 9)
/* HSVOL_CTRL (0x32) */
#define NAU8825_HP_MUTE (1 << 15)
+#define NAU8825_HP_MUTE_AUTO (1 << 14)
+#define NAU8825_HPL_MUTE (1 << 13)
+#define NAU8825_HPR_MUTE (1 << 12)
+#define NAU8825_HPL_VOL_SFT 6
+#define NAU8825_HPL_VOL_MASK (0x3f << NAU8825_HPL_VOL_SFT)
+#define NAU8825_HPR_VOL_SFT 0
+#define NAU8825_HPR_VOL_MASK (0x3f << NAU8825_HPR_VOL_SFT)
+#define NAU8825_HP_VOL_MIN 0x36
/* DACL_CTRL (0x33) */
#define NAU8825_DACL_CH_SEL_SFT 9
+#define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_L (0x0 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_R (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_VOL_MASK 0xff
/* DACR_CTRL (0x34) */
#define NAU8825_DACR_CH_SEL_SFT 9
+#define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_L (0x0 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_R (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_VOL_MASK 0xff
+
+/* IMM_MODE_CTRL (0x4C) */
+#define NAU8825_IMM_THD_SFT 8
+#define NAU8825_IMM_THD_MASK (0x3f << NAU8825_IMM_THD_SFT)
+#define NAU8825_IMM_GEN_VOL_SFT 6
+#define NAU8825_IMM_GEN_VOL_MASK (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_2nd (0x0 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_4th (0x1 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_8th (0x2 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_16th (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+
+#define NAU8825_IMM_CYC_SFT 4
+#define NAU8825_IMM_CYC_MASK (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_1024 (0x0 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_2048 (0x1 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_4096 (0x2 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_8192 (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_EN (1 << 3)
+#define NAU8825_IMM_DAC_SRC_MASK 0x7
+#define NAU8825_IMM_DAC_SRC_BIQ 0x0
+#define NAU8825_IMM_DAC_SRC_DRC 0x1
+#define NAU8825_IMM_DAC_SRC_MIX 0x2
+#define NAU8825_IMM_DAC_SRC_SIN 0x3
/* CLASSG_CTRL (0x50) */
#define NAU8825_CLASSG_TIMER_SFT 8
#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_1ms (0x1 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_2ms (0x2 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_8ms (0x4 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_16ms (0x8 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_32ms (0x10 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_64ms (0x20 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_LDAC_EN (0x1 << 2)
+#define NAU8825_CLASSG_RDAC_EN (0x1 << 1)
#define NAU8825_CLASSG_EN (1 << 0)
/* I2C_DEVICE_ID (0x58) */
@@ -263,7 +345,12 @@
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
/* BIAS_ADJ (0x66) */
-#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8)
+#define NAU8825_BIAS_HPR_IMP (1 << 15)
+#define NAU8825_BIAS_HPL_IMP (1 << 14)
+#define NAU8825_BIAS_TESTDAC_SFT 8
+#define NAU8825_BIAS_TESTDAC_EN (0x3 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACR_EN (0x2 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACL_EN (0x1 << NAU8825_BIAS_TESTDAC_SFT)
#define NAU8825_BIAS_VMID (1 << 6)
#define NAU8825_BIAS_VMID_SEL_SFT 4
#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
@@ -282,6 +369,11 @@
#define NAU8825_POWERUP_ADCL (1 << 6)
/* RDAC (0x73) */
+#define NAU8825_RDAC_FS_BCLK_ENB (1 << 15)
+#define NAU8825_RDAC_EN_SFT 12
+#define NAU8825_RDAC_EN (0x3 << NAU8825_RDAC_EN_SFT)
+#define NAU8825_RDAC_CLK_EN_SFT 8
+#define NAU8825_RDAC_CLK_EN (0x3 << NAU8825_RDAC_CLK_EN_SFT)
#define NAU8825_RDAC_CLK_DELAY_SFT 4
#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
#define NAU8825_RDAC_VREF_SFT 2
@@ -318,8 +410,21 @@
/* System Clock Source */
enum {
- NAU8825_CLK_MCLK = 0,
+ NAU8825_CLK_DIS = 0,
+ NAU8825_CLK_MCLK,
NAU8825_CLK_INTERNAL,
+ NAU8825_CLK_FLL_MCLK,
+ NAU8825_CLK_FLL_BLK,
+ NAU8825_CLK_FLL_FS,
+};
+
+/* Cross talk detection state */
+enum {
+ NAU8825_XTALK_PREPARE = 0,
+ NAU8825_XTALK_HPR_R2L,
+ NAU8825_XTALK_HPL_R2L,
+ NAU8825_XTALK_IMM,
+ NAU8825_XTALK_DONE,
};
struct nau8825 {
@@ -328,6 +433,8 @@ struct nau8825 {
struct snd_soc_dapm_context *dapm;
struct snd_soc_jack *jack;
struct clk *mclk;
+ struct work_struct xtalk_work;
+ struct semaphore xtalk_sem;
int irq;
int mclk_freq; /* 0 - mclk is disabled */
int button_pressed;
@@ -346,6 +453,12 @@ struct nau8825 {
int key_debounce;
int jack_insert_debounce;
int jack_eject_debounce;
+ int high_imped;
+ int xtalk_state;
+ int xtalk_event;
+ int xtalk_event_mask;
+ bool xtalk_protect;
+ int imp_rms[NAU8825_XTALK_IMM];
};
int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index 58325234285c..33e1fc2d1598 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -73,7 +73,7 @@ static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
return !((reg == 0x00) || (reg == 0x0f));
}
-static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg)
{
return pcm1681_accessible_reg(dev, reg) &&
(reg != PCM1681_ZERO_DETECT_STATUS);
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
index 06a66579ca6d..88fbdd184aa0 100644
--- a/sound/soc/codecs/pcm179x.c
+++ b/sound/soc/codecs/pcm179x.c
@@ -59,7 +59,7 @@ static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
return reg >= 0x10 && reg <= 0x17;
}
-static bool pcm179x_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
{
bool accessible;
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
index ed515677409b..8ba322a00363 100644
--- a/sound/soc/codecs/pcm5102a.c
+++ b/sound/soc/codecs/pcm5102a.c
@@ -57,7 +57,6 @@ static struct platform_driver pcm5102a_codec_driver = {
.remove = pcm5102a_remove,
.driver = {
.name = "pcm5102a-codec",
- .owner = THIS_MODULE,
.of_match_table = pcm5102a_of_match,
},
};
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 1bd31644a782..74c0e4eb3788 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1100,6 +1100,13 @@ static const struct dmi_system_id force_combo_jack_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
}
},
+ {
+ .ident = "Intel Kabylake RVP",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
+ }
+ },
+
{ }
};
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
new file mode 100644
index 000000000000..77ff8ebe6dfb
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -0,0 +1,447 @@
+/*
+ * rt5514-spi.c -- RT5514 SPI driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.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/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.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 "rt5514-spi.h"
+
+static struct spi_device *rt5514_spi;
+
+struct rt5514_dsp {
+ struct device *dev;
+ struct delayed_work copy_work;
+ struct mutex dma_lock;
+ struct snd_pcm_substream *substream;
+ unsigned int buf_base, buf_limit, buf_rp;
+ size_t buf_size;
+ size_t dma_offset;
+ size_t dsp_offset;
+};
+
+static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = 0x20000 / 8,
+ .periods_min = 8,
+ .periods_max = 8,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 0x20000,
+};
+
+static struct snd_soc_dai_driver rt5514_spi_dai = {
+ .name = "rt5514-dsp-cpu-dai",
+ .id = 0,
+ .capture = {
+ .stream_name = "DSP Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+};
+
+static void rt5514_spi_copy_work(struct work_struct *work)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ container_of(work, struct rt5514_dsp, copy_work.work);
+ struct snd_pcm_runtime *runtime;
+ size_t period_bytes, truncated_bytes = 0;
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ if (!rt5514_dsp->substream) {
+ dev_err(rt5514_dsp->dev, "No pcm substream\n");
+ goto done;
+ }
+
+ runtime = rt5514_dsp->substream->runtime;
+ period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
+
+ if (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset < period_bytes)
+ period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset;
+
+ if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ period_bytes);
+
+ if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit)
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base;
+ else
+ rt5514_dsp->buf_rp += period_bytes;
+ } else {
+ truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp;
+ rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+ runtime->dma_area + rt5514_dsp->dma_offset,
+ truncated_bytes);
+
+ rt5514_spi_burst_read(rt5514_dsp->buf_base,
+ runtime->dma_area + rt5514_dsp->dma_offset +
+ truncated_bytes, period_bytes - truncated_bytes);
+
+ rt5514_dsp->buf_rp = rt5514_dsp->buf_base +
+ period_bytes - truncated_bytes;
+ }
+
+ rt5514_dsp->dma_offset += period_bytes;
+ if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
+ rt5514_dsp->dma_offset = 0;
+
+ rt5514_dsp->dsp_offset += period_bytes;
+
+ snd_pcm_period_elapsed(rt5514_dsp->substream);
+
+ if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+done:
+ mutex_unlock(&rt5514_dsp->dma_lock);
+}
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
+
+ return 0;
+}
+
+static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ int ret;
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ rt5514_dsp->substream = substream;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return ret;
+}
+
+static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ rt5514_dsp->substream = NULL;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ cancel_delayed_work_sync(&rt5514_dsp->copy_work);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int rt5514_spi_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ u8 buf[8];
+
+ rt5514_dsp->dma_offset = 0;
+ rt5514_dsp->dsp_offset = 0;
+
+ /**
+ * The address area x1800XXXX is the register address, and it cannot
+ * support spi burst read perfectly. So we use the spi burst read
+ * individually to make sure the data correctly.
+ */
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_RP, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_SIZE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_size = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ return 0;
+}
+
+static int rt5514_spi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
+ rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 0);
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
+}
+
+static struct snd_pcm_ops rt5514_spi_pcm_ops = {
+ .open = rt5514_spi_pcm_open,
+ .hw_params = rt5514_spi_hw_params,
+ .hw_free = rt5514_spi_hw_free,
+ .trigger = rt5514_spi_trigger,
+ .prepare = rt5514_spi_prepare,
+ .pointer = rt5514_spi_pcm_pointer,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
+{
+ struct rt5514_dsp *rt5514_dsp;
+
+ rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
+ GFP_KERNEL);
+
+ rt5514_dsp->dev = &rt5514_spi->dev;
+ mutex_init(&rt5514_dsp->dma_lock);
+ INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
+ snd_soc_platform_set_drvdata(platform, rt5514_dsp);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver rt5514_spi_platform = {
+ .probe = rt5514_spi_pcm_probe,
+ .ops = &rt5514_spi_pcm_ops,
+};
+
+static const struct snd_soc_component_driver rt5514_spi_dai_component = {
+ .name = "rt5514-spi-dai",
+};
+
+/**
+ * rt5514_spi_burst_read - Read data from SPI by rt5514 address.
+ * @addr: Start address.
+ * @rxbuf: Data Buffer for reading.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_READ;
+ int status;
+ u8 write_buf[8];
+ unsigned int i, end, offset = 0;
+
+ struct spi_message message;
+ struct spi_transfer x[3];
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof(x));
+
+ x[0].len = 5;
+ x[0].tx_buf = write_buf;
+ spi_message_add_tail(&x[0], &message);
+
+ x[1].len = 4;
+ x[1].tx_buf = write_buf;
+ spi_message_add_tail(&x[1], &message);
+
+ x[2].len = end;
+ x[2].rx_buf = rxbuf + offset;
+ spi_message_add_tail(&x[2], &message);
+
+ status = spi_sync(rt5514_spi, &message);
+
+ if (status)
+ return false;
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ for (i = 0; i < len; i += 8) {
+ write_buf[0] = rxbuf[i + 0];
+ write_buf[1] = rxbuf[i + 1];
+ write_buf[2] = rxbuf[i + 2];
+ write_buf[3] = rxbuf[i + 3];
+ write_buf[4] = rxbuf[i + 4];
+ write_buf[5] = rxbuf[i + 5];
+ write_buf[6] = rxbuf[i + 6];
+ write_buf[7] = rxbuf[i + 7];
+
+ rxbuf[i + 0] = write_buf[7];
+ rxbuf[i + 1] = write_buf[6];
+ rxbuf[i + 2] = write_buf[5];
+ rxbuf[i + 3] = write_buf[4];
+ rxbuf[i + 4] = write_buf[3];
+ rxbuf[i + 5] = write_buf[2];
+ rxbuf[i + 6] = write_buf[1];
+ rxbuf[i + 7] = write_buf[0];
+ }
+
+ return true;
+}
+
+/**
+ * rt5514_spi_burst_write - Write data to SPI by rt5514 address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len)
+{
+ u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE;
+ u8 *write_buf;
+ unsigned int i, end, offset = 0;
+
+ write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+ if (write_buf == NULL)
+ return -ENOMEM;
+
+ while (offset < len) {
+ if (offset + RT5514_SPI_BUF_LEN <= len)
+ end = RT5514_SPI_BUF_LEN;
+ else
+ end = len % RT5514_SPI_BUF_LEN;
+
+ write_buf[0] = spi_cmd;
+ write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+ write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+ write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+ write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+ for (i = 0; i < end; i += 8) {
+ write_buf[i + 12] = txbuf[offset + i + 0];
+ write_buf[i + 11] = txbuf[offset + i + 1];
+ write_buf[i + 10] = txbuf[offset + i + 2];
+ write_buf[i + 9] = txbuf[offset + i + 3];
+ write_buf[i + 8] = txbuf[offset + i + 4];
+ write_buf[i + 7] = txbuf[offset + i + 5];
+ write_buf[i + 6] = txbuf[offset + i + 6];
+ write_buf[i + 5] = txbuf[offset + i + 7];
+ }
+
+ write_buf[end + 5] = spi_cmd;
+
+ spi_write(rt5514_spi, write_buf, end + 6);
+
+ offset += RT5514_SPI_BUF_LEN;
+ }
+
+ kfree(write_buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_write);
+
+static int rt5514_spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ rt5514_spi = spi;
+
+ ret = devm_snd_soc_register_platform(&spi->dev, &rt5514_spi_platform);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register platform.\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &rt5514_spi_dai_component,
+ &rt5514_spi_dai, 1);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register component.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+
+static struct spi_driver rt5514_spi_driver = {
+ .driver = {
+ .name = "rt5514",
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ },
+ .probe = rt5514_spi_probe,
+};
+module_spi_driver(rt5514_spi_driver);
+
+MODULE_DESCRIPTION("RT5514 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h
new file mode 100644
index 000000000000..f69b1cdf2f9b
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.h
@@ -0,0 +1,38 @@
+/*
+ * rt5514-spi.h -- RT5514 driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.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 __RT5514_SPI_H__
+#define __RT5514_SPI_H__
+
+/**
+ * RT5514_SPI_BUF_LEN is the buffer size of SPI master controller.
+*/
+#define RT5514_SPI_BUF_LEN 240
+
+#define RT5514_BUFFER_VOICE_BASE 0x18001034
+#define RT5514_BUFFER_VOICE_LIMIT 0x18001038
+#define RT5514_BUFFER_VOICE_RP 0x1800103c
+#define RT5514_BUFFER_VOICE_SIZE 0x18001040
+
+/* SPI Command */
+enum {
+ RT5514_SPI_CMD_16_READ = 0,
+ RT5514_SPI_CMD_16_WRITE,
+ RT5514_SPI_CMD_32_READ,
+ RT5514_SPI_CMD_32_WRITE,
+ RT5514_SPI_CMD_BURST_READ,
+ RT5514_SPI_CMD_BURST_WRITE,
+};
+
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len);
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len);
+
+#endif /* __RT5514_SPI_H__ */
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index 879bf60f4965..7162f05101d9 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -30,6 +30,9 @@
#include "rl6231.h"
#include "rt5514.h"
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+#include "rt5514-spi.h"
+#endif
static const struct reg_sequence rt5514_i2c_patch[] = {
{0x1800101c, 0x00000000},
@@ -110,6 +113,35 @@ static const struct reg_default rt5514_reg[] = {
{RT5514_VENDOR_ID2, 0x10ec5514},
};
+static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514)
+{
+ /* Reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec);
+ /* LDO_I_limit */
+ regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604);
+ /* I2C bypass enable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001);
+ /* mini-core reset */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b);
+ regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
+ /* I2C bypass disable */
+ regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000);
+ /* PIN config */
+ regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040);
+ /* PLL3(QN)=RCOSC*(10+2) */
+ regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a);
+ /* PLL3 source=RCOSC, fsi=rt_clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b);
+ /* Power on RCOSC, pll3 */
+ regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
+ /* DSP clk source = pll3, ENABLE DSP clk */
+ regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
+ /* Enable DSP clk auto switch */
+ regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001);
+ /* Reduce DSP power */
+ regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001);
+}
+
static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -248,6 +280,74 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt5514->dsp_enabled;
+
+ return 0;
+}
+
+static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_codec *codec = rt5514->codec;
+ const struct firmware *fw = NULL;
+
+ if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
+ return 0;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ rt5514->dsp_enabled = ucontrol->value.integer.value[0];
+
+ if (rt5514->dsp_enabled) {
+ rt5514_enable_dsp_prepare(rt5514);
+
+ request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ff60000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(codec->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ request_firmware(&fw, RT5514_FIRMWARE2, codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ffc0000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(codec->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ /* DSP run */
+ regmap_write(rt5514->i2c_regmap, 0x18002f00,
+ 0x00055148);
+ } else {
+ regmap_multi_reg_write(rt5514->i2c_regmap,
+ rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ }
+
+ return 0;
+}
+
static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
@@ -257,6 +357,8 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
adc_vol_tlv),
+ SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0,
+ rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put),
};
/* ADC Mixer*/
@@ -365,6 +467,35 @@ static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
return 0;
}
+static int rt5514_pre_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 rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /**
+ * If the DSP is enabled in start of recording, the DSP
+ * should be disabled, and sync back to normal recording
+ * settings to make sure recording properly.
+ */
+ if (rt5514->dsp_enabled) {
+ rt5514->dsp_enabled = 0;
+ regmap_multi_reg_write(rt5514->i2c_regmap,
+ rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
+ regcache_mark_dirty(rt5514->regmap);
+ regcache_sync(rt5514->regmap);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC1L"),
@@ -472,6 +603,8 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Audio Interface */
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PRE("DAPM Pre", rt5514_pre_event),
};
static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
@@ -799,10 +932,41 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
+static int rt5514_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (IS_ERR(rt5514->mclk))
+ break;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5514->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5514->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int rt5514_probe(struct snd_soc_codec *codec)
{
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ rt5514->mclk = devm_clk_get(codec->dev, "mclk");
+ if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
rt5514->codec = codec;
return 0;
@@ -858,6 +1022,7 @@ struct snd_soc_dai_driver rt5514_dai[] = {
static struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
.probe = rt5514_probe,
.idle_bias_off = true,
+ .set_bias_level = rt5514_set_bias_level,
.controls = rt5514_snd_controls,
.num_controls = ARRAY_SIZE(rt5514_snd_controls),
.dapm_widgets = rt5514_dapm_widgets,
@@ -871,7 +1036,6 @@ static const struct regmap_config rt5514_i2c_regmap = {
.reg_bits = 32,
.val_bits = 32,
- .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
.readable_reg = rt5514_i2c_readable_register,
.cache_type = REGCACHE_NONE,
@@ -944,7 +1108,7 @@ static int rt5514_i2c_probe(struct i2c_client *i2c,
return -ENODEV;
}
- ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch,
+ ret = regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch,
ARRAY_SIZE(rt5514_i2c_patch));
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n",
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
index 6ad8a612f659..68883c68e999 100644
--- a/sound/soc/codecs/rt5514.h
+++ b/sound/soc/codecs/rt5514.h
@@ -12,6 +12,8 @@
#ifndef __RT5514_H__
#define __RT5514_H__
+#include <linux/clk.h>
+
#define RT5514_DEVICE_ID 0x10ec5514
#define RT5514_RESET 0x2000
@@ -225,6 +227,9 @@
#define RT5514_PLL_INP_MAX 40000000
#define RT5514_PLL_INP_MIN 256000
+#define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin"
+#define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin"
+
/* System Clock Source */
enum {
RT5514_SCLK_S_MCLK,
@@ -240,6 +245,7 @@ enum {
struct rt5514_priv {
struct snd_soc_codec *codec;
struct regmap *i2c_regmap, *regmap;
+ struct clk *mclk;
int sysclk;
int sysclk_src;
int lrck;
@@ -247,6 +253,7 @@ struct rt5514_priv {
int pll_src;
int pll_in;
int pll_out;
+ int dsp_enabled;
};
#endif /* __RT5514_H__ */
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index d70847c9eeb0..490bfe661346 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -63,6 +63,7 @@ static const struct reg_sequence init_list[] = {
{RT5645_PR_BASE + 0x20, 0x611f},
{RT5645_PR_BASE + 0x21, 0x4040},
{RT5645_PR_BASE + 0x23, 0x0004},
+ {RT5645_ASRC_4, 0x0120},
};
static const struct reg_sequence rt5650_init_list[] = {
@@ -157,7 +158,7 @@ static const struct reg_default rt5645_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0120 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
@@ -314,7 +315,7 @@ static const struct reg_default rt5650_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0120 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
@@ -440,6 +441,7 @@ static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case RT5645_RESET:
+ case RT5645_PRIV_INDEX:
case RT5645_PRIV_DATA:
case RT5645_IN1_CTRL1:
case RT5645_IN1_CTRL2:
@@ -740,6 +742,14 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
return ret;
}
+static const char * const rt5645_dac1_vol_ctrl_mode_text[] = {
+ "immediately", "zero crossing", "soft ramp"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1_vol_ctrl_mode, RT5645_PR_BASE,
+ RT5645_DA1_ZDET_SFT, rt5645_dac1_vol_ctrl_mode_text);
+
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -806,6 +816,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
1, 1),
RT5645_HWEQ("Speaker HWEQ"),
+
+ /* Digital Soft Volume Control */
+ SOC_ENUM("DAC1 Digital Volume Control Func", rt5645_dac1_vol_ctrl_mode),
};
/**
@@ -3531,6 +3544,7 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
static const struct acpi_device_id rt5645_acpi_match[] = {
{ "10EC5645", 0 },
{ "10EC5650", 0 },
+ { "10EC5640", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
@@ -3561,6 +3575,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
},
},
+ {
+ .ident = "Microsoft Surface 3",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ },
+ },
{ }
};
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 205e0715c99a..cfc5f97549eb 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -2018,6 +2018,9 @@
/* Codec Private Register definition */
+/* DAC ADC Digital Volume (0x00) */
+#define RT5645_DA1_ZDET_SFT 6
+
/* 3D Speaker Control (0x63) */
#define RT5645_3D_SPK_MASK (0x1 << 15)
#define RT5645_3D_SPK_SFT 15
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 0af5ddbef1da..8ef467f64f03 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -55,6 +55,7 @@ static const struct reg_sequence init_list[] = {
{ RT5670_PR_BASE + 0x14, 0x9a8a },
{ RT5670_PR_BASE + 0x38, 0x3ba1 },
{ RT5670_PR_BASE + 0x3d, 0x3640 },
+ { 0x8a, 0x0123 },
};
static const struct reg_default rt5670_reg[] = {
@@ -131,7 +132,7 @@ static const struct reg_default rt5670_reg[] = {
{ 0x87, 0x0000 },
{ 0x88, 0x0000 },
{ 0x89, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0123 },
{ 0x8b, 0x0000 },
{ 0x8c, 0x0003 },
{ 0x8d, 0x0000 },
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 08b40460663c..527b759c1562 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -38,7 +38,6 @@
/* default value of sgtl5000 registers */
static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_DIG_POWER, 0x0000 },
- { SGTL5000_CHIP_CLK_CTRL, 0x0008 },
{ SGTL5000_CHIP_I2S_CTRL, 0x0010 },
{ SGTL5000_CHIP_SSS_CTRL, 0x0010 },
{ SGTL5000_CHIP_ADCDAC_CTRL, 0x020c },
@@ -47,12 +46,10 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
{ SGTL5000_CHIP_ANA_CTRL, 0x0111 },
- { SGTL5000_CHIP_LINREG_CTRL, 0x0000 },
{ SGTL5000_CHIP_REF_CTRL, 0x0000 },
{ SGTL5000_CHIP_MIC_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
- { SGTL5000_CHIP_ANA_POWER, 0x7060 },
{ SGTL5000_CHIP_PLL_CTRL, 0x5000 },
{ SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_STATUS, 0x0000 },
@@ -92,35 +89,8 @@ static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
"VDDD"
};
-#define LDO_CONSUMER_NAME "VDDD_LDO"
#define LDO_VOLTAGE 1200000
-
-static struct regulator_consumer_supply ldo_consumer[] = {
- REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
-};
-
-static struct regulator_init_data ldo_init_data = {
- .constraints = {
- .min_uV = 1200000,
- .max_uV = 1200000,
- .valid_modes_mask = REGULATOR_MODE_NORMAL,
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
- .num_consumer_supplies = 1,
- .consumer_supplies = &ldo_consumer[0],
-};
-
-/*
- * sgtl5000 internal ldo regulator,
- * enabled when VDDD not provided
- */
-struct ldo_regulator {
- struct regulator_desc desc;
- struct regulator_dev *dev;
- int voltage;
- void *codec_data;
- bool enabled;
-};
+#define LINREG_VDDD ((1600 - LDO_VOLTAGE / 1000) / 50)
enum sgtl5000_micbias_resistor {
SGTL5000_MICBIAS_OFF = 0,
@@ -135,7 +105,7 @@ struct sgtl5000_priv {
int master; /* i2s master or not */
int fmt; /* i2s data format */
struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
- struct ldo_regulator *ldo;
+ int num_supplies;
struct regmap *regmap;
struct clk *mclk;
int revision;
@@ -415,6 +385,9 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
/* tlv for hp volume, -51.5db to 12.0db, step .5db */
static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
+/* tlv for lineout volume, 31 steps of .5db each */
+static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0);
+
static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
/* SOC_DOUBLE_S8_TLV with invert */
{
@@ -443,6 +416,13 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
0, 3, 0, mic_gain_tlv),
+
+ SOC_DOUBLE_TLV("Lineout Playback Volume",
+ SGTL5000_CHIP_LINE_OUT_VOL,
+ SGTL5000_LINE_OUT_VOL_LEFT_SHIFT,
+ SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT,
+ 0x1f, 1,
+ lineout_volume),
};
/* mute the codec used by alsa core */
@@ -778,155 +758,6 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-#ifdef CONFIG_REGULATOR
-static int ldo_regulator_is_enabled(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
- return ldo->enabled;
-}
-
-static int ldo_regulator_enable(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
- struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
- int reg;
-
- if (ldo_regulator_is_enabled(dev))
- return 0;
-
- /* set regulator value firstly */
- reg = (1600 - ldo->voltage / 1000) / 50;
- reg = clamp(reg, 0x0, 0xf);
-
- /* amend the voltage value, unit: uV */
- ldo->voltage = (1600 - reg * 50) * 1000;
-
- /* set voltage to register */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, reg);
-
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINEREG_D_POWERUP,
- SGTL5000_LINEREG_D_POWERUP);
-
- /* when internal ldo is enabled, simple digital power can be disabled */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP,
- 0);
-
- ldo->enabled = 1;
- return 0;
-}
-
-static int ldo_regulator_disable(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
- struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
-
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINEREG_D_POWERUP,
- 0);
-
- /* clear voltage info */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, 0);
-
- ldo->enabled = 0;
-
- return 0;
-}
-
-static int ldo_regulator_get_voltage(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
- return ldo->voltage;
-}
-
-static struct regulator_ops ldo_regulator_ops = {
- .is_enabled = ldo_regulator_is_enabled,
- .enable = ldo_regulator_enable,
- .disable = ldo_regulator_disable,
- .get_voltage = ldo_regulator_get_voltage,
-};
-
-static int ldo_regulator_register(struct snd_soc_codec *codec,
- struct regulator_init_data *init_data,
- int voltage)
-{
- struct ldo_regulator *ldo;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- struct regulator_config config = { };
-
- ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL);
-
- if (!ldo)
- return -ENOMEM;
-
- ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL);
- if (!ldo->desc.name) {
- kfree(ldo);
- dev_err(codec->dev, "failed to allocate decs name memory\n");
- return -ENOMEM;
- }
-
- ldo->desc.type = REGULATOR_VOLTAGE;
- ldo->desc.owner = THIS_MODULE;
- ldo->desc.ops = &ldo_regulator_ops;
- ldo->desc.n_voltages = 1;
-
- ldo->codec_data = codec;
- ldo->voltage = voltage;
-
- config.dev = codec->dev;
- config.driver_data = ldo;
- config.init_data = init_data;
-
- ldo->dev = regulator_register(&ldo->desc, &config);
- if (IS_ERR(ldo->dev)) {
- int ret = PTR_ERR(ldo->dev);
-
- dev_err(codec->dev, "failed to register regulator\n");
- kfree(ldo->desc.name);
- kfree(ldo);
-
- return ret;
- }
- sgtl5000->ldo = ldo;
-
- return 0;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- struct ldo_regulator *ldo = sgtl5000->ldo;
-
- if (!ldo)
- return 0;
-
- regulator_unregister(ldo->dev);
- kfree(ldo->desc.name);
- kfree(ldo);
-
- return 0;
-}
-#else
-static int ldo_regulator_register(struct snd_soc_codec *codec,
- struct regulator_init_data *init_data,
- int voltage)
-{
- dev_err(codec->dev, "this setup needs regulator support in the kernel\n");
- return -EINVAL;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
- return 0;
-}
-#endif
-
/*
* set dac bias
* common state changes:
@@ -940,42 +771,17 @@ static int ldo_regulator_remove(struct snd_soc_codec *codec)
static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- int ret;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- break;
case SND_SOC_BIAS_STANDBY:
- if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
- ret = regulator_bulk_enable(
- ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (ret)
- return ret;
- udelay(10);
-
- regcache_cache_only(sgtl5000->regmap, false);
-
- ret = regcache_sync(sgtl5000->regmap);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to restore cache: %d\n", ret);
-
- regcache_cache_only(sgtl5000->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-
- return ret;
- }
- }
-
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_REFTOP_POWERUP,
+ SGTL5000_REFTOP_POWERUP);
break;
case SND_SOC_BIAS_OFF:
- regcache_cache_only(sgtl5000->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_REFTOP_POWERUP, 0);
break;
}
@@ -1113,7 +919,6 @@ static const u8 vol_quot_table[] = {
* and should be set according to:
* 1. vddd provided by external or not
* 2. vdda and vddio voltage value. > 3.1v or not
- * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd.
*/
static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
{
@@ -1131,7 +936,9 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
- vddd = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer);
+ vddd = (sgtl5000->num_supplies > VDDD)
+ ? regulator_get_voltage(sgtl5000->supplies[VDDD].consumer)
+ : LDO_VOLTAGE;
vdda = vdda / 1000;
vddio = vddio / 1000;
@@ -1178,25 +985,6 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
- /* set voltage to register */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, 0x8);
-
- /*
- * if vddd linear reg has been enabled,
- * simple digital supply should be clear to get
- * proper VDDD voltage.
- */
- if (ana_pwr & SGTL5000_LINEREG_D_POWERUP)
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP,
- 0);
- else
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP |
- SGTL5000_STARTUP_POWERUP,
- 0);
-
/*
* set ADC/DAC VAG to vdda / 2,
* should stay in range (0.8v, 1.575v)
@@ -1256,78 +1044,43 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
return 0;
}
-static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
-{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- /* set internal ldo to 1.2v */
- ret = ldo_regulator_register(codec, &ldo_init_data, LDO_VOLTAGE);
- if (ret) {
- dev_err(codec->dev,
- "Failed to register vddd internal supplies: %d\n", ret);
- return ret;
- }
-
- sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
-
- dev_info(codec->dev, "Using internal LDO instead of VDDD\n");
- return 0;
-}
-
-static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
+static int sgtl5000_enable_regulators(struct i2c_client *client)
{
int ret;
int i;
int external_vddd = 0;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
struct regulator *vddd;
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++)
sgtl5000->supplies[i].supply = supply_names[i];
- /* External VDDD only works before revision 0x11 */
- if (sgtl5000->revision < 0x11) {
- vddd = regulator_get_optional(codec->dev, "VDDD");
- if (IS_ERR(vddd)) {
- /* See if it's just not registered yet */
- if (PTR_ERR(vddd) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- } else {
- external_vddd = 1;
- regulator_put(vddd);
- }
- }
-
- if (!external_vddd) {
- ret = sgtl5000_replace_vddd_with_ldo(codec);
- if (ret)
- return ret;
+ vddd = regulator_get_optional(&client->dev, "VDDD");
+ if (IS_ERR(vddd)) {
+ /* See if it's just not registered yet */
+ if (PTR_ERR(vddd) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ } else {
+ external_vddd = 1;
+ regulator_put(vddd);
}
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->num_supplies = ARRAY_SIZE(sgtl5000->supplies)
+ - 1 + external_vddd;
+ ret = regulator_bulk_get(&client->dev, sgtl5000->num_supplies,
sgtl5000->supplies);
if (ret)
- goto err_ldo_remove;
-
- ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (ret)
- goto err_regulator_free;
-
- /* wait for all power rails bring up */
- udelay(10);
+ return ret;
- return 0;
+ ret = regulator_bulk_enable(sgtl5000->num_supplies,
+ sgtl5000->supplies);
+ if (!ret)
+ usleep_range(10, 20);
+ else
+ regulator_bulk_free(sgtl5000->num_supplies,
+ sgtl5000->supplies);
-err_regulator_free:
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-err_ldo_remove:
- if (!external_vddd)
- ldo_regulator_remove(codec);
return ret;
-
}
static int sgtl5000_probe(struct snd_soc_codec *codec)
@@ -1335,10 +1088,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
int ret;
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- ret = sgtl5000_enable_regulators(codec);
- if (ret)
- return ret;
-
/* power up sgtl5000 */
ret = sgtl5000_set_power_regs(codec);
if (ret)
@@ -1389,25 +1138,11 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
return 0;
err:
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
return ret;
}
static int sgtl5000_remove(struct snd_soc_codec *codec)
{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
return 0;
}
@@ -1448,8 +1183,9 @@ static const struct regmap_config sgtl5000_regmap = {
* and avoid problems like, not being able to probe after an audio playback
* followed by a system reset or a 'reboot' command in Linux
*/
-static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+static void sgtl5000_fill_defaults(struct i2c_client *client)
{
+ struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
int i, ret, val, index;
for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
@@ -1457,10 +1193,10 @@ static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
index = sgtl5000_reg_defaults[i].reg;
ret = regmap_write(sgtl5000->regmap, index, val);
if (ret)
- return ret;
+ dev_err(&client->dev,
+ "%s: error %d setting reg 0x%02x to 0x%04x\n",
+ __func__, ret, index, val);
}
-
- return 0;
}
static int sgtl5000_i2c_probe(struct i2c_client *client,
@@ -1470,16 +1206,23 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
int ret, reg, rev;
struct device_node *np = client->dev.of_node;
u32 value;
+ u16 ana_pwr;
sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL);
if (!sgtl5000)
return -ENOMEM;
+ i2c_set_clientdata(client, sgtl5000);
+
+ ret = sgtl5000_enable_regulators(client);
+ if (ret)
+ return ret;
+
sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap);
if (IS_ERR(sgtl5000->regmap)) {
ret = PTR_ERR(sgtl5000->regmap);
dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
- return ret;
+ goto disable_regs;
}
sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
@@ -1488,21 +1231,25 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
/* Defer the probe to see if the clk will be provided later */
if (ret == -ENOENT)
- return -EPROBE_DEFER;
- return ret;
+ ret = -EPROBE_DEFER;
+ goto disable_regs;
}
ret = clk_prepare_enable(sgtl5000->mclk);
- if (ret)
- return ret;
+ if (ret) {
+ dev_err(&client->dev, "Error enabling clock %d\n", ret);
+ goto disable_regs;
+ }
/* Need 8 clocks before I2C accesses */
udelay(1);
/* read chip information */
ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
- if (ret)
+ if (ret) {
+ dev_err(&client->dev, "Error reading chip id %d\n", ret);
goto disable_clk;
+ }
if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
SGTL5000_PARTID_PART_ID) {
@@ -1516,6 +1263,44 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
sgtl5000->revision = rev;
+ /* reconfigure the clocks in case we're using the PLL */
+ ret = regmap_write(sgtl5000->regmap,
+ SGTL5000_CHIP_CLK_CTRL,
+ SGTL5000_CHIP_CLK_CTRL_DEFAULT);
+ if (ret)
+ dev_err(&client->dev,
+ "Error %d initializing CHIP_CLK_CTRL\n", ret);
+
+ /* Follow section 2.2.1.1 of AN3663 */
+ ana_pwr = SGTL5000_ANA_POWER_DEFAULT;
+ if (sgtl5000->num_supplies <= VDDD) {
+ /* internal VDDD at 1.2V */
+ ret = regmap_update_bits(sgtl5000->regmap,
+ SGTL5000_CHIP_LINREG_CTRL,
+ SGTL5000_LINREG_VDDD_MASK,
+ LINREG_VDDD);
+ if (ret)
+ dev_err(&client->dev,
+ "Error %d setting LINREG_VDDD\n", ret);
+
+ ana_pwr |= SGTL5000_LINEREG_D_POWERUP;
+ dev_info(&client->dev,
+ "Using internal LDO instead of VDDD: check ER1\n");
+ } else {
+ /* using external LDO for VDDD
+ * Clear startup powerup and simple powerup
+ * bits to save power
+ */
+ ana_pwr &= ~(SGTL5000_STARTUP_POWERUP
+ | SGTL5000_LINREG_SIMPLE_POWERUP);
+ dev_dbg(&client->dev, "Using external VDDD\n");
+ }
+ ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+ if (ret)
+ dev_err(&client->dev,
+ "Error %d setting CHIP_ANA_POWER to %04x\n",
+ ret, ana_pwr);
+
if (np) {
if (!of_property_read_u32(np,
"micbias-resistor-k-ohms", &value)) {
@@ -1557,12 +1342,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
}
}
- i2c_set_clientdata(client, sgtl5000);
-
/* Ensure sgtl5000 will start with sane register values */
- ret = sgtl5000_fill_defaults(sgtl5000);
- if (ret)
- goto disable_clk;
+ sgtl5000_fill_defaults(client);
ret = snd_soc_register_codec(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
@@ -1573,6 +1354,11 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
disable_clk:
clk_disable_unprepare(sgtl5000->mclk);
+
+disable_regs:
+ regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
+ regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+
return ret;
}
@@ -1582,6 +1368,9 @@ static int sgtl5000_i2c_remove(struct i2c_client *client)
snd_soc_unregister_codec(&client->dev);
clk_disable_unprepare(sgtl5000->mclk);
+ regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
+ regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+
return 0;
}
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 1c317de26176..22f3442af982 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -92,6 +92,7 @@
/*
* SGTL5000_CHIP_CLK_CTRL
*/
+#define SGTL5000_CHIP_CLK_CTRL_DEFAULT 0x0008
#define SGTL5000_RATE_MODE_MASK 0x0030
#define SGTL5000_RATE_MODE_SHIFT 4
#define SGTL5000_RATE_MODE_WIDTH 2
@@ -325,6 +326,7 @@
/*
* SGTL5000_CHIP_ANA_POWER
*/
+#define SGTL5000_ANA_POWER_DEFAULT 0x7060
#define SGTL5000_DAC_STEREO 0x4000
#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000
#define SGTL5000_STARTUP_POWERUP 0x1000
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index b8d19b77bde9..d8baca3f8413 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -28,6 +28,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <asm/unaligned.h>
#include "tas571x.h"
@@ -63,6 +64,10 @@ static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
case TAS571X_INPUT_MUX_REG:
case TAS571X_CH4_SRC_SELECT_REG:
case TAS571X_PWM_MUX_REG:
+ case TAS5717_CH1_RIGHT_CH_MIX_REG:
+ case TAS5717_CH1_LEFT_CH_MIX_REG:
+ case TAS5717_CH2_LEFT_CH_MIX_REG:
+ case TAS5717_CH2_RIGHT_CH_MIX_REG:
return 4;
default:
return 1;
@@ -135,6 +140,129 @@ static int tas571x_reg_read(void *context, unsigned int reg,
return 0;
}
+/*
+ * register write for 8- and 20-byte registers
+ */
+static int tas571x_reg_write_multiword(struct i2c_client *client,
+ unsigned int reg, const long values[], size_t len)
+{
+ size_t i;
+ uint8_t *buf, *p;
+ int ret;
+ size_t send_size = 1 + len * sizeof(uint32_t);
+
+ buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA);
+ if (!buf)
+ return -ENOMEM;
+ buf[0] = reg;
+
+ for (i = 0, p = buf + 1; i < len; i++, p += sizeof(uint32_t))
+ put_unaligned_be32(values[i], p);
+
+ ret = i2c_master_send(client, buf, send_size);
+
+ kfree(buf);
+
+ if (ret == send_size)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+/*
+ * register read for 8- and 20-byte registers
+ */
+static int tas571x_reg_read_multiword(struct i2c_client *client,
+ unsigned int reg, long values[], size_t len)
+{
+ unsigned int i;
+ uint8_t send_buf;
+ uint8_t *recv_buf, *p;
+ struct i2c_msg msgs[2];
+ unsigned int recv_size = len * sizeof(uint32_t);
+ int ret;
+
+ recv_buf = kzalloc(recv_size, GFP_KERNEL | GFP_DMA);
+ if (!recv_buf)
+ return -ENOMEM;
+
+ send_buf = reg;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = &send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = recv_size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ goto err_ret;
+ else if (ret != ARRAY_SIZE(msgs)) {
+ ret = -EIO;
+ goto err_ret;
+ }
+
+ for (i = 0, p = recv_buf; i < len; i++, p += sizeof(uint32_t))
+ values[i] = get_unaligned_be32(p);
+
+err_ret:
+ kfree(recv_buf);
+ return ret;
+}
+
+/*
+ * Integer array controls for setting biquad, mixer, DRC coefficients.
+ * According to the datasheet each coefficient is effectively 26bits,
+ * i.e. stored as 32bits, where bits [31:26] are ignored.
+ * TI's TAS57xx Graphical Development Environment tool however produces
+ * coefficients with more than 26 bits. For this reason we allow values
+ * in the full 32-bits reange.
+ * The coefficients are ordered as given in the TAS571x data sheet:
+ * b0, b1, b2, a1, a2
+ */
+
+static int tas571x_coefficient_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int numcoef = kcontrol->private_value >> 16;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = numcoef;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffffffff;
+ return 0;
+}
+
+static int tas571x_coefficient_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+
+ return tas571x_reg_read_multiword(i2c, index,
+ ucontrol->value.integer.value, numcoef);
+}
+
+static int tas571x_coefficient_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct i2c_client *i2c = to_i2c_client(codec->dev);
+ int numcoef = kcontrol->private_value >> 16;
+ int index = kcontrol->private_value & 0xffff;
+
+ return tas571x_reg_write_multiword(i2c, index,
+ ucontrol->value.integer.value, numcoef);
+}
+
static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
{
struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
@@ -241,6 +369,15 @@ static const struct snd_soc_dai_ops tas571x_dai_ops = {
.digital_mute = tas571x_mute,
};
+
+#define BIQUAD_COEFS(xname, reg) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = tas571x_coefficient_info, \
+ .get = tas571x_coefficient_get,\
+ .put = tas571x_coefficient_put, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .private_value = reg | (5 << 16) }
+
static const char *const tas5711_supply_names[] = {
"AVDD",
"DVDD",
@@ -264,6 +401,16 @@ static const struct snd_kcontrol_new tas5711_controls[] = {
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
+
+ SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
+ TAS5717_CH1_LEFT_CH_MIX_REG,
+ TAS5717_CH1_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
+ TAS5717_CH2_LEFT_CH_MIX_REG,
+ TAS5717_CH2_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
};
static const struct regmap_range tas571x_readonly_regs_range[] = {
@@ -340,6 +487,43 @@ static const struct snd_kcontrol_new tas5717_controls[] = {
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
+
+ /*
+ * The biquads are named according to the register names.
+ * Please note that TI's TAS57xx Graphical Development Environment
+ * tool names them different.
+ */
+ BIQUAD_COEFS("CH1 - Biquad 0", TAS5717_CH1_BQ0_REG),
+ BIQUAD_COEFS("CH1 - Biquad 1", TAS5717_CH1_BQ1_REG),
+ BIQUAD_COEFS("CH1 - Biquad 2", TAS5717_CH1_BQ2_REG),
+ BIQUAD_COEFS("CH1 - Biquad 3", TAS5717_CH1_BQ3_REG),
+ BIQUAD_COEFS("CH1 - Biquad 4", TAS5717_CH1_BQ4_REG),
+ BIQUAD_COEFS("CH1 - Biquad 5", TAS5717_CH1_BQ5_REG),
+ BIQUAD_COEFS("CH1 - Biquad 6", TAS5717_CH1_BQ6_REG),
+ BIQUAD_COEFS("CH1 - Biquad 7", TAS5717_CH1_BQ7_REG),
+ BIQUAD_COEFS("CH1 - Biquad 8", TAS5717_CH1_BQ8_REG),
+ BIQUAD_COEFS("CH1 - Biquad 9", TAS5717_CH1_BQ9_REG),
+ BIQUAD_COEFS("CH1 - Biquad 10", TAS5717_CH1_BQ10_REG),
+ BIQUAD_COEFS("CH1 - Biquad 11", TAS5717_CH1_BQ11_REG),
+
+ BIQUAD_COEFS("CH2 - Biquad 0", TAS5717_CH2_BQ0_REG),
+ BIQUAD_COEFS("CH2 - Biquad 1", TAS5717_CH2_BQ1_REG),
+ BIQUAD_COEFS("CH2 - Biquad 2", TAS5717_CH2_BQ2_REG),
+ BIQUAD_COEFS("CH2 - Biquad 3", TAS5717_CH2_BQ3_REG),
+ BIQUAD_COEFS("CH2 - Biquad 4", TAS5717_CH2_BQ4_REG),
+ BIQUAD_COEFS("CH2 - Biquad 5", TAS5717_CH2_BQ5_REG),
+ BIQUAD_COEFS("CH2 - Biquad 6", TAS5717_CH2_BQ6_REG),
+ BIQUAD_COEFS("CH2 - Biquad 7", TAS5717_CH2_BQ7_REG),
+ BIQUAD_COEFS("CH2 - Biquad 8", TAS5717_CH2_BQ8_REG),
+ BIQUAD_COEFS("CH2 - Biquad 9", TAS5717_CH2_BQ9_REG),
+ BIQUAD_COEFS("CH2 - Biquad 10", TAS5717_CH2_BQ10_REG),
+ BIQUAD_COEFS("CH2 - Biquad 11", TAS5717_CH2_BQ11_REG),
+
+ BIQUAD_COEFS("CH3 - Biquad 0", TAS5717_CH3_BQ0_REG),
+ BIQUAD_COEFS("CH3 - Biquad 1", TAS5717_CH3_BQ1_REG),
+
+ BIQUAD_COEFS("CH4 - Biquad 0", TAS5717_CH4_BQ0_REG),
+ BIQUAD_COEFS("CH4 - Biquad 1", TAS5717_CH4_BQ1_REG),
};
static const struct reg_default tas5717_reg_defaults[] = {
@@ -350,6 +534,10 @@ static const struct reg_default tas5717_reg_defaults[] = {
{ 0x08, 0x00c0 },
{ 0x09, 0x00c0 },
{ 0x1b, 0x82 },
+ { TAS5717_CH1_RIGHT_CH_MIX_REG, 0x0 },
+ { TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000},
+ { TAS5717_CH2_LEFT_CH_MIX_REG, 0x0 },
+ { TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000},
};
static const struct regmap_config tas5717_regmap_config = {
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
index cf800c364f0f..c45677bc26ad 100644
--- a/sound/soc/codecs/tas571x.h
+++ b/sound/soc/codecs/tas571x.h
@@ -52,4 +52,44 @@
#define TAS571X_CH4_SRC_SELECT_REG 0x21
#define TAS571X_PWM_MUX_REG 0x25
+/* 20-byte biquad registers */
+#define TAS5717_CH1_BQ0_REG 0x26
+#define TAS5717_CH1_BQ1_REG 0x27
+#define TAS5717_CH1_BQ2_REG 0x28
+#define TAS5717_CH1_BQ3_REG 0x29
+#define TAS5717_CH1_BQ4_REG 0x2a
+#define TAS5717_CH1_BQ5_REG 0x2b
+#define TAS5717_CH1_BQ6_REG 0x2c
+#define TAS5717_CH1_BQ7_REG 0x2d
+#define TAS5717_CH1_BQ8_REG 0x2e
+#define TAS5717_CH1_BQ9_REG 0x2f
+
+#define TAS5717_CH2_BQ0_REG 0x30
+#define TAS5717_CH2_BQ1_REG 0x31
+#define TAS5717_CH2_BQ2_REG 0x32
+#define TAS5717_CH2_BQ3_REG 0x33
+#define TAS5717_CH2_BQ4_REG 0x34
+#define TAS5717_CH2_BQ5_REG 0x35
+#define TAS5717_CH2_BQ6_REG 0x36
+#define TAS5717_CH2_BQ7_REG 0x37
+#define TAS5717_CH2_BQ8_REG 0x38
+#define TAS5717_CH2_BQ9_REG 0x39
+
+#define TAS5717_CH1_BQ10_REG 0x58
+#define TAS5717_CH1_BQ11_REG 0x59
+
+#define TAS5717_CH4_BQ0_REG 0x5a
+#define TAS5717_CH4_BQ1_REG 0x5b
+
+#define TAS5717_CH2_BQ10_REG 0x5c
+#define TAS5717_CH2_BQ11_REG 0x5d
+
+#define TAS5717_CH3_BQ0_REG 0x5e
+#define TAS5717_CH3_BQ1_REG 0x5f
+
+#define TAS5717_CH1_RIGHT_CH_MIX_REG 0x72
+#define TAS5717_CH1_LEFT_CH_MIX_REG 0x73
+#define TAS5717_CH2_LEFT_CH_MIX_REG 0x76
+#define TAS5717_CH2_RIGHT_CH_MIX_REG 0x77
+
#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index fe16c34607bb..ac9b146526eb 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -38,141 +38,143 @@ struct aic31xx_pdata {
int micbias_vg;
};
+#define AIC31XX_REG(page, reg) ((page * 128) + reg)
+
/* Page Control Register */
-#define AIC31XX_PAGECTL 0x00
+#define AIC31XX_PAGECTL AIC31XX_REG(0, 0)
/* Page 0 Registers */
/* Software reset register */
-#define AIC31XX_RESET 0x01
+#define AIC31XX_RESET AIC31XX_REG(0, 1)
/* OT FLAG register */
-#define AIC31XX_OT_FLAG 0x03
+#define AIC31XX_OT_FLAG AIC31XX_REG(0, 3)
/* Clock clock Gen muxing, Multiplexers*/
-#define AIC31XX_CLKMUX 0x04
+#define AIC31XX_CLKMUX AIC31XX_REG(0, 4)
/* PLL P and R-VAL register */
-#define AIC31XX_PLLPR 0x05
+#define AIC31XX_PLLPR AIC31XX_REG(0, 5)
/* PLL J-VAL register */
-#define AIC31XX_PLLJ 0x06
+#define AIC31XX_PLLJ AIC31XX_REG(0, 6)
/* PLL D-VAL MSB register */
-#define AIC31XX_PLLDMSB 0x07
+#define AIC31XX_PLLDMSB AIC31XX_REG(0, 7)
/* PLL D-VAL LSB register */
-#define AIC31XX_PLLDLSB 0x08
+#define AIC31XX_PLLDLSB AIC31XX_REG(0, 8)
/* DAC NDAC_VAL register*/
-#define AIC31XX_NDAC 0x0B
+#define AIC31XX_NDAC AIC31XX_REG(0, 11)
/* DAC MDAC_VAL register */
-#define AIC31XX_MDAC 0x0C
+#define AIC31XX_MDAC AIC31XX_REG(0, 12)
/* DAC OSR setting register 1, MSB value */
-#define AIC31XX_DOSRMSB 0x0D
+#define AIC31XX_DOSRMSB AIC31XX_REG(0, 13)
/* DAC OSR setting register 2, LSB value */
-#define AIC31XX_DOSRLSB 0x0E
-#define AIC31XX_MINI_DSP_INPOL 0x10
+#define AIC31XX_DOSRLSB AIC31XX_REG(0, 14)
+#define AIC31XX_MINI_DSP_INPOL AIC31XX_REG(0, 16)
/* Clock setting register 8, PLL */
-#define AIC31XX_NADC 0x12
+#define AIC31XX_NADC AIC31XX_REG(0, 18)
/* Clock setting register 9, PLL */
-#define AIC31XX_MADC 0x13
+#define AIC31XX_MADC AIC31XX_REG(0, 19)
/* ADC Oversampling (AOSR) Register */
-#define AIC31XX_AOSR 0x14
+#define AIC31XX_AOSR AIC31XX_REG(0, 20)
/* Clock setting register 9, Multiplexers */
-#define AIC31XX_CLKOUTMUX 0x19
+#define AIC31XX_CLKOUTMUX AIC31XX_REG(0, 25)
/* Clock setting register 10, CLOCKOUT M divider value */
-#define AIC31XX_CLKOUTMVAL 0x1A
+#define AIC31XX_CLKOUTMVAL AIC31XX_REG(0, 26)
/* Audio Interface Setting Register 1 */
-#define AIC31XX_IFACE1 0x1B
+#define AIC31XX_IFACE1 AIC31XX_REG(0, 27)
/* Audio Data Slot Offset Programming */
-#define AIC31XX_DATA_OFFSET 0x1C
+#define AIC31XX_DATA_OFFSET AIC31XX_REG(0, 28)
/* Audio Interface Setting Register 2 */
-#define AIC31XX_IFACE2 0x1D
+#define AIC31XX_IFACE2 AIC31XX_REG(0, 29)
/* Clock setting register 11, BCLK N Divider */
-#define AIC31XX_BCLKN 0x1E
+#define AIC31XX_BCLKN AIC31XX_REG(0, 30)
/* Audio Interface Setting Register 3, Secondary Audio Interface */
-#define AIC31XX_IFACESEC1 0x1F
+#define AIC31XX_IFACESEC1 AIC31XX_REG(0, 31)
/* Audio Interface Setting Register 4 */
-#define AIC31XX_IFACESEC2 0x20
+#define AIC31XX_IFACESEC2 AIC31XX_REG(0, 32)
/* Audio Interface Setting Register 5 */
-#define AIC31XX_IFACESEC3 0x21
+#define AIC31XX_IFACESEC3 AIC31XX_REG(0, 33)
/* I2C Bus Condition */
-#define AIC31XX_I2C 0x22
+#define AIC31XX_I2C AIC31XX_REG(0, 34)
/* ADC FLAG */
-#define AIC31XX_ADCFLAG 0x24
+#define AIC31XX_ADCFLAG AIC31XX_REG(0, 36)
/* DAC Flag Registers */
-#define AIC31XX_DACFLAG1 0x25
-#define AIC31XX_DACFLAG2 0x26
+#define AIC31XX_DACFLAG1 AIC31XX_REG(0, 37)
+#define AIC31XX_DACFLAG2 AIC31XX_REG(0, 38)
/* Sticky Interrupt flag (overflow) */
-#define AIC31XX_OFFLAG 0x27
+#define AIC31XX_OFFLAG AIC31XX_REG(0, 39)
/* Sticy DAC Interrupt flags */
-#define AIC31XX_INTRDACFLAG 0x2C
+#define AIC31XX_INTRDACFLAG AIC31XX_REG(0, 44)
/* Sticy ADC Interrupt flags */
-#define AIC31XX_INTRADCFLAG 0x2D
+#define AIC31XX_INTRADCFLAG AIC31XX_REG(0, 45)
/* DAC Interrupt flags 2 */
-#define AIC31XX_INTRDACFLAG2 0x2E
+#define AIC31XX_INTRDACFLAG2 AIC31XX_REG(0, 46)
/* ADC Interrupt flags 2 */
-#define AIC31XX_INTRADCFLAG2 0x2F
+#define AIC31XX_INTRADCFLAG2 AIC31XX_REG(0, 47)
/* INT1 interrupt control */
-#define AIC31XX_INT1CTRL 0x30
+#define AIC31XX_INT1CTRL AIC31XX_REG(0, 48)
/* INT2 interrupt control */
-#define AIC31XX_INT2CTRL 0x31
+#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49)
/* GPIO1 control */
-#define AIC31XX_GPIO1 0x33
+#define AIC31XX_GPIO1 AIC31XX_REG(0, 50)
-#define AIC31XX_DACPRB 0x3C
+#define AIC31XX_DACPRB AIC31XX_REG(0, 60)
/* ADC Instruction Set Register */
-#define AIC31XX_ADCPRB 0x3D
+#define AIC31XX_ADCPRB AIC31XX_REG(0, 61)
/* DAC channel setup register */
-#define AIC31XX_DACSETUP 0x3F
+#define AIC31XX_DACSETUP AIC31XX_REG(0, 63)
/* DAC Mute and volume control register */
-#define AIC31XX_DACMUTE 0x40
+#define AIC31XX_DACMUTE AIC31XX_REG(0, 64)
/* Left DAC channel digital volume control */
-#define AIC31XX_LDACVOL 0x41
+#define AIC31XX_LDACVOL AIC31XX_REG(0, 65)
/* Right DAC channel digital volume control */
-#define AIC31XX_RDACVOL 0x42
+#define AIC31XX_RDACVOL AIC31XX_REG(0, 66)
/* Headset detection */
-#define AIC31XX_HSDETECT 0x43
+#define AIC31XX_HSDETECT AIC31XX_REG(0, 67)
/* ADC Digital Mic */
-#define AIC31XX_ADCSETUP 0x51
+#define AIC31XX_ADCSETUP AIC31XX_REG(0, 81)
/* ADC Digital Volume Control Fine Adjust */
-#define AIC31XX_ADCFGA 0x52
+#define AIC31XX_ADCFGA AIC31XX_REG(0, 82)
/* ADC Digital Volume Control Coarse Adjust */
-#define AIC31XX_ADCVOL 0x53
+#define AIC31XX_ADCVOL AIC31XX_REG(0, 83)
/* Page 1 Registers */
/* Headphone drivers */
-#define AIC31XX_HPDRIVER 0x9F
+#define AIC31XX_HPDRIVER AIC31XX_REG(1, 31)
/* Class-D Speakear Amplifier */
-#define AIC31XX_SPKAMP 0xA0
+#define AIC31XX_SPKAMP AIC31XX_REG(1, 32)
/* HP Output Drivers POP Removal Settings */
-#define AIC31XX_HPPOP 0xA1
+#define AIC31XX_HPPOP AIC31XX_REG(1, 33)
/* Output Driver PGA Ramp-Down Period Control */
-#define AIC31XX_SPPGARAMP 0xA2
+#define AIC31XX_SPPGARAMP AIC31XX_REG(1, 34)
/* DAC_L and DAC_R Output Mixer Routing */
-#define AIC31XX_DACMIXERROUTE 0xA3
+#define AIC31XX_DACMIXERROUTE AIC31XX_REG(1, 35)
/* Left Analog Vol to HPL */
-#define AIC31XX_LANALOGHPL 0xA4
+#define AIC31XX_LANALOGHPL AIC31XX_REG(1, 36)
/* Right Analog Vol to HPR */
-#define AIC31XX_RANALOGHPR 0xA5
+#define AIC31XX_RANALOGHPR AIC31XX_REG(1, 37)
/* Left Analog Vol to SPL */
-#define AIC31XX_LANALOGSPL 0xA6
+#define AIC31XX_LANALOGSPL AIC31XX_REG(1, 38)
/* Right Analog Vol to SPR */
-#define AIC31XX_RANALOGSPR 0xA7
+#define AIC31XX_RANALOGSPR AIC31XX_REG(1, 39)
/* HPL Driver */
-#define AIC31XX_HPLGAIN 0xA8
+#define AIC31XX_HPLGAIN AIC31XX_REG(1, 40)
/* HPR Driver */
-#define AIC31XX_HPRGAIN 0xA9
+#define AIC31XX_HPRGAIN AIC31XX_REG(1, 41)
/* SPL Driver */
-#define AIC31XX_SPLGAIN 0xAA
+#define AIC31XX_SPLGAIN AIC31XX_REG(1, 42)
/* SPR Driver */
-#define AIC31XX_SPRGAIN 0xAB
+#define AIC31XX_SPRGAIN AIC31XX_REG(1, 43)
/* HP Driver Control */
-#define AIC31XX_HPCONTROL 0xAC
+#define AIC31XX_HPCONTROL AIC31XX_REG(1, 44)
/* MIC Bias Control */
-#define AIC31XX_MICBIAS 0xAE
+#define AIC31XX_MICBIAS AIC31XX_REG(1, 46)
/* MIC PGA*/
-#define AIC31XX_MICPGA 0xAF
+#define AIC31XX_MICPGA AIC31XX_REG(1, 47)
/* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */
-#define AIC31XX_MICPGAPI 0xB0
+#define AIC31XX_MICPGAPI AIC31XX_REG(1, 48)
/* ADC Input Selection for M-Terminal */
-#define AIC31XX_MICPGAMI 0xB1
+#define AIC31XX_MICPGAMI AIC31XX_REG(1, 49)
/* Input CM Settings */
-#define AIC31XX_MICPGACM 0xB2
+#define AIC31XX_MICPGACM AIC31XX_REG(1, 50)
/* Bits, masks and shifts */
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 11d85c5c787a..f1ea052a822e 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -32,6 +32,7 @@
#include <sound/tlv.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/regmap.h>
#include "tpa6130a2.h"
@@ -40,219 +41,72 @@ enum tpa_model {
TPA6140A2,
};
-static struct i2c_client *tpa6130a2_client;
-
/* This struct is used to save the context */
struct tpa6130a2_data {
- struct mutex mutex;
- unsigned char regs[TPA6130A2_CACHEREGNUM];
+ struct device *dev;
+ struct regmap *regmap;
struct regulator *supply;
int power_gpio;
- u8 power_state:1;
enum tpa_model id;
};
-static int tpa6130a2_i2c_read(int reg)
-{
- struct tpa6130a2_data *data;
- int val;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- /* If powered off, return the cached value */
- if (data->power_state) {
- val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
- if (val < 0)
- dev_err(&tpa6130a2_client->dev, "Read failed\n");
- else
- data->regs[reg] = val;
- } else {
- val = data->regs[reg];
- }
-
- return val;
-}
-
-static int tpa6130a2_i2c_write(int reg, u8 value)
-{
- struct tpa6130a2_data *data;
- int val = 0;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- if (data->power_state) {
- val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
- if (val < 0) {
- dev_err(&tpa6130a2_client->dev, "Write failed\n");
- return val;
- }
- }
-
- /* Either powered on or off, we save the context */
- data->regs[reg] = value;
-
- return val;
-}
-
-static u8 tpa6130a2_read(int reg)
-{
- struct tpa6130a2_data *data;
-
- if (WARN_ON(!tpa6130a2_client))
- return 0;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- return data->regs[reg];
-}
-
-static int tpa6130a2_initialize(void)
-{
- struct tpa6130a2_data *data;
- int i, ret = 0;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- for (i = 1; i < TPA6130A2_REG_VERSION; i++) {
- ret = tpa6130a2_i2c_write(i, data->regs[i]);
- if (ret < 0)
- break;
- }
-
- return ret;
-}
-
-static int tpa6130a2_power(u8 power)
+static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
{
- struct tpa6130a2_data *data;
- u8 val;
- int ret = 0;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- mutex_lock(&data->mutex);
- if (power == data->power_state)
- goto exit;
+ int ret;
- if (power) {
+ if (enable) {
ret = regulator_enable(data->supply);
if (ret != 0) {
- dev_err(&tpa6130a2_client->dev,
+ dev_err(data->dev,
"Failed to enable supply: %d\n", ret);
- goto exit;
+ return ret;
}
/* Power on */
if (data->power_gpio >= 0)
gpio_set_value(data->power_gpio, 1);
-
- data->power_state = 1;
- ret = tpa6130a2_initialize();
- if (ret < 0) {
- dev_err(&tpa6130a2_client->dev,
- "Failed to initialize chip\n");
- if (data->power_gpio >= 0)
- gpio_set_value(data->power_gpio, 0);
- regulator_disable(data->supply);
- data->power_state = 0;
- goto exit;
- }
} else {
- /* set SWS */
- val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
- val |= TPA6130A2_SWS;
- tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
-
/* Power off */
if (data->power_gpio >= 0)
gpio_set_value(data->power_gpio, 0);
ret = regulator_disable(data->supply);
if (ret != 0) {
- dev_err(&tpa6130a2_client->dev,
+ dev_err(data->dev,
"Failed to disable supply: %d\n", ret);
- goto exit;
+ return ret;
}
- data->power_state = 0;
+ /* device regs does not match the cache state anymore */
+ regcache_mark_dirty(data->regmap);
}
-exit:
- mutex_unlock(&data->mutex);
return ret;
}
-static int tpa6130a2_get_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kctrl, int event)
{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct tpa6130a2_data *data;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- mutex_lock(&data->mutex);
-
- ucontrol->value.integer.value[0] =
- (tpa6130a2_read(reg) >> shift) & mask;
-
- if (invert)
- ucontrol->value.integer.value[0] =
- max - ucontrol->value.integer.value[0];
-
- mutex_unlock(&data->mutex);
- return 0;
-}
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c);
+ int ret;
-static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct tpa6130a2_data *data;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- unsigned int val = (ucontrol->value.integer.value[0] & mask);
- unsigned int val_reg;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- if (invert)
- val = max - val;
-
- mutex_lock(&data->mutex);
-
- val_reg = tpa6130a2_read(reg);
- if (((val_reg >> shift) & mask) == val) {
- mutex_unlock(&data->mutex);
- return 0;
+ /* before widget power up */
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* Turn on the chip */
+ tpa6130a2_power(data, true);
+ /* Sync the registers */
+ ret = regcache_sync(data->regmap);
+ if (ret < 0) {
+ dev_err(c->dev, "Failed to initialize chip\n");
+ tpa6130a2_power(data, false);
+ return ret;
+ }
+ /* after widget power down */
+ } else {
+ tpa6130a2_power(data, false);
}
- val_reg &= ~(mask << shift);
- val_reg |= val << shift;
- tpa6130a2_i2c_write(reg, val_reg);
-
- mutex_unlock(&data->mutex);
-
- return 1;
+ return 0;
}
/*
@@ -273,9 +127,8 @@ static const DECLARE_TLV_DB_RANGE(tpa6130_tlv,
);
static const struct snd_kcontrol_new tpa6130a2_controls[] = {
- SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+ SOC_SINGLE_TLV("Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
- tpa6130a2_get_volsw, tpa6130a2_put_volsw,
tpa6130_tlv),
};
@@ -286,85 +139,79 @@ static const DECLARE_TLV_DB_RANGE(tpa6140_tlv,
);
static const struct snd_kcontrol_new tpa6140a2_controls[] = {
- SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume",
+ SOC_SINGLE_TLV("Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0,
- tpa6130a2_get_volsw, tpa6130a2_put_volsw,
tpa6140_tlv),
};
-/*
- * Enable or disable channel (left or right)
- * The bit number for mute and amplifier are the same per channel:
- * bit 6: Right channel
- * bit 7: Left channel
- * in both registers.
- */
-static void tpa6130a2_channel_enable(u8 channel, int enable)
+static int tpa6130a2_component_probe(struct snd_soc_component *component)
{
- u8 val;
+ struct tpa6130a2_data *data = snd_soc_component_get_drvdata(component);
- if (enable) {
- /* Enable channel */
- /* Enable amplifier */
- val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
- val |= channel;
- val &= ~TPA6130A2_SWS;
- tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
-
- /* Unmute channel */
- val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
- val &= ~channel;
- tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
- } else {
- /* Disable channel */
- /* Mute channel */
- val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
- val |= channel;
- tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
-
- /* Disable amplifier */
- val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
- val &= ~channel;
- tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
- }
+ if (data->id == TPA6140A2)
+ return snd_soc_add_component_controls(component,
+ tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls));
+ else
+ return snd_soc_add_component_controls(component,
+ tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls));
}
-int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable)
-{
- int ret = 0;
- if (enable) {
- ret = tpa6130a2_power(1);
- if (ret < 0)
- return ret;
- tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L,
- 1);
- } else {
- tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L,
- 0);
- ret = tpa6130a2_power(0);
- }
+static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LEFTIN"),
+ SND_SOC_DAPM_INPUT("RIGHTIN"),
+ SND_SOC_DAPM_OUTPUT("HPLEFT"),
+ SND_SOC_DAPM_OUTPUT("HPRIGHT"),
+
+ SND_SOC_DAPM_PGA("Left Mute", TPA6130A2_REG_VOL_MUTE,
+ TPA6130A2_HP_EN_L_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Mute", TPA6130A2_REG_VOL_MUTE,
+ TPA6130A2_HP_EN_R_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left PGA", TPA6130A2_REG_CONTROL,
+ TPA6130A2_HP_EN_L_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right PGA", TPA6130A2_REG_CONTROL,
+ TPA6130A2_HP_EN_R_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("Power", TPA6130A2_REG_CONTROL,
+ TPA6130A2_SWS_SHIFT, 1, tpa6130a2_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
- return ret;
-}
-EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable);
+static const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = {
+ { "Left PGA", NULL, "LEFTIN" },
+ { "Right PGA", NULL, "RIGHTIN" },
-int tpa6130a2_add_controls(struct snd_soc_codec *codec)
-{
- struct tpa6130a2_data *data;
+ { "Left Mute", NULL, "Left PGA" },
+ { "Right Mute", NULL, "Right PGA" },
- if (tpa6130a2_client == NULL)
- return -ENODEV;
+ { "HPLEFT", NULL, "Left Mute" },
+ { "HPRIGHT", NULL, "Right Mute" },
- data = i2c_get_clientdata(tpa6130a2_client);
+ { "Left PGA", NULL, "Power" },
+ { "Right PGA", NULL, "Power" },
+};
- if (data->id == TPA6140A2)
- return snd_soc_add_codec_controls(codec, tpa6140a2_controls,
- ARRAY_SIZE(tpa6140a2_controls));
- else
- return snd_soc_add_codec_controls(codec, tpa6130a2_controls,
- ARRAY_SIZE(tpa6130a2_controls));
-}
-EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
+struct snd_soc_component_driver tpa6130a2_component_driver = {
+ .name = "tpa6130a2",
+ .probe = tpa6130a2_component_probe,
+ .dapm_widgets = tpa6130a2_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tpa6130a2_dapm_widgets),
+ .dapm_routes = tpa6130a2_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tpa6130a2_dapm_routes),
+};
+
+static const struct reg_default tpa6130a2_reg_defaults[] = {
+ { TPA6130A2_REG_CONTROL, TPA6130A2_SWS },
+ { TPA6130A2_REG_VOL_MUTE, TPA6130A2_MUTE_R | TPA6130A2_MUTE_L },
+};
+
+static const struct regmap_config tpa6130a2_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TPA6130A2_REG_VERSION,
+ .reg_defaults = tpa6130a2_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tpa6130a2_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
static int tpa6130a2_probe(struct i2c_client *client,
const struct i2c_device_id *id)
@@ -374,6 +221,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
struct device_node *np = client->dev.of_node;
const char *regulator;
+ unsigned int version;
int ret;
dev = &client->dev;
@@ -382,6 +230,12 @@ static int tpa6130a2_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
+ data->dev = dev;
+
+ data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
if (pdata) {
data->power_gpio = pdata->power_gpio;
} else if (np) {
@@ -392,26 +246,17 @@ static int tpa6130a2_probe(struct i2c_client *client,
return -ENODEV;
}
- tpa6130a2_client = client;
-
- i2c_set_clientdata(tpa6130a2_client, data);
+ i2c_set_clientdata(client, data);
data->id = id->driver_data;
- mutex_init(&data->mutex);
-
- /* Set default register values */
- data->regs[TPA6130A2_REG_CONTROL] = TPA6130A2_SWS;
- data->regs[TPA6130A2_REG_VOL_MUTE] = TPA6130A2_MUTE_R |
- TPA6130A2_MUTE_L;
-
if (data->power_gpio >= 0) {
ret = devm_gpio_request(dev, data->power_gpio,
"tpa6130a2 enable");
if (ret < 0) {
dev_err(dev, "Failed to request power GPIO (%d)\n",
data->power_gpio);
- goto err_gpio;
+ return ret;
}
gpio_direction_output(data->power_gpio, 0);
}
@@ -432,39 +277,27 @@ static int tpa6130a2_probe(struct i2c_client *client,
if (IS_ERR(data->supply)) {
ret = PTR_ERR(data->supply);
dev_err(dev, "Failed to request supply: %d\n", ret);
- goto err_gpio;
+ return ret;
}
- ret = tpa6130a2_power(1);
+ ret = tpa6130a2_power(data, true);
if (ret != 0)
- goto err_gpio;
+ return ret;
/* Read version */
- ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
- TPA6130A2_VERSION_MASK;
- if ((ret != 1) && (ret != 2))
- dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
+ regmap_read(data->regmap, TPA6130A2_REG_VERSION, &version);
+ version &= TPA6130A2_VERSION_MASK;
+ if ((version != 1) && (version != 2))
+ dev_warn(dev, "UNTESTED version detected (%d)\n", version);
/* Disable the chip */
- ret = tpa6130a2_power(0);
+ ret = tpa6130a2_power(data, false);
if (ret != 0)
- goto err_gpio;
-
- return 0;
-
-err_gpio:
- tpa6130a2_client = NULL;
+ return ret;
- return ret;
-}
-
-static int tpa6130a2_remove(struct i2c_client *client)
-{
- tpa6130a2_power(0);
- tpa6130a2_client = NULL;
-
- return 0;
+ return devm_snd_soc_register_component(&client->dev,
+ &tpa6130a2_component_driver, NULL, 0);
}
static const struct i2c_device_id tpa6130a2_id[] = {
@@ -489,7 +322,6 @@ static struct i2c_driver tpa6130a2_i2c_driver = {
.of_match_table = of_match_ptr(tpa6130a2_of_match),
},
.probe = tpa6130a2_probe,
- .remove = tpa6130a2_remove,
.id_table = tpa6130a2_id,
};
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
index 417444020ba6..f19cad5d4172 100644
--- a/sound/soc/codecs/tpa6130a2.h
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -30,19 +30,20 @@
#define TPA6130A2_REG_OUT_IMPEDANCE 0x03
#define TPA6130A2_REG_VERSION 0x04
-#define TPA6130A2_CACHEREGNUM (TPA6130A2_REG_VERSION + 1)
-
/* Register bits */
/* TPA6130A2_REG_CONTROL (0x01) */
-#define TPA6130A2_SWS (0x01 << 0)
+#define TPA6130A2_SWS_SHIFT 0
+#define TPA6130A2_SWS (0x01 << TPA6130A2_SWS_SHIFT)
#define TPA6130A2_TERMAL (0x01 << 1)
#define TPA6130A2_MODE(x) (x << 4)
#define TPA6130A2_MODE_STEREO (0x00)
#define TPA6130A2_MODE_DUAL_MONO (0x01)
#define TPA6130A2_MODE_BRIDGE (0x02)
#define TPA6130A2_MODE_MASK (0x03)
-#define TPA6130A2_HP_EN_R (0x01 << 6)
-#define TPA6130A2_HP_EN_L (0x01 << 7)
+#define TPA6130A2_HP_EN_R_SHIFT 6
+#define TPA6130A2_HP_EN_R (0x01 << TPA6130A2_HP_EN_R_SHIFT)
+#define TPA6130A2_HP_EN_L_SHIFT 7
+#define TPA6130A2_HP_EN_L (0x01 << TPA6130A2_HP_EN_L_SHIFT)
/* TPA6130A2_REG_VOL_MUTE (0x02) */
#define TPA6130A2_VOLUME(x) ((x & 0x3f) << 0)
@@ -56,7 +57,4 @@
/* TPA6130A2_REG_VERSION (0x04) */
#define TPA6130A2_VERSION_MASK (0x0f)
-extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
-extern int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable);
-
#endif /* __TPA6130A2_H__ */
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index e7fe6b7b95b7..846deed6af41 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1713,6 +1713,7 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
};
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index d54f1b46c9ec..156547026a40 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1104,6 +1104,11 @@ SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+ &arizona_voice_trigger_switch[2]),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -1998,10 +2003,16 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
+ { "DRC2 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
{ "DRC2 Signal Activity", NULL, "DRC2L" },
{ "DRC2 Signal Activity", NULL, "DRC2R" },
+
+ { "DSP Voice Trigger", NULL, "SYSCLK" },
+ { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+ { "DSP3 Voice Trigger", "Switch", "DSP3" },
};
static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -2223,6 +2234,7 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
{
struct wm5110_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
@@ -2230,6 +2242,12 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
if (ret != -ENODEV)
serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ info.core = i;
+ arizona_call_notifiers(arizona,
+ ARIZONA_NOTIFY_VOICE_TRIGGER,
+ &info);
+ }
}
if (!serviced) {
@@ -2252,6 +2270,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", wm5110_adsp2_irq,
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 4bcf5f8ece50..d18261a44256 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -358,6 +358,9 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
case 24:
iface |= 0x0008;
break;
+ case 32:
+ iface |= 0x000c;
+ break;
}
wm8731_set_deemph(codec);
@@ -541,7 +544,7 @@ static int wm8731_startup(struct snd_pcm_substream *substream,
#define WM8731_RATES SNDRV_PCM_RATE_8000_96000
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
- SNDRV_PCM_FMTBIT_S24_LE)
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static const struct snd_soc_dai_ops wm8731_dai_ops = {
.startup = wm8731_startup,
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 6f1024f48b19..cdcc91282e8a 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -32,7 +32,6 @@
*/
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -486,7 +485,7 @@ SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0),
SND_SOC_DAPM_OUTPUT("MONO1"),
SND_SOC_DAPM_MUX("Mono 2 Mux", SND_SOC_NOPM, 0, 0, &wm8753_mono2_controls),
SND_SOC_DAPM_OUTPUT("MONO2"),
-SND_SOC_DAPM_MIXER("Out3 Left + Right", -1, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Out3 Left + Right", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls),
SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("OUT3"),
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 6ac76fe116b0..7347abff4b2c 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1,10 +1,13 @@
/*
- * wm8985.c -- WM8985 ALSA SoC Audio driver
+ * wm8985.c -- WM8985 / WM8758 ALSA SoC Audio driver
*
* Copyright 2010 Wolfson Microelectronics plc
- *
* Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
*
+ * WM8758 support:
+ * Copyright: 2016 Barix AG
+ * Author: Petr Kulhavy <petr@barix.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.
@@ -40,6 +43,11 @@ static const char *wm8985_supply_names[WM8985_NUM_SUPPLIES] = {
"AVDD2"
};
+enum wm8985_type {
+ WM8985,
+ WM8758,
+};
+
static const struct reg_default wm8985_reg_defaults[] = {
{ 1, 0x0000 }, /* R1 - Power management 1 */
{ 2, 0x0000 }, /* R2 - Power management 2 */
@@ -181,6 +189,7 @@ static const int volume_update_regs[] = {
struct wm8985_priv {
struct regmap *regmap;
struct regulator_bulk_data supplies[WM8985_NUM_SUPPLIES];
+ enum wm8985_type dev_type;
unsigned int sysclk;
unsigned int bclk;
};
@@ -289,7 +298,7 @@ static const char *depth_3d_text[] = {
};
static SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, depth_3d_text);
-static const struct snd_kcontrol_new wm8985_snd_controls[] = {
+static const struct snd_kcontrol_new wm8985_common_snd_controls[] = {
SOC_SINGLE("Digital Loopback Switch", WM8985_COMPANDING_CONTROL,
0, 1, 0),
@@ -355,10 +364,6 @@ static const struct snd_kcontrol_new wm8985_snd_controls[] = {
SOC_ENUM("High Pass Filter Mode", filter_mode),
SOC_SINGLE("High Pass Filter Cutoff", WM8985_ADC_CONTROL, 4, 7, 0),
- SOC_DOUBLE_R_TLV("Aux Bypass Volume",
- WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 6, 7, 0,
- aux_tlv),
-
SOC_DOUBLE_R_TLV("Input PGA Bypass Volume",
WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 2, 7, 0,
bypass_tlv),
@@ -379,20 +384,30 @@ static const struct snd_kcontrol_new wm8985_snd_controls[] = {
SOC_SINGLE_TLV("EQ5 Volume", WM8985_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv),
SOC_ENUM("3D Depth", depth_3d),
+};
+
+static const struct snd_kcontrol_new wm8985_specific_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Aux Bypass Volume",
+ WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 6, 7, 0,
+ aux_tlv),
SOC_ENUM("Speaker Mode", speaker_mode)
};
static const struct snd_kcontrol_new left_out_mixer[] = {
SOC_DAPM_SINGLE("Line Switch", WM8985_LEFT_MIXER_CTRL, 1, 1, 0),
- SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0),
SOC_DAPM_SINGLE("PCM Switch", WM8985_LEFT_MIXER_CTRL, 0, 1, 0),
+
+ /* --- WM8985 only --- */
+ SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0),
};
static const struct snd_kcontrol_new right_out_mixer[] = {
SOC_DAPM_SINGLE("Line Switch", WM8985_RIGHT_MIXER_CTRL, 1, 1, 0),
- SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0),
SOC_DAPM_SINGLE("PCM Switch", WM8985_RIGHT_MIXER_CTRL, 0, 1, 0),
+
+ /* --- WM8985 only --- */
+ SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0),
};
static const struct snd_kcontrol_new left_input_mixer[] = {
@@ -410,6 +425,8 @@ static const struct snd_kcontrol_new right_input_mixer[] = {
static const struct snd_kcontrol_new left_boost_mixer[] = {
SOC_DAPM_SINGLE_TLV("L2 Volume", WM8985_LEFT_ADC_BOOST_CTRL,
4, 7, 0, boost_tlv),
+
+ /* --- WM8985 only --- */
SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8985_LEFT_ADC_BOOST_CTRL,
0, 7, 0, boost_tlv)
};
@@ -417,11 +434,13 @@ static const struct snd_kcontrol_new left_boost_mixer[] = {
static const struct snd_kcontrol_new right_boost_mixer[] = {
SOC_DAPM_SINGLE_TLV("R2 Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
4, 7, 0, boost_tlv),
+
+ /* --- WM8985 only --- */
SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
0, 7, 0, boost_tlv)
};
-static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget wm8985_common_dapm_widgets[] = {
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8985_POWER_MANAGEMENT_3,
0, 0),
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8985_POWER_MANAGEMENT_3,
@@ -431,21 +450,11 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8985_POWER_MANAGEMENT_2,
1, 0),
- SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
- 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
- SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
- 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
-
SND_SOC_DAPM_MIXER("Left Input Mixer", WM8985_POWER_MANAGEMENT_2,
2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)),
SND_SOC_DAPM_MIXER("Right Input Mixer", WM8985_POWER_MANAGEMENT_2,
3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)),
- SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
- 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
- SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
- 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
-
SND_SOC_DAPM_PGA("Left Capture PGA", WM8985_LEFT_INP_PGA_GAIN_CTRL,
6, 1, NULL, 0),
SND_SOC_DAPM_PGA("Right Capture PGA", WM8985_RIGHT_INP_PGA_GAIN_CTRL,
@@ -468,8 +477,6 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LIP"),
SND_SOC_DAPM_INPUT("RIN"),
SND_SOC_DAPM_INPUT("RIP"),
- SND_SOC_DAPM_INPUT("AUXL"),
- SND_SOC_DAPM_INPUT("AUXR"),
SND_SOC_DAPM_INPUT("L2"),
SND_SOC_DAPM_INPUT("R2"),
SND_SOC_DAPM_OUTPUT("HPL"),
@@ -478,13 +485,42 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("SPKR")
};
-static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
+static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
+
+ SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
+ SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
+
+ SND_SOC_DAPM_INPUT("AUXL"),
+ SND_SOC_DAPM_INPUT("AUXR"),
+};
+
+static const struct snd_soc_dapm_widget wm8758_dapm_widgets[] = {
+ SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 2, 0, left_out_mixer,
+ ARRAY_SIZE(left_out_mixer) - 1),
+ SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
+ 3, 0, right_out_mixer,
+ ARRAY_SIZE(right_out_mixer) - 1),
+
+ SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 4, 0, left_boost_mixer,
+ ARRAY_SIZE(left_boost_mixer) - 1),
+ SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+ 5, 0, right_boost_mixer,
+ ARRAY_SIZE(right_boost_mixer) - 1),
+};
+
+static const struct snd_soc_dapm_route wm8985_common_dapm_routes[] = {
{ "Right Output Mixer", "PCM Switch", "Right DAC" },
- { "Right Output Mixer", "Aux Switch", "AUXR" },
{ "Right Output Mixer", "Line Switch", "Right Boost Mixer" },
{ "Left Output Mixer", "PCM Switch", "Left DAC" },
- { "Left Output Mixer", "Aux Switch", "AUXL" },
{ "Left Output Mixer", "Line Switch", "Left Boost Mixer" },
{ "Right Headphone Out", NULL, "Right Output Mixer" },
@@ -501,13 +537,11 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
{ "Right ADC", NULL, "Right Boost Mixer" },
- { "Right Boost Mixer", "AUXR Volume", "AUXR" },
{ "Right Boost Mixer", NULL, "Right Capture PGA" },
{ "Right Boost Mixer", "R2 Volume", "R2" },
{ "Left ADC", NULL, "Left Boost Mixer" },
- { "Left Boost Mixer", "AUXL Volume", "AUXL" },
{ "Left Boost Mixer", NULL, "Left Capture PGA" },
{ "Left Boost Mixer", "L2 Volume", "L2" },
@@ -522,6 +556,38 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
{ "Left Input Mixer", "MicN Switch", "LIN" },
{ "Left Input Mixer", "MicP Switch", "LIP" },
};
+static const struct snd_soc_dapm_route wm8985_aux_dapm_routes[] = {
+ { "Right Output Mixer", "Aux Switch", "AUXR" },
+ { "Left Output Mixer", "Aux Switch", "AUXL" },
+
+ { "Right Boost Mixer", "AUXR Volume", "AUXR" },
+ { "Left Boost Mixer", "AUXL Volume", "AUXL" },
+};
+
+static int wm8985_add_widgets(struct snd_soc_codec *codec)
+{
+ struct wm8985_priv *wm8985 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ switch (wm8985->dev_type) {
+ case WM8758:
+ snd_soc_dapm_new_controls(dapm, wm8758_dapm_widgets,
+ ARRAY_SIZE(wm8758_dapm_widgets));
+ break;
+
+ case WM8985:
+ snd_soc_add_codec_controls(codec, wm8985_specific_snd_controls,
+ ARRAY_SIZE(wm8985_specific_snd_controls));
+
+ snd_soc_dapm_new_controls(dapm, wm8985_dapm_widgets,
+ ARRAY_SIZE(wm8985_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, wm8985_aux_dapm_routes,
+ ARRAY_SIZE(wm8985_aux_dapm_routes));
+ break;
+ }
+
+ return 0;
+}
static int eqmode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -999,6 +1065,8 @@ static int wm8985_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT,
WM8985_BIASCUT);
+ wm8985_add_widgets(codec);
+
return 0;
err_reg_enable:
@@ -1042,12 +1110,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8985 = {
.set_bias_level = wm8985_set_bias_level,
.suspend_bias_off = true,
- .controls = wm8985_snd_controls,
- .num_controls = ARRAY_SIZE(wm8985_snd_controls),
- .dapm_widgets = wm8985_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm8985_dapm_widgets),
- .dapm_routes = wm8985_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(wm8985_dapm_routes),
+ .controls = wm8985_common_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8985_common_snd_controls),
+ .dapm_widgets = wm8985_common_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8985_common_dapm_widgets),
+ .dapm_routes = wm8985_common_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8985_common_dapm_routes),
};
static const struct regmap_config wm8985_regmap = {
@@ -1074,6 +1142,8 @@ static int wm8985_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, wm8985);
+ wm8985->dev_type = WM8985;
+
wm8985->regmap = devm_regmap_init_spi(spi, &wm8985_regmap);
if (IS_ERR(wm8985->regmap)) {
ret = PTR_ERR(wm8985->regmap);
@@ -1115,6 +1185,8 @@ static int wm8985_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8985);
+ wm8985->dev_type = id->driver_data;
+
wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap);
if (IS_ERR(wm8985->regmap)) {
ret = PTR_ERR(wm8985->regmap);
@@ -1135,7 +1207,8 @@ static int wm8985_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8985_i2c_id[] = {
- { "wm8985", 0 },
+ { "wm8985", WM8985 },
+ { "wm8758", WM8758 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id);
@@ -1183,6 +1256,6 @@ static void __exit wm8985_exit(void)
}
module_exit(wm8985_exit);
-MODULE_DESCRIPTION("ASoC WM8985 driver");
+MODULE_DESCRIPTION("ASoC WM8985 / WM8758 driver");
MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8985.h b/sound/soc/codecs/wm8985.h
index 2e71ff507638..41b1048e3c97 100644
--- a/sound/soc/codecs/wm8985.h
+++ b/sound/soc/codecs/wm8985.h
@@ -290,6 +290,9 @@
#define WM8985_GPIO1GPD_MASK 0x0040 /* GPIO1GPD */
#define WM8985_GPIO1GPD_SHIFT 6 /* GPIO1GPD */
#define WM8985_GPIO1GPD_WIDTH 1 /* GPIO1GPD */
+#define WM8758_OPCLKDIV_MASK 0x0030 /* OPCLKDIV - [1:0] */
+#define WM8758_OPCLKDIV_SHIFT 4 /* OPCLKDIV - [1:0] */
+#define WM8758_OPCLKDIV_WIDTH 2 /* OPCLKDIV - [1:0] */
#define WM8985_GPIO1POL 0x0008 /* GPIO1POL */
#define WM8985_GPIO1POL_MASK 0x0008 /* GPIO1POL */
#define WM8985_GPIO1POL_SHIFT 3 /* GPIO1POL */
@@ -301,6 +304,12 @@
/*
* R9 (0x09) - Jack Detect Control 1
*/
+#define WM8758_JD_VMID1_MASK 0x0100 /* JD_VMID1 */
+#define WM8758_JD_VMID1_SHIFT 8 /* JD_VMID1 */
+#define WM8758_JD_VMID1_WIDTH 1 /* JD_VMID1 */
+#define WM8758_JD_VMID0_MASK 0x0080 /* JD_VMID0 */
+#define WM8758_JD_VMID0_SHIFT 7 /* JD_VMID0 */
+#define WM8758_JD_VMID0_WIDTH 1 /* JD_VMID0 */
#define WM8985_JD_EN 0x0040 /* JD_EN */
#define WM8985_JD_EN_MASK 0x0040 /* JD_EN */
#define WM8985_JD_EN_SHIFT 6 /* JD_EN */
@@ -649,6 +658,12 @@
#define WM8985_OUT4_2LNR_MASK 0x0020 /* OUT4_2LNR */
#define WM8985_OUT4_2LNR_SHIFT 5 /* OUT4_2LNR */
#define WM8985_OUT4_2LNR_WIDTH 1 /* OUT4_2LNR */
+#define WM8758_VMIDTOG_MASK 0x0010 /* VMIDTOG */
+#define WM8758_VMIDTOG_SHIFT 4 /* VMIDTOG */
+#define WM8758_VMIDTOG_WIDTH 1 /* VMIDTOG */
+#define WM8758_OUT2DEL_MASK 0x0008 /* OUT2DEL */
+#define WM8758_OUT2DEL_SHIFT 3 /* OUT2DEL */
+#define WM8758_OUT2DEL_WIDTH 1 /* OUT2DEL */
#define WM8985_POBCTRL 0x0004 /* POBCTRL */
#define WM8985_POBCTRL_MASK 0x0004 /* POBCTRL */
#define WM8985_POBCTRL_SHIFT 2 /* POBCTRL */
@@ -684,6 +699,9 @@
#define WM8985_BEEPVOL_MASK 0x000E /* BEEPVOL - [3:1] */
#define WM8985_BEEPVOL_SHIFT 1 /* BEEPVOL - [3:1] */
#define WM8985_BEEPVOL_WIDTH 3 /* BEEPVOL - [3:1] */
+#define WM8758_DELEN2_MASK 0x0004 /* DELEN2 */
+#define WM8758_DELEN2_SHIFT 2 /* DELEN2 */
+#define WM8758_DELEN2_WIDTH 1 /* DELEN2 */
#define WM8985_BEEPEN 0x0001 /* BEEPEN */
#define WM8985_BEEPEN_MASK 0x0001 /* BEEPEN */
#define WM8985_BEEPEN_SHIFT 0 /* BEEPEN */
@@ -790,6 +808,14 @@
/*
* R49 (0x31) - Output ctrl
*/
+#define WM8758_HP_COM 0x0100 /* HP_COM */
+#define WM8758_HP_COM_MASK 0x0100 /* HP_COM */
+#define WM8758_HP_COM_SHIFT 8 /* HP_COM */
+#define WM8758_HP_COM_WIDTH 1 /* HP_COM */
+#define WM8758_LINE_COM 0x0080 /* LINE_COM */
+#define WM8758_LINE_COM_MASK 0x0080 /* LINE_COM */
+#define WM8758_LINE_COM_SHIFT 7 /* LINE_COM */
+#define WM8758_LINE_COM_WIDTH 1 /* LINE_COM */
#define WM8985_DACL2RMIX 0x0040 /* DACL2RMIX */
#define WM8985_DACL2RMIX_MASK 0x0040 /* DACL2RMIX */
#define WM8985_DACL2RMIX_SHIFT 6 /* DACL2RMIX */
@@ -806,6 +832,14 @@
#define WM8985_OUT3BOOST_MASK 0x0008 /* OUT3BOOST */
#define WM8985_OUT3BOOST_SHIFT 3 /* OUT3BOOST */
#define WM8985_OUT3BOOST_WIDTH 1 /* OUT3BOOST */
+#define WM8758_OUT4ENDEL 0x0010 /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_MASK 0x0010 /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_SHIFT 4 /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_WIDTH 1 /* OUT4ENDEL */
+#define WM8758_OUT3ENDEL 0x0008 /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_MASK 0x0008 /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_SHIFT 3 /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_WIDTH 1 /* OUT3ENDEL */
#define WM8985_TSOPCTRL 0x0004 /* TSOPCTRL */
#define WM8985_TSOPCTRL_MASK 0x0004 /* TSOPCTRL */
#define WM8985_TSOPCTRL_SHIFT 2 /* TSOPCTRL */
@@ -1021,6 +1055,10 @@
#define WM8985_HALFIPBIAS_MASK 0x0080 /* HALFIPBIAS */
#define WM8985_HALFIPBIAS_SHIFT 7 /* HALFIPBIAS */
#define WM8985_HALFIPBIAS_WIDTH 1 /* HALFIPBIAS */
+#define WM8758_HALFIPBIAS 0x0040 /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_MASK 0x0040 /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_SHIFT 6 /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_WIDTH 1 /* HALFI_IPGA */
#define WM8985_VBBIASTST_MASK 0x0060 /* VBBIASTST - [6:5] */
#define WM8985_VBBIASTST_SHIFT 5 /* VBBIASTST - [6:5] */
#define WM8985_VBBIASTST_WIDTH 2 /* VBBIASTST - [6:5] */
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 449f66636205..3a5c896a2d13 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -1166,6 +1166,7 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
};
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index a07bd7c2c587..21fbe7d07063 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -394,6 +394,7 @@ static const struct {
int compr_direction;
int num_caps;
const struct wm_adsp_fw_caps *caps;
+ bool voice_trigger;
} wm_adsp_fw[WM_ADSP_NUM_FW] = {
[WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
[WM_ADSP_FW_HIFI] = { .file = "hifi" },
@@ -406,6 +407,7 @@ static const struct {
.compr_direction = SND_COMPRESS_CAPTURE,
.num_caps = ARRAY_SIZE(ctrl_caps),
.caps = ctrl_caps,
+ .voice_trigger = true,
},
[WM_ADSP_FW_ASR] = { .file = "asr" },
[WM_ADSP_FW_TRACE] = {
@@ -2366,13 +2368,15 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
dsp->running = false;
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA |
- ADSP2_START, 0);
+ ADSP2_CORE_ENA | ADSP2_START, 0);
/* Make sure DMAs are quiesced */
+ regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
@@ -2998,6 +3002,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
goto out;
}
+ if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
+ ret = WM_ADSP_COMPR_VOICE_TRIGGER;
+
out_notify:
if (compr && compr->stream)
snd_compr_fragment_elapsed(compr->stream);
@@ -3037,12 +3044,8 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
buf = compr->buf;
- if (!compr->buf) {
- ret = -ENXIO;
- goto out;
- }
-
- if (compr->buf->error) {
+ if (!compr->buf || compr->buf->error) {
+ snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
ret = -EIO;
goto out;
}
@@ -3060,8 +3063,12 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
*/
if (buf->avail < wm_adsp_compr_frag_words(compr)) {
ret = wm_adsp_buffer_get_error(buf);
- if (ret < 0)
+ if (ret < 0) {
+ if (compr->buf->error)
+ snd_compr_stop_error(stream,
+ SNDRV_PCM_STATE_XRUN);
goto out;
+ }
ret = wm_adsp_buffer_reenable_irq(buf);
if (ret < 0) {
@@ -3156,11 +3163,10 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
- if (!compr->buf)
- return -ENXIO;
-
- if (compr->buf->error)
+ if (!compr->buf || compr->buf->error) {
+ snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
return -EIO;
+ }
count /= WM_ADSP_DATA_WORD_SIZE;
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index feb61e2c4bb4..be3b5bcb7f17 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -19,6 +19,10 @@
#include "wmfw.h"
+/* Return values for wm_adsp_compr_handle_irq */
+#define WM_ADSP_COMPR_OK 0
+#define WM_ADSP_COMPR_VOICE_TRIGGER 1
+
struct wm_adsp_region {
int type;
unsigned int base;
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 237dc67002ef..05c2d33aa74d 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -1599,7 +1599,14 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
pdata = pdev->dev.platform_data;
return pdata;
} else if (match) {
- pdata = (struct davinci_mcasp_pdata*) match->data;
+ pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "Failed to allocate memory for pdata\n");
+ ret = -ENOMEM;
+ return pdata;
+ }
} else {
/* control shouldn't reach here. something is wrong */
ret = -EINVAL;
diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig
index d50e08517dce..c297efe43861 100644
--- a/