summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-18 20:05:46 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-18 20:05:46 +0300
commit021f163d696caed5a336fa1569efdd22216da340 (patch)
tree8503e92e30aa11734d18d69174c02234e8ccaca6
parent9ea446352047d8350553250db51da2c73a610688 (diff)
parent222bde03881c470de8aa4ca8e58f5950c2b84d12 (diff)
downloadlinux-021f163d696caed5a336fa1569efdd22216da340.tar.xz
Merge tag 'sound-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "After a heavy storm by syzkaller in 4.5 cycle, we have relatively few changes in the core at this time while a lot of changes are found in the driver side, unsurprisingly. Below are some highlights: ALSA core: - A few more hardening in ALSA timer codes - An extension of sequencer API for advertising the card / pid - Small fixes in compress-offload and jack layers HD-audio: - Dynamic PCM assignment in HDMI/DP codec; preparation for upcoming DP-MST support - Lots of code refactoring for sharing with ASoC SKL driver - Regression fixes for Intel HDMI/DP - Fixups for CX20724 codec, Lenovo AiO USB-audio: - Add quirk_alias option to make quirk debugging easier - Fixes for possible Oops by malformed firmware Firewire: - Add support for FW-1804 in tascam driver - Improvements / changes in card registration, multi stream handling, etc for DICE - Lots of code refactoring ASoC: - Enhancements of still ongoing topology API - Lots of commits for Intel Skylake support including HDMI support - A few Intel Atom driver updates for recent devices - Lots of improvements to the Renesas drivers - Capture support for Qualcomm drivers - Support for TI DaVinci DRA7xxx devices - New machine drivers for Freescale systems with Cirrus CODECs, Mediatek systems with RT5650 CODECs - New CPU drivers for Allwinner S/PDIF controllers - New CODEC drivers for Maxim MAX9867 and MAX98926 and Realtek RT5514" * tag 'sound-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (291 commits) ALSA: hda - Fix mutex deadlock at HDMI/DP hotplug ALSA: ctl: change return value in compatibility layer so that it's the same value in core implementation ALSA: mixart: silence an uninitialized variable warning ALSA: usb-audio: Add sanity checks for endpoint accesses ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk() ALSA: usb-audio: Fix NULL dereference in create_fixed_stream_quirk() ALSA: hda - Limit i915 HDMI binding only for HSW and later ALSA: hda - Fix unconditional GPIO toggle via automute ALSA: mixart: silence unitialized variable warnings ALSA: hda - Fixes double fault in nvhdmi_chmap_cea_alloc_validate_get_type ALSA: intel8x0: Add clock quirk entry for AD1981B on IBM ThinkPad X41. ALSA: hda - Add new GPU codec ID 0x10de0082 to snd-hda ASoC: rsnd: add simplified module explanation ASoC: hdac_hdmi: Add broxton device ID ASoC: Intel: Bxtn: Add Broxton PCI ID ASoC: Intel: Skylake: Move Skylake dsp ops & loader ops ASoC: Intel: add dmabuffer to common sst_dsp ASoC: Intel: Skylake: Unstatify skl_dsp_enable_core ASoC: Intel: Skylake: Fix whitepsace issues ASoC: Intel: Skylake: Move module id defines ...
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau17x1.txt24
-rw-r--r--Documentation/devicetree/bindings/sound/fsl-asoc-card.txt9
-rw-r--r--Documentation/devicetree/bindings/sound/max9867.txt17
-rw-r--r--Documentation/devicetree/bindings/sound/max98926.txt32
-rw-r--r--Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt15
-rw-r--r--Documentation/devicetree/bindings/sound/mt8173-rt5650.txt15
-rw-r--r--Documentation/devicetree/bindings/sound/pcm179x.txt11
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.txt340
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt1
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip-i2s.txt1
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip-spdif.txt8
-rw-r--r--Documentation/devicetree/bindings/sound/rt5514.txt25
-rw-r--r--Documentation/devicetree/bindings/sound/rt5616.txt6
-rw-r--r--Documentation/devicetree/bindings/sound/rt5640.txt3
-rw-r--r--Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt39
-rw-r--r--Documentation/devicetree/bindings/sound/ti,ads117x.txt11
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt9
-rw-r--r--Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt74
-rw-r--r--drivers/misc/atmel-ssc.c1
-rw-r--r--include/drm/drm_edid.h12
-rw-r--r--include/linux/platform_data/adau17x1.h2
-rw-r--r--include/sound/hda_chmap.h76
-rw-r--r--include/sound/hdaudio.h2
-rw-r--r--include/sound/jack.h23
-rw-r--r--include/sound/pcm.h2
-rw-r--r--include/sound/soc-topology.h21
-rw-r--r--include/sound/soc.h2
-rw-r--r--include/uapi/sound/asequencer.h12
-rw-r--r--include/uapi/sound/asound.h4
-rw-r--r--sound/core/Kconfig9
-rw-r--r--sound/core/compress_offload.c12
-rw-r--r--sound/core/control_compat.c2
-rw-r--r--sound/core/jack.c23
-rw-r--r--sound/core/pcm_lib.c2
-rw-r--r--sound/core/pcm_misc.c30
-rw-r--r--sound/core/seq/seq_clientmgr.c14
-rw-r--r--sound/core/seq/seq_clientmgr.h2
-rw-r--r--sound/core/timer.c268
-rw-r--r--sound/drivers/mts64.c96
-rw-r--r--sound/drivers/portman2x4.c94
-rw-r--r--sound/firewire/bebob/bebob.c21
-rw-r--r--sound/firewire/bebob/bebob.h5
-rw-r--r--sound/firewire/bebob/bebob_midi.c16
-rw-r--r--sound/firewire/bebob/bebob_pcm.c28
-rw-r--r--sound/firewire/bebob/bebob_stream.c58
-rw-r--r--sound/firewire/dice/dice-midi.c31
-rw-r--r--sound/firewire/dice/dice-pcm.c278
-rw-r--r--sound/firewire/dice/dice-stream.c468
-rw-r--r--sound/firewire/dice/dice-transaction.c56
-rw-r--r--sound/firewire/dice/dice.c108
-rw-r--r--sound/firewire/dice/dice.h41
-rw-r--r--sound/firewire/fireworks/fireworks.c3
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c6
-rw-r--r--sound/firewire/oxfw/oxfw-scs1x.c35
-rw-r--r--sound/hda/Makefile2
-rw-r--r--sound/hda/hdac_device.c16
-rw-r--r--sound/hda/hdac_i915.c14
-rw-r--r--sound/hda/hdac_regmap.c69
-rw-r--r--sound/hda/hdmi_chmap.c791
-rw-r--r--sound/mips/Kconfig12
-rw-r--r--sound/mips/Makefile2
-rw-r--r--sound/mips/au1x00.c734
-rw-r--r--sound/pci/Kconfig2
-rw-r--r--sound/pci/hda/Kconfig2
-rw-r--r--sound/pci/hda/hda_eld.c31
-rw-r--r--sound/pci/hda/hda_intel.c2
-rw-r--r--sound/pci/hda/patch_cirrus.c8
-rw-r--r--sound/pci/hda/patch_conexant.c7
-rw-r--r--sound/pci/hda/patch_hdmi.c1451
-rw-r--r--sound/pci/hda/patch_realtek.c1
-rw-r--r--sound/pci/hda/thinkpad_helper.c17
-rw-r--r--sound/pci/intel8x0.c1
-rw-r--r--sound/pci/mixart/mixart.c2
-rw-r--r--sound/pci/mixart/mixart_mixer.c4
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c27
-rw-r--r--sound/soc/bcm/bcm2835-i2s.c284
-rw-r--r--sound/soc/codecs/Kconfig38
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ab8500-codec.c1
-rw-r--r--sound/soc/codecs/adau1761-i2c.c14
-rw-r--r--sound/soc/codecs/adau1761-spi.c14
-rw-r--r--sound/soc/codecs/adau1761.c10
-rw-r--r--sound/soc/codecs/adau1781-i2c.c10
-rw-r--r--sound/soc/codecs/adau1781-spi.c10
-rw-r--r--sound/soc/codecs/adau1781.c2
-rw-r--r--sound/soc/codecs/ads117x.c12
-rw-r--r--sound/soc/codecs/arizona.c71
-rw-r--r--sound/soc/codecs/arizona.h4
-rw-r--r--sound/soc/codecs/cs42xx8.c10
-rw-r--r--sound/soc/codecs/cs47l24.c123
-rw-r--r--sound/soc/codecs/hdac_hdmi.c1219
-rw-r--r--sound/soc/codecs/hdac_hdmi.h6
-rwxr-xr-xsound/soc/codecs/max9867.c546
-rwxr-xr-xsound/soc/codecs/max9867.h83
-rw-r--r--sound/soc/codecs/max98926.c606
-rw-r--r--sound/soc/codecs/max98926.h848
-rw-r--r--sound/soc/codecs/nau8825.c169
-rw-r--r--sound/soc/codecs/nau8825.h16
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c73
-rw-r--r--sound/soc/codecs/pcm179x-spi.c72
-rw-r--r--sound/soc/codecs/pcm179x.c56
-rw-r--r--sound/soc/codecs/pcm179x.h9
-rw-r--r--sound/soc/codecs/pcm3168a.c8
-rw-r--r--sound/soc/codecs/rt298.c7
-rw-r--r--sound/soc/codecs/rt298.h8
-rw-r--r--sound/soc/codecs/rt5514.c982
-rw-r--r--sound/soc/codecs/rt5514.h252
-rw-r--r--sound/soc/codecs/rt5616.c414
-rw-r--r--sound/soc/codecs/rt5640.c91
-rw-r--r--sound/soc/codecs/rt5640.h2
-rw-r--r--sound/soc/codecs/rt5645.c15
-rw-r--r--sound/soc/codecs/rt5659.c2
-rw-r--r--sound/soc/codecs/ssm4567.c5
-rw-r--r--sound/soc/codecs/wm5102.c93
-rw-r--r--sound/soc/codecs/wm5110.c75
-rw-r--r--sound/soc/codecs/wm8974.c93
-rw-r--r--sound/soc/codecs/wm8997.c2
-rw-r--r--sound/soc/codecs/wm8998.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c131
-rw-r--r--sound/soc/codecs/wm_adsp.h8
-rw-r--r--sound/soc/davinci/Kconfig3
-rw-r--r--sound/soc/davinci/davinci-mcasp.c12
-rw-r--r--sound/soc/fsl/Kconfig4
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c26
-rw-r--r--sound/soc/fsl/fsl_sai.c3
-rw-r--r--sound/soc/fsl/fsl_ssi.c42
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c3
-rw-r--r--sound/soc/intel/Kconfig4
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c4
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c2
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c50
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c17
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c28
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c122
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c130
-rw-r--r--sound/soc/intel/boards/skl_rt286.c113
-rw-r--r--sound/soc/intel/common/sst-acpi.h3
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h1
-rw-r--r--sound/soc/intel/common/sst-match-acpi.c45
-rw-r--r--sound/soc/intel/skylake/skl-messages.c124
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c34
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c106
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c14
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h7
-rw-r--r--sound/soc/intel/skylake/skl-sst.c3
-rw-r--r--sound/soc/intel/skylake/skl-topology.c208
-rw-r--r--sound/soc/intel/skylake/skl-topology.h27
-rw-r--r--sound/soc/intel/skylake/skl-tplg-interface.h3
-rw-r--r--sound/soc/intel/skylake/skl.c88
-rw-r--r--sound/soc/intel/skylake/skl.h15
-rw-r--r--sound/soc/mediatek/Kconfig22
-rw-r--r--sound/soc/mediatek/Makefile2
-rw-r--r--sound/soc/mediatek/mt8173-rt5650-rt5514.c258
-rw-r--r--sound/soc/mediatek/mt8173-rt5650-rt5676.c18
-rw-r--r--sound/soc/mediatek/mt8173-rt5650.c236
-rw-r--r--sound/soc/mediatek/mtk-afe-common.h1
-rw-r--r--sound/soc/mediatek/mtk-afe-pcm.c47
-rw-r--r--sound/soc/mxs/mxs-saif.c2
-rw-r--r--sound/soc/omap/omap-hdmi-audio.c1
-rw-r--r--sound/soc/pxa/brownstone.c2
-rw-r--r--sound/soc/qcom/Kconfig7
-rw-r--r--sound/soc/qcom/apq8016_sbc.c10
-rw-r--r--sound/soc/qcom/lpass-apq8016.c31
-rw-r--r--sound/soc/qcom/lpass-cpu.c146
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c11
-rw-r--r--sound/soc/qcom/lpass-lpaif-reg.h116
-rw-r--r--sound/soc/qcom/lpass-platform.c244
-rw-r--r--sound/soc/qcom/lpass.h10
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c13
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c17
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c2
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.h2
-rw-r--r--sound/soc/sh/rcar/adg.c218
-rw-r--r--sound/soc/sh/rcar/cmd.c18
-rw-r--r--sound/soc/sh/rcar/core.c160
-rw-r--r--sound/soc/sh/rcar/ctu.c281
-rw-r--r--sound/soc/sh/rcar/dma.c56
-rw-r--r--sound/soc/sh/rcar/dvc.c33
-rw-r--r--sound/soc/sh/rcar/gen.c56
-rw-r--r--sound/soc/sh/rcar/mix.c4
-rw-r--r--sound/soc/sh/rcar/rsnd.h115
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c27
-rw-r--r--sound/soc/sh/rcar/src.c171
-rw-r--r--sound/soc/sh/rcar/ssi.c334
-rw-r--r--sound/soc/sh/rcar/ssiu.c20
-rw-r--r--sound/soc/soc-core.c8
-rw-r--r--sound/soc/soc-dapm.c4
-rw-r--r--sound/soc/soc-pcm.c24
-rw-r--r--sound/soc/soc-topology.c240
-rw-r--r--sound/soc/sunxi/Kconfig8
-rw-r--r--sound/soc/sunxi/Makefile1
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c550
-rw-r--r--sound/usb/card.c56
-rw-r--r--sound/usb/clock.c2
-rw-r--r--sound/usb/endpoint.c3
-rw-r--r--sound/usb/midi.c15
-rw-r--r--sound/usb/midi.h14
-rw-r--r--sound/usb/mixer_quirks.c4
-rw-r--r--sound/usb/pcm.c2
-rw-r--r--sound/usb/quirks.c58
-rw-r--r--sound/usb/quirks.h3
202 files changed, 12770 insertions, 4360 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,adau17x1.txt b/Documentation/devicetree/bindings/sound/adi,adau17x1.txt
new file mode 100644
index 000000000000..8dbce0e18dda
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/adi,adau17x1.txt
@@ -0,0 +1,24 @@
+Analog Devices ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781
+
+Required properties:
+
+ - compatible: Should contain one of the following:
+ "adi,adau1361"
+ "adi,adau1461"
+ "adi,adau1761"
+ "adi,adau1961"
+ "adi,adau1381"
+ "adi,adau1781"
+
+ - reg: The i2c address. Value depends on the state of ADDR0
+ and ADDR1, as wired in hardware.
+
+Examples:
+#include <dt-bindings/sound/adau17x1.h>
+
+ i2c_bus {
+ adau1361@38 {
+ compatible = "adi,adau1761";
+ reg = <0x38>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
index 4da41bf1888e..ceaef5126989 100644
--- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
+++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
@@ -24,6 +24,9 @@ The compatible list for this generic sound card currently:
"fsl,imx-audio-cs42888"
+ "fsl,imx-audio-cs427x"
+ (compatible with CS4271 and CS4272)
+
"fsl,imx-audio-wm8962"
(compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
@@ -63,6 +66,12 @@ Optional properties:
- audio-asrc : The phandle of ASRC. It can be absent if there's no
need to add ASRC support via DPCM.
+Optional unless SSI is selected as a CPU DAI:
+
+ - mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
+
+ - mux-ext-port : The external port of the i.MX audio muxer
+
Example:
sound-cs42888 {
compatible = "fsl,imx-audio-cs42888";
diff --git a/Documentation/devicetree/bindings/sound/max9867.txt b/Documentation/devicetree/bindings/sound/max9867.txt
new file mode 100644
index 000000000000..394cd4eb17ec
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/max9867.txt
@@ -0,0 +1,17 @@
+max9867 codec
+
+This device supports I2C mode only.
+
+Required properties:
+
+- compatible : "maxim,max9867"
+- reg : The chip select number on the I2C bus
+
+Example:
+
+&i2c {
+ max9867: max9867@0x18 {
+ compatible = "maxim,max9867";
+ reg = <0x18>;
+ };
+};
diff --git a/Documentation/devicetree/bindings/sound/max98926.txt b/Documentation/devicetree/bindings/sound/max98926.txt
new file mode 100644
index 000000000000..0b7f4e4d5f9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/max98926.txt
@@ -0,0 +1,32 @@
+max98926 audio CODEC
+
+This device supports I2C.
+
+Required properties:
+
+ - compatible : "maxim,max98926"
+
+ - vmon-slot-no : slot number used to send voltage information
+ or in inteleave mode this will be used as
+ interleave slot.
+
+ - imon-slot-no : slot number used to send current information
+
+ - interleave-mode : When using two MAX98926 in a system it is
+ possible to create ADC data that that will
+ overflow the frame size. Digital Audio Interleave
+ mode provides a means to output VMON and IMON data
+ from two devices on a single DOUT line when running
+ smaller frames sizes such as 32 BCLKS per LRCLK or
+ 48 BCLKS per LRCLK.
+
+ - reg : the I2C address of the device for I2C
+
+Example:
+
+codec: max98926@1a {
+ compatible = "maxim,max98926";
+ vmon-slot-no = <0>;
+ imon-slot-no = <2>;
+ reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt
new file mode 100644
index 000000000000..e8b3c80c6fff
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt
@@ -0,0 +1,15 @@
+MT8173 with RT5650 RT5514 CODECS
+
+Required properties:
+- compatible : "mediatek,mt8173-rt5650-rt5514"
+- mediatek,audio-codec: the phandles of rt5650 and rt5514 codecs
+- mediatek,platform: the phandle of MT8173 ASoC platform
+
+Example:
+
+ sound {
+ compatible = "mediatek,mt8173-rt5650-rt5514";
+ mediatek,audio-codec = <&rt5650 &rt5514>;
+ mediatek,platform = <&afe>;
+ };
+
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
new file mode 100644
index 000000000000..fe5a5ef1714d
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
@@ -0,0 +1,15 @@
+MT8173 with RT5650 CODECS
+
+Required properties:
+- compatible : "mediatek,mt8173-rt5650"
+- mediatek,audio-codec: the phandles of rt5650 codecs
+- mediatek,platform: the phandle of MT8173 ASoC platform
+
+Example:
+
+ sound {
+ compatible = "mediatek,mt8173-rt5650";
+ mediatek,audio-codec = <&rt5650>;
+ mediatek,platform = <&afe>;
+ };
+
diff --git a/Documentation/devicetree/bindings/sound/pcm179x.txt b/Documentation/devicetree/bindings/sound/pcm179x.txt
index 4ae70d3462d6..436c2b247693 100644
--- a/Documentation/devicetree/bindings/sound/pcm179x.txt
+++ b/Documentation/devicetree/bindings/sound/pcm179x.txt
@@ -1,6 +1,6 @@
Texas Instruments pcm179x DT bindings
-This driver supports the SPI bus.
+This driver supports both the I2C and SPI bus.
Required properties:
@@ -9,6 +9,11 @@ Required properties:
For required properties on SPI, please consult
Documentation/devicetree/bindings/spi/spi-bus.txt
+Required properties on I2C:
+
+ - reg: the I2C address
+
+
Examples:
codec_spi: 1792a@0 {
@@ -16,3 +21,7 @@ Examples:
spi-max-frequency = <600000>;
};
+ codec_i2c: 1792a@4c {
+ compatible = "ti,pcm1792a";
+ reg = <0x4c>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
index 8ee0fa91e4a0..c7b29df4a963 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt
@@ -1,6 +1,337 @@
Renesas R-Car sound
+=============================================
+* Modules
+=============================================
+
+Renesas R-Car sound is constructed from below modules
+(for Gen2 or later)
+
+ SCU : Sampling Rate Converter Unit
+ - SRC : Sampling Rate Converter
+ - CMD
+ - CTU : Channel Transfer Unit
+ - MIX : Mixer
+ - DVC : Digital Volume and Mute Function
+ SSIU : Serial Sound Interface Unit
+ SSI : Serial Sound Interface
+
+See detail of each module's channels, connection, limitation on datasheet
+
+=============================================
+* Multi channel
+=============================================
+
+Multi channel is supported by Multi-SSI, or TDM-SSI.
+
+ Multi-SSI : 6ch case, you can use stereo x 3 SSI
+ TDM-SSI : 6ch case, you can use TDM
+
+=============================================
+* Enable/Disable each modules
+=============================================
+
+See datasheet to check SRC/CTU/MIX/DVC connect-limitation.
+DT controls enabling/disabling module.
+${LINUX}/arch/arm/boot/dts/r8a7790-lager.dts can be good example.
+This is example of
+
+Playback: [MEM] -> [SRC2] -> [DVC0] -> [SSIU0/SSI0] -> [codec]
+Capture: [MEM] <- [DVC1] <- [SRC3] <- [SSIU1/SSI1] <- [codec]
+
+ &rcar_sound {
+ ...
+ rcar_sound,dai {
+ dai0 {
+ playback = <&ssi0 &src2 &dvc0>;
+ capture = <&ssi1 &src3 &dvc1>;
+ };
+ };
+ };
+
+You can use below.
+${LINUX}/arch/arm/boot/dts/r8a7790.dts can be good example.
+
+ &src0 &ctu00 &mix0 &dvc0 &ssi0
+ &src1 &ctu01 &mix1 &dvc1 &ssi1
+ &src2 &ctu02 &ssi2
+ &src3 &ctu03 &ssi3
+ &src4 &ssi4
+ &src5 &ctu10 &ssi5
+ &src6 &ctu11 &ssi6
+ &src7 &ctu12 &ssi7
+ &src8 &ctu13 &ssi8
+ &src9 &ssi9
+
+=============================================
+* SRC (Sampling Rate Converter)
+=============================================
+
+ [xx]Hz [yy]Hz
+ ------> [SRC] ------>
+
+SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes
+
+ Asynchronous mode: input data / output data are based on different clocks.
+ you can use this mode on Playback / Capture
+ Synchronous mode: input data / output data are based on same clocks.
+ This mode will be used if system doesn't have its input clock,
+ for example digital TV case.
+ you can use this mode on Playback
+
+------------------
+** Asynchronous mode
+------------------
+
+You need to use "renesas,rsrc-card" sound card for it.
+example)
+
+ sound {
+ compatible = "renesas,rsrc-card";
+ ...
+ /*
+ * SRC Asynchronous mode setting
+ * Playback:
+ * All input data will be converted to 48kHz
+ * Capture:
+ * Inputed 48kHz data will be converted to
+ * system specified Hz
+ */
+ convert-rate = <48000>;
+ ...
+ cpu {
+ sound-dai = <&rcar_sound>;
+ };
+ codec {
+ ...
+ };
+ };
+
+------------------
+** Synchronous mode
+------------------
+
+ > amixer set "SRC Out Rate" on
+ > aplay xxxx.wav
+ > amixer set "SRC Out Rate" 48000
+ > amixer set "SRC Out Rate" 44100
+
+=============================================
+* CTU (Channel Transfer Unit)
+=============================================
+
+ [xx]ch [yy]ch
+ ------> [CTU] -------->
+
+CTU can convert [xx]ch to [yy]ch, or exchange outputed channel.
+CTU conversion needs matrix settings.
+For more detail information, see below
+
+ Renesas R-Car datasheet
+ - Sampling Rate Converter Unit (SCU)
+ - SCU Operation
+ - CMD Block
+ - Functional Blocks in CMD
+
+ Renesas R-Car datasheet
+ - Sampling Rate Converter Unit (SCU)
+ - Register Description
+ - CTUn Scale Value exx Register (CTUn_SVxxR)
+
+ ${LINUX}/sound/soc/sh/rcar/ctu.c
+ - comment of header
+
+You need to use "renesas,rsrc-card" sound card for it.
+example)
+
+ sound {
+ compatible = "renesas,rsrc-card";
+ ...
+ /*
+ * CTU setting
+ * All input data will be converted to 2ch
+ * as output data
+ */
+ convert-channels = <2>;
+ ...
+ cpu {
+ sound-dai = <&rcar_sound>;
+ };
+ codec {
+ ...
+ };
+ };
+
+Ex) Exchange output channel
+ Input -> Output
+ 1ch -> 0ch
+ 0ch -> 1ch
+
+ example of using matrix
+ output 0ch = (input 0ch x 0) + (input 1ch x 1)
+ output 1ch = (input 0ch x 1) + (input 1ch x 0)
+
+ amixer set "CTU Reset" on
+ amixer set "CTU Pass" 9,10
+ amixer set "CTU SV0" 0,4194304
+ amixer set "CTU SV1" 4194304,0
+
+ example of changing connection
+ amixer set "CTU Reset" on
+ amixer set "CTU Pass" 2,1
+
+=============================================
+* MIX (Mixer)
+=============================================
+
+MIX merges 2 sounds path. You can see 2 sound interface on system,
+and these sounds will be merged by MIX.
+
+ aplay -D plughw:0,0 xxxx.wav &
+ aplay -D plughw:0,1 yyyy.wav
+
+You need to use "renesas,rsrc-card" sound card for it.
+Ex)
+ [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0]
+ |
+ [MEM] -> [SRC2] -> [CTU03] -+
+
+ sound {
+ compatible = "renesas,rsrc-card";
+ ...
+ cpu@0 {
+ sound-dai = <&rcar_sound 0>;
+ };
+ cpu@1 {
+ sound-dai = <&rcar_sound 1>;
+ };
+ codec {
+ ...
+ };
+ };
+
+ &rcar_sound {
+ ...
+ rcar_sound,dai {
+ dai0 {
+ playback = <&src1 &ctu02 &mix0 &dvc0 &ssi0>;
+ };
+ dai1 {
+ playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>;
+ };
+ };
+ };
+
+=============================================
+* DVC (Digital Volume and Mute Function)
+=============================================
+
+DVC controls Playback/Capture volume.
+
+Playback Volume
+ amixer set "DVC Out" 100%
+
+Capture Volume
+ amixer set "DVC In" 100%
+
+Playback Mute
+ amixer set "DVC Out Mute" on
+
+Capture Mute
+ amixer set "DVC In Mute" on
+
+Volume Ramp
+ amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps"
+ amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
+ amixer set "DVC Out Ramp" on
+ aplay xxx.wav &
+ amixer set "DVC Out" 80% // Volume Down
+ amixer set "DVC Out" 100% // Volume Up
+
+=============================================
+* SSIU (Serial Sound Interface Unit)
+=============================================
+
+There is no DT settings for SSIU, because SSIU will be automatically
+selected via SSI.
+SSIU can avoid some under/over run error, because it has some buffer.
+But you can't use it if SSI was PIO mode.
+In DMA mode, you can select not to use SSIU by using "no-busif" on DT.
+
+ &ssi0 {
+ no-busif;
+ };
+
+=============================================
+* SSI (Serial Sound Interface)
+=============================================
+
+** PIO mode
+
+You can use PIO mode which is for connection check by using.
+Note: The system will drop non-SSI modules in PIO mode
+even though if DT is selecting other modules.
+
+ &ssi0 {
+ pio-transfer
+ };
+
+** DMA mode without SSIU
+
+You can use DMA without SSIU.
+Note: under/over run, or noise are likely to occur
+
+ &ssi0 {
+ no-busif;
+ };
+
+** PIN sharing
+
+Each SSI can share WS pin. It is based on platform.
+This is example if SSI1 want to share WS pin with SSI0
+
+ &ssi1 {
+ shared-pin;
+ };
+
+** Multi-SSI
+
+You can use Multi-SSI.
+This is example of SSI0/SSI1/SSI2 (= for 6ch)
+
+ &rcar_sound {
+ ...
+ rcar_sound,dai {
+ dai0 {
+ playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
+ };
+ };
+ };
+
+** TDM-SSI
+
+You can use TDM with SSI.
+This is example of TDM 6ch.
+Driver can automatically switches TDM <-> stereo mode in this case.
+
+ rsnd_tdm: sound {
+ compatible = "simple-audio-card";
+ ...
+ simple-audio-card,cpu {
+ /* system can use TDM 6ch */
+ dai-tdm-slot-num = <6>;
+ sound-dai = <&rcar_sound>;
+ };
+ simple-audio-card,codec {
+ ...
+ };
+ };
+
+
+=============================================
Required properties:
+=============================================
+
- compatible : "renesas,rcar_sound-<soctype>", fallbacks
"renesas,rcar_sound-gen1" if generation1, and
"renesas,rcar_sound-gen2" if generation2
@@ -64,7 +395,10 @@ DAI subnode properties:
- playback : list of playback modules
- capture : list of capture modules
+
+=============================================
Example:
+=============================================
rcar_sound: sound@ec500000 {
#sound-dai-cells = <1>;
@@ -250,7 +584,9 @@ rcar_sound: sound@ec500000 {
};
};
+=============================================
Example: simple sound card
+=============================================
rsnd_ak4643: sound {
compatible = "simple-audio-card";
@@ -290,7 +626,9 @@ Example: simple sound card
shared-pin;
};
+=============================================
Example: simple sound card for TDM
+=============================================
rsnd_tdm: sound {
compatible = "simple-audio-card";
@@ -309,7 +647,9 @@ Example: simple sound card for TDM
};
};
+=============================================
Example: simple sound card for Multi channel
+=============================================
&rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>;
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt
index 2b2caa281ce3..255ece3043ad 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt
+++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt
@@ -30,6 +30,7 @@ Optional subnode properties:
- frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion.
- convert-rate : platform specified sampling rate convert
+- convert-channels : platform specified converted channel size (2 - 8 ch)
- audio-prefix : see audio-routing
- audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt
index b7f3a9325ebd..6e86d8aa29b4 100644
--- a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt
@@ -9,6 +9,7 @@ Required properties:
- "rockchip,rk3066-i2s": for rk3066
- "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188
- "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288
+ - "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: should contain the I2S interrupt.
diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt
index e64dbdea7db9..11046429a118 100644
--- a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt
+++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt
@@ -7,8 +7,12 @@ a fibre cable.
Required properties:
- compatible: should be one of the following:
- - "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or
- "rockchip,rk3066-spdif"
+ - "rockchip,rk3066-spdif"
+ - "rockchip,rk3188-spdif"
+ - "rockchip,rk3288-spdif"
+ - "rockchip,rk3366-spdif"
+ - "rockchip,rk3368-spdif"
+ - "rockchip,rk3399-spdif"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: should contain the SPDIF interrupt.
diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt
new file mode 100644
index 000000000000..e24436fc5ea9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/rt5514.txt
@@ -0,0 +1,25 @@
+RT5514 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+- compatible : "realtek,rt5514".
+
+- reg : The I2C address of the device.
+
+Pins on the device (for linking into audio routes) for RT5514:
+
+ * DMIC1L
+ * DMIC1R
+ * DMIC2L
+ * DMIC2R
+ * AMICL
+ * AMICR
+
+Example:
+
+codec: rt5514@57 {
+ compatible = "realtek,rt5514";
+ reg = <0x57>;
+};
diff --git a/Documentation/devicetree/bindings/sound/rt5616.txt b/Documentation/devicetree/bindings/sound/rt5616.txt
index efc48c65198d..e41085818559 100644
--- a/Documentation/devicetree/bindings/sound/rt5616.txt
+++ b/Documentation/devicetree/bindings/sound/rt5616.txt
@@ -8,6 +8,12 @@ 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 RT5616:
* IN1P
diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt
index 9e62f6eb348f..57fe64643050 100644
--- a/Documentation/devicetree/bindings/sound/rt5640.txt
+++ b/Documentation/devicetree/bindings/sound/rt5640.txt
@@ -12,6 +12,9 @@ Required properties:
Optional properties:
+- clocks: The phandle of the master clock to the CODEC
+- clock-names: Should be "mclk"
+
- realtek,in1-differential
- realtek,in2-differential
- realtek,in3-differential
diff --git a/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt b/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt
new file mode 100644
index 000000000000..13503aa505a9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt
@@ -0,0 +1,39 @@
+Allwinner Sony/Philips Digital Interface Format (S/PDIF) Controller
+
+The Allwinner S/PDIF audio block is a transceiver that allows the
+processor to receive and transmit digital audio via an coaxial cable or
+a fibre cable.
+For now only playback is supported.
+
+Required properties:
+
+ - compatible : should be one of the following:
+ - "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
+
+ - reg : Offset and length of the register set for the device.
+
+ - interrupts : Contains the spdif interrupt.
+
+ - dmas : Generic dma devicetree binding as described in
+ Documentation/devicetree/bindings/dma/dma.txt.
+
+ - dma-names : Two dmas have to be defined, "tx" and "rx".
+
+ - clocks : Contains an entry for each entry in clock-names.
+
+ - clock-names : Includes the following entries:
+ "apb" clock for the spdif bus.
+ "spdif" clock for spdif controller.
+
+Example:
+
+spdif: spdif@01c21000 {
+ compatible = "allwinner,sun4i-a10-spdif";
+ reg = <0x01c21000 0x40>;
+ interrupts = <13>;
+ clocks = <&apb0_gates 1>, <&spdif_clk>;
+ clock-names = "apb", "spdif";
+ dmas = <&dma 0 2>, <&dma 0 2>;
+ dma-names = "rx", "tx";
+ status = "okay";
+};
diff --git a/Documentation/devicetree/bindings/sound/ti,ads117x.txt b/Documentation/devicetree/bindings/sound/ti,ads117x.txt
new file mode 100644
index 000000000000..7db19b50865a
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,ads117x.txt
@@ -0,0 +1,11 @@
+Texas Intstruments ADS117x ADC
+
+Required properties:
+
+ - compatible : "ti,ads1174" or "ti,ads1178"
+
+Example:
+
+ads1178 {
+ compatible = "ti,ads1178";
+};
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 48148d6d9307..fc53ccd9a629 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -1910,6 +1910,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
- Default: 0x0000
ignore_ctl_error - Ignore any USB-controller regarding mixer
interface (default: no)
+ autoclock - Enable auto-clock selection for UAC2 devices
+ (default: yes)
+ quirk_alias - Quirk alias list, pass strings like
+ "0123abcd:5678beef", which applies the existing
+ quirk for the device 5678:beef to a new device
+ 0123:abcd.
This module supports multiple devices, autoprobe and hotplugging.
@@ -1919,6 +1925,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
NB: ignore_ctl_error=1 may help when you get an error at accessing
the mixer element such as URB error -22. This happens on some
buggy USB device or the controller.
+ NB: quirk_alias option is provided only for testing / development.
+ If you want to have a proper support, contact to upstream for
+ adding the matching quirk in the driver code statically.
Module snd-usb-caiaq
--------------------
diff --git a/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt b/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt
new file mode 100644
index 000000000000..82744ac3513d
--- /dev/null
+++ b/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt
@@ -0,0 +1,74 @@
+To support DP MST audio, HD Audio hdmi codec driver introduces virtual pin
+and dynamic pcm assignment.
+
+Virtual pin is an extension of per_pin. The most difference of DP MST
+from legacy is that DP MST introduces device entry. Each pin can contain
+several device entries. Each device entry behaves as a pin.
+
+As each pin may contain several device entries and each codec may contain
+several pins, if we use one pcm per per_pin, there will be many PCMs.
+The new solution is to create a few PCMs and to dynamically bind pcm to
+per_pin. Driver uses spec->dyn_pcm_assign flag to indicate whether to use
+the new solution.
+
+PCM
+===
+To be added
+
+
+Jack
+====
+
+Presume:
+ - MST must be dyn_pcm_assign, and it is acomp (for Intel scenario);
+ - NON-MST may or may not be dyn_pcm_assign, it can be acomp or !acomp;
+
+So there are the following scenarios:
+ a. MST (&& dyn_pcm_assign && acomp)
+ b. NON-MST && dyn_pcm_assign && acomp
+ c. NON-MST && !dyn_pcm_assign && !acomp
+
+Below discussion will ignore MST and NON-MST difference as it doesn't
+impact on jack handling too much.
+
+Driver uses struct hdmi_pcm pcm[] array in hdmi_spec and snd_jack is
+a member of hdmi_pcm. Each pin has one struct hdmi_pcm * pcm pointer.
+
+For !dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n] statically.
+
+For dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n]
+when monitor is hotplugged.
+
+
+Build Jack
+----------
+
+- dyn_pcm_assign
+Will not use hda_jack but use snd_jack in spec->pcm_rec[pcm_idx].jack directly.
+
+- !dyn_pcm_assign
+Use hda_jack and assign spec->pcm_rec[pcm_idx].jack = jack->jack statically.
+
+
+Unsolicited Event Enabling
+--------------------------
+Enable unsolicited event if !acomp.
+
+
+Monitor Hotplug Event Handling
+------------------------------
+- acomp
+pin_eld_notify() -> check_presence_and_report() -> hdmi_present_sense() ->
+sync_eld_via_acomp().
+Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for
+both dyn_pcm_assign and !dyn_pcm_assign
+
+- !acomp
+Hdmi_unsol_event() -> hdmi_intrinsic_event() -> check_presence_and_report() ->
+hdmi_present_sense() -> hdmi_prepsent_sense_via_verbs()
+Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for dyn_pcm_assign.
+Use hda_jack mechanism to handle jack events.
+
+
+Others to be added later
+========================
diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c
index e11a0bd6c66e..0516ecda54d3 100644
--- a/drivers/misc/atmel-ssc.c
+++ b/drivers/misc/atmel-ssc.c
@@ -34,6 +34,7 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
if (ssc->pdev->dev.of_node) {
if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
== ssc_num) {
+ ssc->pdev->id = ssc_num;
ssc_valid = 1;
break;
}
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 2af97691e878..dec6221e8198 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -403,6 +403,18 @@ static inline int drm_eld_size(const uint8_t *eld)
return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
}
+/**
+ * drm_eld_get_conn_type - Get device type hdmi/dp connected
+ * @eld: pointer to an ELD memory structure
+ *
+ * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to
+ * identify the display type connected.
+ */
+static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
+{
+ return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK;
+}
+
struct edid *drm_do_get_edid(struct drm_connector *connector,
int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
size_t len),
diff --git a/include/linux/platform_data/adau17x1.h b/include/linux/platform_data/adau17x1.h
index a81766cae230..9db1b905df24 100644
--- a/include/linux/platform_data/adau17x1.h
+++ b/include/linux/platform_data/adau17x1.h
@@ -1,5 +1,5 @@
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781 codecs
*
* Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h
new file mode 100644
index 000000000000..e20d219a0304
--- /dev/null
+++ b/include/sound/hda_chmap.h
@@ -0,0 +1,76 @@
+/*
+ * For multichannel support
+ */
+
+#ifndef __SOUND_HDA_CHMAP_H
+#define __SOUND_HDA_CHMAP_H
+
+#include <sound/pcm.h>
+#include <sound/hdaudio.h>
+
+
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+
+struct hdac_cea_channel_speaker_allocation {
+ int ca_index;
+ int speakers[8];
+
+ /* derived values, just for convenience */
+ int channels;
+ int spk_mask;
+};
+struct hdac_chmap;
+
+struct hdac_chmap_ops {
+ /*
+ * Helpers for producing the channel map TLVs. These can be overridden
+ * for devices that have non-standard mapping requirements.
+ */
+ int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels);
+ void (*cea_alloc_to_tlv_chmap)(struct hdac_chmap *hchmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels);
+
+ /* check that the user-given chmap is supported */
+ int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
+ int channels, unsigned char *chmap);
+
+ void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap);
+ void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap, int prepared);
+ bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx);
+
+ /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
+ int (*pin_get_slot_channel)(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot);
+ int (*pin_set_slot_channel)(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot, int channel);
+ void (*set_channel_count)(struct hdac_device *codec,
+ hda_nid_t cvt_nid, int chs);
+};
+
+struct hdac_chmap {
+ unsigned int channels_max; /* max over all cvts */
+ struct hdac_chmap_ops ops;
+ struct hdac_device *hdac;
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+ struct hdac_chmap *chmap);
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+ int channels, bool chmap_set,
+ bool non_pcm, unsigned char *map);
+int snd_hdac_get_active_channels(int ca);
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid, bool non_pcm, int ca,
+ int channels, unsigned char *map,
+ bool chmap_set);
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca);
+int snd_hdac_chmap_to_spk_mask(unsigned char c);
+int snd_hdac_spk_to_chmap(int spk);
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+ struct hdac_chmap *chmap);
+#endif /* __SOUND_HDA_CHMAP_H */
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index c21c38ce7450..93e63c56f48f 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -168,11 +168,13 @@ int snd_hdac_power_up(struct hdac_device *codec);
int snd_hdac_power_down(struct hdac_device *codec);
int snd_hdac_power_up_pm(struct hdac_device *codec);
int snd_hdac_power_down_pm(struct hdac_device *codec);
+int snd_hdac_keep_power_up(struct hdac_device *codec);
#else
static inline int snd_hdac_power_up(struct hdac_device *codec) { return 0; }
static inline int snd_hdac_power_down(struct hdac_device *codec) { return 0; }
static inline int snd_hdac_power_up_pm(struct hdac_device *codec) { return 0; }
static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; }
+static inline int snd_hdac_keep_power_up(struct hdac_device *codec) { return 0; }
#endif
/*
diff --git a/include/sound/jack.h b/include/sound/jack.h
index 23bede121c78..1e84bfb553cf 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -72,14 +72,16 @@ enum snd_jack_types {
#define SND_JACK_SWITCH_TYPES 6
struct snd_jack {
- struct input_dev *input_dev;
struct list_head kctl_list;
struct snd_card *card;
+ const char *id;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ struct input_dev *input_dev;
int registered;
int type;
- const char *id;
char name[100];
unsigned int key[6]; /* Keep in sync with definitions above */
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
void *private_data;
void (*private_free)(struct snd_jack *);
};
@@ -89,10 +91,11 @@ struct snd_jack {
int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack **jack, bool initial_kctl, bool phantom_jack);
int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask);
+#ifdef CONFIG_SND_JACK_INPUT_DEV
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent);
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
int keytype);
-
+#endif
void snd_jack_report(struct snd_jack *jack, int status);
#else
@@ -107,6 +110,13 @@ static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name
return 0;
}
+static inline void snd_jack_report(struct snd_jack *jack, int status)
+{
+}
+
+#endif
+
+#if !defined(CONFIG_SND_JACK) || !defined(CONFIG_SND_JACK_INPUT_DEV)
static inline void snd_jack_set_parent(struct snd_jack *jack,
struct device *parent)
{
@@ -118,11 +128,6 @@ static inline int snd_jack_set_key(struct snd_jack *jack,
{
return 0;
}
-
-static inline void snd_jack_report(struct snd_jack *jack, int status)
-{
-}
-
-#endif
+#endif /* !CONFIG_SND_JACK || !CONFIG_SND_JACK_INPUT_DEV */
#endif
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index b0be09279943..af1fb37c6b26 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -1093,6 +1093,8 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate);
unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit);
unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
unsigned int rates_b);
+unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min,
+ unsigned int rate_max);
/**
* snd_pcm_set_runtime_buffer - Set the PCM runtime buffer
diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h
index 5b68e3f5aa85..b897b9d63161 100644
--- a/include/sound/soc-topology.h
+++ b/include/sound/soc-topology.h
@@ -56,12 +56,6 @@ struct snd_soc_dobj_widget {
unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */
};
-/* dynamic PCM DAI object */
-struct snd_soc_dobj_pcm_dai {
- struct snd_soc_tplg_pcm_dai *pd;
- unsigned int count;
-};
-
/* generic dynamic object - all dynamic objects belong to this struct */
struct snd_soc_dobj {
enum snd_soc_dobj_type type;
@@ -71,7 +65,6 @@ struct snd_soc_dobj {
union {
struct snd_soc_dobj_control control;
struct snd_soc_dobj_widget widget;
- struct snd_soc_dobj_pcm_dai pcm_dai;
};
void *private; /* core does not touch this */
};
@@ -126,10 +119,16 @@ struct snd_soc_tplg_ops {
int (*widget_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);
- /* FE - used for any driver specific init */
- int (*pcm_dai_load)(struct snd_soc_component *,
- struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe);
- int (*pcm_dai_unload)(struct snd_soc_component *,
+ /* FE DAI - used for any driver specific init */
+ int (*dai_load)(struct snd_soc_component *,
+ struct snd_soc_dai_driver *dai_drv);
+ int (*dai_unload)(struct snd_soc_component *,
+ struct snd_soc_dobj *);
+
+ /* DAI link - used for any driver specific init */
+ int (*link_load)(struct snd_soc_component *,
+ struct snd_soc_dai_link *link);
+ int (*link_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);
/* callback to handle vendor bespoke data */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 7afb72ceac56..02b4a215fd75 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -27,7 +27,6 @@
#include <sound/compress_driver.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
-#include <sound/soc-topology.h>
/*
* Convenience kcontrol builders
@@ -404,6 +403,7 @@ struct snd_soc_jack_zone;
struct snd_soc_jack_pin;
#include <sound/soc-dapm.h>
#include <sound/soc-dpcm.h>
+#include <sound/soc-topology.h>
struct snd_soc_jack_gpio;
diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h
index 5a5fa4956ebd..7b7659a79ac4 100644
--- a/include/uapi/sound/asequencer.h
+++ b/include/uapi/sound/asequencer.h
@@ -25,7 +25,7 @@
#include <sound/asound.h>
/** version of the sequencer */
-#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1)
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2)
/**
* definition of sequencer event types
@@ -357,7 +357,9 @@ struct snd_seq_client_info {
unsigned char event_filter[32]; /* event filter bitmap */
int num_ports; /* RO: number of ports */
int event_lost; /* number of lost events */
- char reserved[64]; /* for future use */
+ int card; /* RO: card number[kernel] */
+ int pid; /* RO: pid[user] */
+ char reserved[56]; /* for future use */
};
@@ -594,14 +596,8 @@ struct snd_seq_query_subs {
#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct snd_seq_queue_status)
#define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO _IOWR('S', 0x41, struct snd_seq_queue_tempo)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO _IOW ('S', 0x42, struct snd_seq_queue_tempo)
-#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER _IOWR('S', 0x43, struct snd_seq_queue_owner)
-#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER _IOW ('S', 0x44, struct snd_seq_queue_owner)
#define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER _IOWR('S', 0x45, struct snd_seq_queue_timer)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER _IOW ('S', 0x46, struct snd_seq_queue_timer)
-/* XXX
-#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct snd_seq_queue_sync)
-#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct snd_seq_queue_sync)
-*/
#define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT _IOWR('S', 0x49, struct snd_seq_queue_client)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT _IOW ('S', 0x4a, struct snd_seq_queue_client)
#define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL _IOWR('S', 0x4b, struct snd_seq_client_pool)
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index a82108e5d1c0..67bf49d8c944 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -23,7 +23,11 @@
#ifndef _UAPI__SOUND_ASOUND_H
#define _UAPI__SOUND_ASOUND_H
+#if defined(__KERNEL__) || defined(__linux__)
#include <linux/types.h>
+#else
+#include <sys/ioctl.h>
+#endif
#ifndef __KERNEL__
#include <stdlib.h>
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index a2a1e24becc6..6d12ca9bcb80 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -24,12 +24,15 @@ config SND_RAWMIDI
config SND_COMPRESS_OFFLOAD
tristate
-# To be effective this also requires INPUT - users should say:
-# select SND_JACK if INPUT=y || INPUT=SND
-# to avoid having to force INPUT on.
config SND_JACK
bool
+# enable input device support in jack layer
+config SND_JACK_INPUT_DEV
+ bool
+ depends on SND_JACK
+ default y if INPUT=y || INPUT=SND
+
config SND_SEQUENCER
tristate "Sequencer support"
select SND_TIMER
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 7fac3cae8abd..a9933c07a6bf 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -69,11 +69,14 @@ struct snd_compr_file {
/*
* a note on stream states used:
- * we use follwing states in the compressed core
+ * we use following states in the compressed core
* SNDRV_PCM_STATE_OPEN: When stream has been opened.
* SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
- * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
+ * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
* state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
+ * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
+ * playback only). User after setting up stream writes the data buffer
+ * before starting the stream.
* SNDRV_PCM_STATE_RUNNING: When stream has been started and is
* decoding/encoding and rendering/capturing data.
* SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
@@ -286,6 +289,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
mutex_lock(&stream->device->lock);
/* write is allowed when stream is running or has been steup */
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
+ stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
mutex_unlock(&stream->device->lock);
return -EBADFD;
@@ -700,7 +704,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
/*
* We are called with lock held. So drop the lock while we wait for
- * drain complete notfication from the driver
+ * drain complete notification from the driver
*
* It is expected that driver will notify the drain completion and then
* stream will be moved to SETUP state, even if draining resulted in an
@@ -755,7 +759,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)
if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
return -EPERM;
- /* you can signal next track isf this is intended to be a gapless stream
+ /* you can signal next track if this is intended to be a gapless stream
* and current track metadata is set
*/
if (stream->metadata_set == false)
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 0608f216f359..1fa70766ffab 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -196,7 +196,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
kctl = snd_ctl_find_id(card, id);
if (! kctl) {
up_read(&card->controls_rwsem);
- return -ENXIO;
+ return -ENOENT;
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 7237acbdcbbc..f652e90efd7e 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -32,6 +32,7 @@ struct snd_jack_kctl {
unsigned int mask_bits; /* only masked status bits are reported via kctl */
};
+#ifdef CONFIG_SND_JACK_INPUT_DEV
static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_HEADPHONE_INSERT,
SW_MICROPHONE_INSERT,
@@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_VIDEOOUT_INSERT,
SW_LINEIN_INSERT,
};
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
static int snd_jack_dev_disconnect(struct snd_device *device)
{
+#ifdef CONFIG_SND_JACK_INPUT_DEV
struct snd_jack *jack = device->device_data;
if (!jack->input_dev)
@@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
else
input_free_device(jack->input_dev);
jack->input_dev = NULL;
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
return 0;
}
@@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device)
return 0;
}
+#ifdef CONFIG_SND_JACK_INPUT_DEV
static int snd_jack_dev_register(struct snd_device *device)
{
struct snd_jack *jack = device->device_data;
@@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device)
return err;
}
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
{
@@ -209,11 +215,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack *jack;
struct snd_jack_kctl *jack_kctl = NULL;
int err;
- int i;
static struct snd_device_ops ops = {
.dev_free = snd_jack_dev_free,
+#ifdef CONFIG_SND_JACK_INPUT_DEV
.dev_register = snd_jack_dev_register,
.dev_disconnect = snd_jack_dev_disconnect,
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
};
if (initial_kctl) {
@@ -230,6 +237,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
/* don't creat input device for phantom jack */
if (!phantom_jack) {
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+ int i;
+
jack->input_dev = input_allocate_device();
if (jack->input_dev == NULL) {
err = -ENOMEM;
@@ -245,6 +255,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
@@ -262,13 +273,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
return 0;
fail_input:
+#ifdef CONFIG_SND_JACK_INPUT_DEV
input_free_device(jack->input_dev);
+#endif
kfree(jack->id);
kfree(jack);
return err;
}
EXPORT_SYMBOL(snd_jack_new);
+#ifdef CONFIG_SND_JACK_INPUT_DEV
/**
* snd_jack_set_parent - Set the parent device for a jack
*
@@ -326,10 +340,10 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
jack->type |= type;
jack->key[key] = keytype;
-
return 0;
}
EXPORT_SYMBOL(snd_jack_set_key);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
/**
* snd_jack_report - Report the current status of a jack
@@ -340,7 +354,9 @@ EXPORT_SYMBOL(snd_jack_set_key);
void snd_jack_report(struct snd_jack *jack, int status)
{
struct snd_jack_kctl *jack_kctl;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
int i;
+#endif
if (!jack)
return;
@@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
snd_kctl_jack_report(jack->card, jack_kctl->kctl,
status & jack_kctl->mask_bits);
+#ifdef CONFIG_SND_JACK_INPUT_DEV
if (!jack->input_dev)
return;
@@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status)
}
input_sync(jack->input_dev);
-
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
}
EXPORT_SYMBOL(snd_jack_report);
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 6b5a811e01a5..3a9b66c6e09c 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -322,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
pcm_err(substream->pcm,
- "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size,
runtime->period_size);
}
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index ebe8444de6c6..53dc37357bca 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -565,3 +565,33 @@ unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
return rates_a & rates_b;
}
EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect);
+
+/**
+ * snd_pcm_rate_range_to_bits - converts rate range to SNDRV_PCM_RATE_xxx bit
+ * @rate_min: the minimum sample rate
+ * @rate_max: the maximum sample rate
+ *
+ * This function has an implicit assumption: the rates in the given range have
+ * only the pre-defined rates like 44100 or 16000.
+ *
+ * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate range,
+ * or SNDRV_PCM_RATE_KNOT for an unknown range.
+ */
+unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min,
+ unsigned int rate_max)
+{
+ unsigned int rates = 0;
+ int i;
+
+ for (i = 0; i < snd_pcm_known_rates.count; i++) {
+ if (snd_pcm_known_rates.list[i] >= rate_min
+ && snd_pcm_known_rates.list[i] <= rate_max)
+ rates |= 1 << i;
+ }
+
+ if (!rates)
+ rates = SNDRV_PCM_RATE_KNOT;
+
+ return rates;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_rate_range_to_bits);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 58e79e02f217..d6d9419d8bac 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -364,6 +364,7 @@ static int snd_seq_open(struct inode *inode, struct file *file)
/* fill client data */
user->file = file;
sprintf(client->name, "Client-%d", c);
+ client->data.user.owner = get_pid(task_pid(current));
/* make others aware this new client */
snd_seq_system_client_ev_client_start(c);
@@ -380,6 +381,7 @@ static int snd_seq_release(struct inode *inode, struct file *file)
seq_free_client(client);
if (client->data.user.fifo)
snd_seq_fifo_delete(&client->data.user.fifo);
+ put_pid(client->data.user.owner);
kfree(client);
}
@@ -1197,6 +1199,17 @@ static void get_client_info(struct snd_seq_client *cptr,
info->event_lost = cptr->event_lost;
memcpy(info->event_filter, cptr->event_filter, 32);
info->num_ports = cptr->num_ports;
+
+ if (cptr->type == USER_CLIENT)
+ info->pid = pid_vnr(cptr->data.user.owner);
+ else
+ info->pid = -1;
+
+ if (cptr->type == KERNEL_CLIENT)
+ info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1;
+ else
+ info->card = -1;
+
memset(info->reserved, 0, sizeof(info->reserved));
}
@@ -2271,6 +2284,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
client->accept_input = 1;
client->accept_output = 1;
+ client->data.kernel.card = card;
va_start(args, name_fmt);
vsnprintf(client->name, sizeof(client->name), name_fmt, args);
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index 20f0a725ec7d..c6614254ef8a 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -33,6 +33,7 @@
struct snd_seq_user_client {
struct file *file; /* file struct of client */
/* ... */
+ struct pid *owner;
/* fifo */
struct snd_seq_fifo *fifo; /* queue for incoming events */
@@ -41,6 +42,7 @@ struct snd_seq_user_client {
struct snd_seq_kernel_client {
/* ... */
+ struct snd_card *card;
};
diff --git a/sound/core/timer.c b/sound/core/timer.c
index dca817fc7894..aa1b15c155d1 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti,
return 0;
}
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
-
/*
* close a timer instance
*/
@@ -318,25 +316,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
if (snd_BUG_ON(!timeri))
return -ENXIO;
+ mutex_lock(&register_mutex);
+ list_del(&timeri->open_list);
+
/* force to stop the timer */
snd_timer_stop(timeri);
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- /* wait, until the active callback is finished */
- spin_lock_irq(&slave_active_lock);
- while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
- spin_unlock_irq(&slave_active_lock);
- udelay(10);
- spin_lock_irq(&slave_active_lock);
- }
- spin_unlock_irq(&slave_active_lock);
- mutex_lock(&register_mutex);
- list_del(&timeri->open_list);
- mutex_unlock(&register_mutex);
- } else {
- timer = timeri->timer;
- if (snd_BUG_ON(!timer))
- goto out;
+ timer = timeri->timer;
+ if (timer) {
/* wait, until the active callback is finished */
spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -345,11 +332,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_lock_irq(&timer->lock);
}
spin_unlock_irq(&timer->lock);
- mutex_lock(&register_mutex);
- list_del(&timeri->open_list);
- if (list_empty(&timer->open_list_head) &&
- timer->hw.close)
- timer->hw.close(timer);
+
/* remove slave links */
spin_lock_irq(&slave_active_lock);
spin_lock(&timer->lock);
@@ -363,18 +346,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
}
spin_unlock(&timer->lock);
spin_unlock_irq(&slave_active_lock);
- /* release a card refcount for safe disconnection */
- if (timer->card)
- put_device(&timer->card->card_dev);
- mutex_unlock(&register_mutex);
+
+ /* slave doesn't need to release timer resources below */
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ timer = NULL;
}
- out:
+
if (timeri->private_free)
timeri->private_free(timeri);
kfree(timeri->owner);
kfree(timeri);
- if (timer)
+
+ if (timer) {
+ if (list_empty(&timer->open_list_head) && timer->hw.close)
+ timer->hw.close(timer);
+ /* release a card refcount for safe disconnection */
+ if (timer->card)
+ put_device(&timer->card->card_dev);
module_put(timer->module);
+ }
+
+ mutex_unlock(&register_mutex);
return 0;
}
@@ -395,7 +387,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
{
struct snd_timer *timer;
- unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ts;
struct timespec tstamp;
@@ -419,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
return;
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
return;
- spin_lock_irqsave(&timer->lock, flags);
list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
ts->ccallback(ts, event + 100, &tstamp, resolution);
- spin_unlock_irqrestore(&timer->lock, flags);
}
-static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
- unsigned long sticks)
+/* start/continue a master timer */
+static int snd_timer_start1(struct snd_timer_instance *timeri,
+ bool start, unsigned long ticks)
{
+ struct snd_timer *timer;
+ int result;
+ unsigned long flags;
+
+ timer = timeri->timer;
+ if (!timer)
+ return -EINVAL;
+
+ spin_lock_irqsave(&timer->lock, flags);
+ if (timer->card && timer->card->shutdown) {
+ result = -ENODEV;
+ goto unlock;
+ }
+ if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START)) {
+ result = -EBUSY;
+ goto unlock;
+ }
+
+ if (start)
+ timeri->ticks = timeri->cticks = ticks;
+ else if (!timeri->cticks)
+ timeri->cticks = 1;
+ timeri->pticks = 0;
+
list_move_tail(&timeri->active_list, &timer->active_list_head);
if (timer->running) {
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
goto __start_now;
timer->flags |= SNDRV_TIMER_FLG_RESCHED;
timeri->flags |= SNDRV_TIMER_IFLG_START;
- return 1; /* delayed start */
+ result = 1; /* delayed start */
} else {
- timer->sticks = sticks;
+ if (start)
+ timer->sticks = ticks;
timer->hw.start(timer);
__start_now:
timer->running++;
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
- return 0;
+ result = 0;
}
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
+ spin_unlock_irqrestore(&timer->lock, flags);
+ return result;
}
-static int snd_timer_start_slave(struct snd_timer_instance *timeri)
+/* start/continue a slave timer */
+static int snd_timer_start_slave(struct snd_timer_instance *timeri,
+ bool start)
{
unsigned long flags;
@@ -460,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
spin_lock(&timeri->timer->lock);
list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head);
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
spin_unlock(&timeri->timer->lock);
}
spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */
}
-/*
- * start the timer instance
- */
-int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+/* stop/pause a master timer */
+static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
{
struct snd_timer *timer;
- int result = -EINVAL;
+ int result = 0;
unsigned long flags;
- if (timeri == NULL || ticks < 1)
- return -EINVAL;
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- result = snd_timer_start_slave(timeri);
- if (result >= 0)
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
- }
- timer = timeri->timer;
- if (timer == NULL)
- return -EINVAL;
- if (timer->card && timer->card->shutdown)
- return -ENODEV;
- spin_lock_irqsave(&timer->lock, flags);
- if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
- SNDRV_TIMER_IFLG_START)) {
- result = -EBUSY;
- goto unlock;
- }
- timeri->ticks = timeri->cticks = ticks;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, ticks);
- unlock:
- spin_unlock_irqrestore(&timer->lock, flags);
- if (result >= 0)
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
-}
-
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
-{
- struct snd_timer *timer;
- unsigned long flags;
-
- if (snd_BUG_ON(!timeri))
- return -ENXIO;
-
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- spin_lock_irqsave(&slave_active_lock, flags);
- if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
- spin_unlock_irqrestore(&slave_active_lock, flags);
- return -EBUSY;
- }
- if (timeri->timer)
- spin_lock(&timeri->timer->lock);
- timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- list_del_init(&timeri->ack_list);
- list_del_init(&timeri->active_list);
- if (timeri->timer)
- spin_unlock(&timeri->timer->lock);
- spin_unlock_irqrestore(&slave_active_lock, flags);
- goto __end;
- }
timer = timeri->timer;
if (!timer)
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
SNDRV_TIMER_IFLG_START))) {
- spin_unlock_irqrestore(&timer->lock, flags);
- return -EBUSY;
+ result = -EBUSY;
+ goto unlock;
}
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
- if (timer->card && timer->card->shutdown) {
- spin_unlock_irqrestore(&timer->lock, flags);
- return 0;
+ if (timer->card && timer->card->shutdown)
+ goto unlock;
+ if (stop) {
+ timeri->cticks = timeri->ticks;
+ timeri->pticks = 0;
}
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) {
@@ -556,35 +528,60 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
}
}
timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
spin_unlock_irqrestore(&timer->lock, flags);
- __end:
- if (event != SNDRV_TIMER_EVENT_RESOLUTION)
- snd_timer_notify1(timeri, event);
+ return result;
+}
+
+/* stop/pause a slave timer */
+static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&slave_active_lock, flags);
+ if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
+ spin_unlock_irqrestore(&slave_active_lock, flags);
+ return -EBUSY;
+ }
+ timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+ if (timeri->timer) {
+ spin_lock(&timeri->timer->lock);
+ list_del_init(&timeri->ack_list);
+ list_del_init(&timeri->active_list);
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ spin_unlock(&timeri->timer->lock);
+ }
+ spin_unlock_irqrestore(&slave_active_lock, flags);
return 0;
}
/*
+ * start the timer instance
+ */
+int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+{
+ if (timeri == NULL || ticks < 1)
+ return -EINVAL;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_start_slave(timeri, true);
+ else
+ return snd_timer_start1(timeri, true, ticks);
+}
+
+/*
* stop the timer instance.
*
* do not call this from the timer callback!
*/
int snd_timer_stop(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- unsigned long flags;
- int err;
-
- err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
- if (err < 0)
- return err;
- timer = timeri->timer;
- if (!timer)
- return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- timeri->cticks = timeri->ticks;
- timeri->pticks = 0;
- spin_unlock_irqrestore(&timer->lock, flags);
- return 0;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, true);
+ else
+ return snd_timer_stop1(timeri, true);
}
/*
@@ -592,32 +589,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
*/
int snd_timer_continue(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- int result = -EINVAL;
- unsigned long flags;
-
- if (timeri == NULL)
- return result;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
- return snd_timer_start_slave(timeri);
- timer = timeri->timer;
- if (! timer)
- return -EINVAL;
- if (timer->card && timer->card->shutdown)
- return -ENODEV;
- spin_lock_irqsave(&timer->lock, flags);
- if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
- result = -EBUSY;
- goto unlock;
- }
- if (!timeri->cticks)
- timeri->cticks = 1;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, timer->sticks);
- unlock:
- spin_unlock_irqrestore(&timer->lock, flags);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
- return result;
+ return snd_timer_start_slave(timeri, false);
+ else
+ return snd_timer_start1(timeri, false, 0);
}
/*
@@ -625,7 +600,10 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
*/
int snd_timer_pause(struct snd_timer_instance * timeri)
{
- return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, false);
+ else
+ return snd_timer_stop1(timeri, false);
}
/*
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index 2a008a9ccf85..fd4d18df84d3 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -65,8 +65,6 @@ struct mts64 {
struct snd_card *card;
struct snd_rawmidi *rmidi;
struct pardevice *pardev;
- int pardev_claimed;
-
int open_count;
int current_midi_output_port;
int current_midi_input_port;
@@ -850,30 +848,6 @@ __out:
spin_unlock(&mts->lock);
}
-static int snd_mts64_probe_port(struct parport *p)
-{
- struct pardevice *pardev;
- int res;
-
- pardev = parport_register_device(p, DRIVER_NAME,
- NULL, NULL, NULL,
- 0, NULL);
- if (!pardev)
- return -EIO;
-
- if (parport_claim(pardev)) {
- parport_unregister_device(pardev);
- return -EIO;
- }
-
- res = mts64_probe(p);
-
- parport_release(pardev);
- parport_unregister_device(pardev);
-
- return res;
-}
-
static void snd_mts64_attach(struct parport *p)
{
struct platform_device *device;
@@ -907,10 +881,20 @@ static void snd_mts64_detach(struct parport *p)
/* nothing to do here */
}
+static int snd_mts64_dev_probe(struct pardevice *pardev)
+{
+ if (strcmp(pardev->name, DRIVER_NAME))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver mts64_parport_driver = {
- .name = "mts64",
- .attach = snd_mts64_attach,
- .detach = snd_mts64_detach
+ .name = "mts64",
+ .probe = snd_mts64_dev_probe,
+ .match_port = snd_mts64_attach,
+ .detach = snd_mts64_detach,
+ .devmodel = true,
};
/*********************************************************************
@@ -922,8 +906,7 @@ static void snd_mts64_card_private_free(struct snd_card *card)
struct pardevice *pardev = mts->pardev;
if (pardev) {
- if (mts->pardev_claimed)
- parport_release(pardev);
+ parport_release(pardev);
parport_unregister_device(pardev);
}
@@ -938,6 +921,12 @@ static int snd_mts64_probe(struct platform_device *pdev)
struct snd_card *card = NULL;
struct mts64 *mts = NULL;
int err;
+ struct pardev_cb mts64_cb = {
+ .preempt = NULL,
+ .wakeup = NULL,
+ .irq_func = snd_mts64_interrupt, /* ISR */
+ .flags = PARPORT_DEV_EXCL, /* flags */
+ };
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
@@ -946,8 +935,6 @@ static int snd_mts64_probe(struct platform_device *pdev)
return -ENODEV;
if (!enable[dev])
return -ENOENT;
- if ((err = snd_mts64_probe_port(p)) < 0)
- return err;
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
0, &card);
@@ -960,40 +947,42 @@ static int snd_mts64_probe(struct platform_device *pdev)
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
- pardev = parport_register_device(p, /* port */
- DRIVER_NAME, /* name */
- NULL, /* preempt */
- NULL, /* wakeup */
- snd_mts64_interrupt, /* ISR */
- PARPORT_DEV_EXCL, /* flags */
- (void *)card); /* private */
- if (pardev == NULL) {
+ mts64_cb.private = card; /* private */
+ pardev = parport_register_dev_model(p, /* port */
+ DRIVER_NAME, /* name */
+ &mts64_cb, /* callbacks */
+ pdev->id); /* device number */
+ if (!pardev) {
snd_printd("Cannot register pardevice\n");
err = -EIO;
goto __err;
}
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto free_pardev;
+ }
+
if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
snd_printd("Cannot create main component\n");
- parport_unregister_device(pardev);
- goto __err;
+ goto release_pardev;
}
card->private_data = mts;
card->private_free = snd_mts64_card_private_free;
+
+ err = mts64_probe(p);
+ if (err) {
+ err = -EIO;
+ goto __err;
+ }
if ((err = snd_mts64_rawmidi_create(card)) < 0) {
snd_printd("Creating Rawmidi component failed\n");
goto __err;
}
- /* claim parport */
- if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
- err = -EIO;
- goto __err;
- }
- mts->pardev_claimed = 1;
-
/* init device */
if ((err = mts64_device_init(p)) < 0)
goto __err;
@@ -1009,6 +998,10 @@ static int snd_mts64_probe(struct platform_device *pdev)
snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
return 0;
+release_pardev:
+ parport_release(pardev);
+free_pardev:
+ parport_unregister_device(pardev);
__err:
snd_card_free(card);
return err;
@@ -1024,7 +1017,6 @@ static int snd_mts64_remove(struct platform_device *pdev)
return 0;
}
-
static struct platform_driver snd_mts64_driver = {
.probe = snd_mts64_probe,
.remove = snd_mts64_remove,
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index 464385a480e4..189e3e7028af 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -83,8 +83,6 @@ struct portman {
struct snd_card *card;
struct snd_rawmidi *rmidi;
struct pardevice *pardev;
- int pardev_claimed;
-
int open_count;
int mode[PORTMAN_NUM_INPUT_PORTS];
struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS];
@@ -648,30 +646,6 @@ static void snd_portman_interrupt(void *userdata)
spin_unlock(&pm->reg_lock);
}
-static int snd_portman_probe_port(struct parport *p)
-{
- struct pardevice *pardev;
- int res;
-
- pardev = parport_register_device(p, DRIVER_NAME,
- NULL, NULL, NULL,
- 0, NULL);
- if (!pardev)
- return -EIO;
-
- if (parport_claim(pardev)) {
- parport_unregister_device(pardev);
- return -EIO;
- }
-
- res = portman_probe(p);
-
- parport_release(pardev);
- parport_unregister_device(pardev);
-
- return res ? -EIO : 0;
-}
-
static void snd_portman_attach(struct parport *p)
{
struct platform_device *device;
@@ -705,10 +679,20 @@ static void snd_portman_detach(struct parport *p)
/* nothing to do here */
}
+static int snd_portman_dev_probe(struct pardevice *pardev)
+{
+ if (strcmp(pardev->name, DRIVER_NAME))
+ return -ENODEV;
+
+ return 0;
+}
+
static struct parport_driver portman_parport_driver = {
- .name = "portman2x4",
- .attach = snd_portman_attach,
- .detach = snd_portman_detach
+ .name = "portman2x4",
+ .probe = snd_portman_dev_probe,
+ .match_port = snd_portman_attach,
+ .detach = snd_portman_detach,
+ .devmodel = true,
};
/*********************************************************************
@@ -720,8 +704,7 @@ static void snd_portman_card_private_free(struct snd_card *card)
struct pardevice *pardev = pm->pardev;
if (pardev) {
- if (pm->pardev_claimed)
- parport_release(pardev);
+ parport_release(pardev);
parport_unregister_device(pardev);
}
@@ -736,6 +719,12 @@ static int snd_portman_probe(struct platform_device *pdev)
struct snd_card *card = NULL;
struct portman *pm = NULL;
int err;
+ struct pardev_cb portman_cb = {
+ .preempt = NULL,
+ .wakeup = NULL,
+ .irq_func = snd_portman_interrupt, /* ISR */
+ .flags = PARPORT_DEV_EXCL, /* flags */
+ };
p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
@@ -745,9 +734,6 @@ static int snd_portman_probe(struct platform_device *pdev)
if (!enable[dev])
return -ENOENT;
- if ((err = snd_portman_probe_port(p)) < 0)
- return err;
-
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
0, &card);
if (err < 0) {
@@ -759,40 +745,42 @@ static int snd_portman_probe(struct platform_device *pdev)
sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq);
- pardev = parport_register_device(p, /* port */
- DRIVER_NAME, /* name */
- NULL, /* preempt */
- NULL, /* wakeup */
- snd_portman_interrupt, /* ISR */
- PARPORT_DEV_EXCL, /* flags */
- (void *)card); /* private */
+ portman_cb.private = card; /* private */
+ pardev = parport_register_dev_model(p, /* port */
+ DRIVER_NAME, /* name */
+ &portman_cb, /* callbacks */
+ pdev->id); /* device number */
if (pardev == NULL) {
snd_printd("Cannot register pardevice\n");
err = -EIO;
goto __err;
}
+ /* claim parport */
+ if (parport_claim(pardev)) {
+ snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+ err = -EIO;
+ goto free_pardev;
+ }
+
if ((err = portman_create(card, pardev, &pm)) < 0) {
snd_printd("Cannot create main component\n");
- parport_unregister_device(pardev);
- goto __err;
+ goto release_pardev;
}
card->private_data = pm;
card->private_free = snd_portman_card_private_free;
+
+ err = portman_probe(p);
+ if (err) {
+ err = -EIO;
+ goto __err;
+ }
if ((err = snd_portman_rawmidi_create(card)) < 0) {
snd_printd("Creating Rawmidi component failed\n");
goto __err;
}
- /* claim parport */
- if (parport_claim(pardev)) {
- snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
- err = -EIO;
- goto __err;
- }
- pm->pardev_claimed = 1;
-
/* init device */
if ((err = portman_device_init(pm)) < 0)
goto __err;
@@ -808,6 +796,10 @@ static int snd_portman_probe(struct platform_device *pdev)
snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
return 0;
+release_pardev:
+ parport_release(pardev);
+free_pardev:
+ parport_unregister_device(pardev);
__err:
snd_card_free(card);
return err;
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 091290d1f3ea..3e4e0756e3fe 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -300,6 +300,22 @@ error:
return err;
}
+/*
+ * This driver doesn't update streams in bus reset handler.
+ *
+ * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with
+ * discontinued counter at bus reset. This discontinuity is immediately
+ * detected in packet streaming layer, then it sets XRUN to PCM substream.
+ *
+ * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation.
+ * Then, they can recover the PCM substream by executing ioctl(2) with
+ * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers
+ * restart packet streaming.
+ *
+ * The above processing may be executed before this bus-reset handler is
+ * executed. When this handler updates streams with current isochronous
+ * channels, the streams already have the current ones.
+ */
static void
bebob_update(struct fw_unit *unit)
{
@@ -309,7 +325,6 @@ bebob_update(struct fw_unit *unit)
return;
fcp_bus_reset(bebob->unit);
- snd_bebob_stream_update_duplex(bebob);
if (bebob->deferred_registration) {
if (snd_card_register(bebob->card) < 0) {
@@ -327,10 +342,6 @@ static void bebob_remove(struct fw_unit *unit)
if (bebob == NULL)
return;
- /* Awake bus-reset waiters. */
- if (!completion_done(&bebob->bus_reset))
- complete_all(&bebob->bus_reset);
-
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(bebob->card);
}
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 4d8fcc78e747..b50bb33d9d46 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -88,8 +88,6 @@ struct snd_bebob {
unsigned int midi_input_ports;
unsigned int midi_output_ports;
- /* for bus reset quirk */
- struct completion bus_reset;
bool connected;
struct amdtp_stream *master;
@@ -97,7 +95,7 @@ struct snd_bebob {
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
- atomic_t substreams_counter;
+ unsigned int substreams_counter;
struct snd_bebob_stream_formation
tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
@@ -219,7 +217,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob);
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
-void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 90d95be499b0..868eb0decbec 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&bebob->substreams_counter);
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter++;
err = snd_bebob_stream_start_duplex(bebob, 0);
+ mutex_unlock(&bebob->mutex);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
end:
@@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0)
goto end;
- atomic_inc(&bebob->substreams_counter);
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter++;
err = snd_bebob_stream_start_duplex(bebob, 0);
+ mutex_unlock(&bebob->mutex);
if (err < 0)
snd_bebob_stream_lock_release(bebob);
end:
@@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
- atomic_dec(&bebob->substreams_counter);
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter--;
snd_bebob_stream_stop_duplex(bebob);
+ mutex_unlock(&bebob->mutex);
snd_bebob_stream_lock_release(bebob);
return 0;
@@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_bebob *bebob = substream->rmidi->private_data;
- atomic_dec(&bebob->substreams_counter);
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter--;
snd_bebob_stream_stop_duplex(bebob);
+ mutex_unlock(&bebob->mutex);
snd_bebob_stream_lock_release(bebob);
return 0;
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index ef224d6f5c24..5d7b9343fa85 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->substreams_counter);
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter++;
+ mutex_unlock(&bebob->mutex);
+ }
amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
@@ -237,8 +240,11 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
- atomic_inc(&bebob->substreams_counter);
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter++;
+ mutex_unlock(&bebob->mutex);
+ }
amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
@@ -250,8 +256,11 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&bebob->substreams_counter);
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter--;
+ mutex_unlock(&bebob->mutex);
+ }
snd_bebob_stream_stop_duplex(bebob);
@@ -262,8 +271,11 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_bebob *bebob = substream->private_data;
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
- atomic_dec(&bebob->substreams_counter);
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&bebob->mutex);
+ bebob->substreams_counter--;
+ mutex_unlock(&bebob->mutex);
+ }
snd_bebob_stream_stop_duplex(bebob);
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 5022c9b97ddf..77cbb02bff34 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -549,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
destroy_both_connections(bebob);
goto end;
}
- /* See comments in next function */
- init_completion(&bebob->bus_reset);
+
bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
/*
@@ -588,29 +587,10 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
struct amdtp_stream *master, *slave;
enum cip_flags sync_mode;
unsigned int curr_rate;
- bool updated = false;
int err = 0;
- /*
- * Normal BeBoB firmware has a quirk at bus reset to transmits packets
- * with discontinuous value in dbc field.
- *
- * This 'struct completion' is used to call .update() at first to update
- * connections/streams. Next following codes handle streaming error.
- */
- if (amdtp_streaming_error(&bebob->tx_stream)) {
- if (completion_done(&bebob->bus_reset))
- reinit_completion(&bebob->bus_reset);
-
- updated = (wait_for_completion_interruptible_timeout(
- &bebob->bus_reset,
- msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0);
- }
-
- mutex_lock(&bebob->mutex);
-
/* Need no substreams */
- if (atomic_read(&bebob->substreams_counter) == 0)
+ if (bebob->substreams_counter == 0)
goto end;
err = get_sync_mode(bebob, &sync_mode);
@@ -642,8 +622,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
amdtp_stream_stop(master);
if (amdtp_streaming_error(slave))
amdtp_stream_stop(slave);
- if (!updated &&
- !amdtp_stream_running(master) && !amdtp_stream_running(slave))
+ if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
break_both_connections(bebob);
/* stop streams if rate is different */
@@ -741,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
}
}
end:
- mutex_unlock(&bebob->mutex);
return err;
}
@@ -757,9 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
master = &bebob->tx_stream;
}
- mutex_lock(&bebob->mutex);
-
- if (atomic_read(&bebob->substreams_counter) == 0) {
+ if (bebob->substreams_counter == 0) {
amdtp_stream_pcm_abort(master);
amdtp_stream_stop(master);
@@ -768,32 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
break_both_connections(bebob);
}
-
- mutex_unlock(&bebob->mutex);
-}
-
-void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
-{
- /* vs. XRUN recovery due to discontinuity at bus reset */
- mutex_lock(&bebob->mutex);
-
- if ((cmp_connection_update(&bebob->in_conn) < 0) ||
- (cmp_connection_update(&bebob->out_conn) < 0)) {
- amdtp_stream_pcm_abort(&bebob->rx_stream);
- amdtp_stream_pcm_abort(&bebob->tx_stream);
- amdtp_stream_stop(&bebob->rx_stream);
- amdtp_stream_stop(&bebob->tx_stream);
- break_both_connections(bebob);
- } else {
- amdtp_stream_update(&bebob->rx_stream);
- amdtp_stream_update(&bebob->tx_stream);
- }
-
- /* wake up stream_start_duplex() */
- if (!completion_done(&bebob->bus_reset))
- complete_all(&bebob->bus_reset);
-
- mutex_unlock(&bebob->mutex);
}
/*
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
index 151b09f240f2..a040617505a7 100644
--- a/sound/firewire/dice/dice-midi.c
+++ b/sound/firewire/dice/dice-midi.c
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_am824_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
substrm->number, substrm);
else
- amdtp_am824_midi_trigger(&dice->tx_stream,
+ amdtp_am824_midi_trigger(&dice->tx_stream[0],
substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,10 +69,10 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags);
if (up)
- amdtp_am824_midi_trigger(&dice->rx_stream,
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
substrm->number, substrm);
else
- amdtp_am824_midi_trigger(&dice->rx_stream,
+ amdtp_am824_midi_trigger(&dice->rx_stream[0],
substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags);
@@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice,
int snd_dice_create_midi(struct snd_dice *dice)
{
+ __be32 reg;
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
- unsigned int i, midi_in_ports, midi_out_ports;
+ unsigned int midi_in_ports, midi_out_ports;
int err;
- midi_in_ports = midi_out_ports = 0;
- for (i = 0; i < 3; i++) {
- midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports);
- midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports);
- }
+ /*
+ * Use the number of MIDI conformant data channel at current sampling
+ * transfer frequency.
+ */
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ midi_in_ports = be32_to_cpu(reg);
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ midi_out_ports = be32_to_cpu(reg);
if (midi_in_ports + midi_out_ports == 0)
return 0;
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 9b3431999fc8..4aa0249826fd 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -9,99 +9,46 @@
#include "dice.h"
-static int dice_rate_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_pcm_substream *substream = rule->private;
- struct snd_dice *dice = substream->private_data;
-
- const struct snd_interval *c =
- hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval *r =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval rates = {
- .min = UINT_MAX, .max = 0, .integer = 1
- };
- unsigned int i, rate, mode, *pcm_channels;
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- pcm_channels = dice->tx_channels;
- else
- pcm_channels = dice->rx_channels;
-
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
- rate = snd_dice_rates[i];
- if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
- continue;
-
- if (!snd_interval_test(c, pcm_channels[mode]))
- continue;
-
- rates.min = min(rates.min, rate);
- rates.max = max(rates.max, rate);
- }
-
- return snd_interval_refine(r, &rates);
-}
-
-static int dice_channels_constraint(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_pcm_substream *substream = rule->private;
- struct snd_dice *dice = substream->private_data;
-
- const struct snd_interval *r =
- hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *c =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
- struct snd_interval channels = {
- .min = UINT_MAX, .max = 0, .integer = 1
- };
- unsigned int i, rate, mode, *pcm_channels;
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- pcm_channels = dice->tx_channels;
- else
- pcm_channels = dice->rx_channels;
-
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
- rate = snd_dice_rates[i];
- if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
- continue;
-
- if (!snd_interval_test(r, rate))
- continue;
-
- channels.min = min(channels.min, pcm_channels[mode]);
- channels.max = max(channels.max, pcm_channels[mode]);
- }
-
- return snd_interval_refine(c, &channels);
-}
-
-static void limit_channels_and_rates(struct snd_dice *dice,
- struct snd_pcm_runtime *runtime,
- unsigned int *pcm_channels)
+static int limit_channels_and_rates(struct snd_dice *dice,
+ struct snd_pcm_runtime *runtime,
+ enum amdtp_stream_direction dir,
+ unsigned int index, unsigned int size)
{
struct snd_pcm_hardware *hw = &runtime->hw;
- unsigned int i, rate, mode;
+ struct amdtp_stream *stream;
+ unsigned int rate;
+ __be32 reg;
+ int err;
- hw->channels_min = UINT_MAX;
- hw->channels_max = 0;
+ /*
+ * Retrieve current Multi Bit Linear Audio data channel and limit to
+ * it.
+ */
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ err = snd_dice_transaction_read_tx(dice,
+ size * index + TX_NUMBER_AUDIO,
+ &reg, sizeof(reg));
+ } else {
+ stream = &dice->rx_stream[index];
+ err = snd_dice_transaction_read_rx(dice,
+ size * index + RX_NUMBER_AUDIO,
+ &reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
- rate = snd_dice_rates[i];
- if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
- continue;
- hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->channels_min = hw->channels_max = be32_to_cpu(reg);
- if (pcm_channels[mode] == 0)
- continue;
- hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
- hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
- }
+ /* Retrieve current sampling transfer frequency and limit to it. */
+ err = snd_dice_transaction_get_rate(dice, &rate);
+ if (err < 0)
+ return err;
+ hw->rates = snd_pcm_rate_to_rate_bit(rate);
snd_pcm_limit_hw_rates(runtime);
+
+ return 0;
}
static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
@@ -121,8 +68,10 @@ static int init_hw_info(struct snd_dice *dice,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
+ enum amdtp_stream_direction dir;
struct amdtp_stream *stream;
- unsigned int *pcm_channels;
+ __be32 reg[2];
+ unsigned int count, size;
int err;
hw->info = SNDRV_PCM_INFO_MMAP |
@@ -134,38 +83,38 @@ static int init_hw_info(struct snd_dice *dice,
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw->formats = AM824_IN_PCM_FORMAT_BITS;
- stream = &dice->tx_stream;
- pcm_channels = dice->tx_channels;
+ dir = AMDTP_IN_STREAM;
+ stream = &dice->tx_stream[substream->pcm->device];
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
+ sizeof(reg));
} else {
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
- stream = &dice->rx_stream;
- pcm_channels = dice->rx_channels;
+ dir = AMDTP_OUT_STREAM;
+ stream = &dice->rx_stream[substream->pcm->device];
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
+ sizeof(reg));
}
- limit_channels_and_rates(dice, runtime, pcm_channels);
- limit_period_and_buffer(hw);
-
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
- dice_rate_constraint, substream,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
- goto end;
- err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- dice_channels_constraint, substream,
- SNDRV_PCM_HW_PARAM_RATE, -1);
+ return err;
+
+ count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ if (substream->pcm->device >= count)
+ return -ENXIO;
+
+ size = be32_to_cpu(reg[1]) * 4;
+ err = limit_channels_and_rates(dice, substream->runtime, dir,
+ substream->pcm->device, size);
if (err < 0)
- goto end;
+ return err;
+ limit_period_and_buffer(hw);
- err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
-end:
- return err;
+ return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
}
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
- unsigned int source, rate;
- bool internal;
int err;
err = snd_dice_stream_lock_try(dice);
@@ -176,39 +125,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_locked;
- err = snd_dice_transaction_get_clock_source(dice, &source);
- if (err < 0)
- goto err_locked;
- switch (source) {
- case CLOCK_SOURCE_AES1:
- case CLOCK_SOURCE_AES2:
- case CLOCK_SOURCE_AES3:
- case CLOCK_SOURCE_AES4:
- case CLOCK_SOURCE_AES_ANY:
- case CLOCK_SOURCE_ADAT:
- case CLOCK_SOURCE_TDIF:
- case CLOCK_SOURCE_WC:
- internal = false;
- break;
- default:
- internal = true;
- break;
- }
-
- /*
- * When source of clock is not internal or any PCM streams are running,
- * available sampling rate is limited at current sampling rate.
- */
- if (!internal ||
- amdtp_stream_pcm_running(&dice->tx_stream) ||
- amdtp_stream_pcm_running(&dice->rx_stream)) {
- err = snd_dice_transaction_get_rate(dice, &rate);
- if (err < 0)
- goto err_locked;
- substream->runtime->hw.rate_min = rate;
- substream->runtime->hw.rate_max = rate;
- }
-
snd_pcm_set_sync(substream);
end:
return err;
@@ -230,6 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -243,7 +160,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0;
}
@@ -251,6 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -264,7 +182,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex);
}
- amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
+ amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0;
}
@@ -304,26 +222,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
static int capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err;
mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex);
if (err >= 0)
- amdtp_stream_pcm_prepare(&dice->tx_stream);
+ amdtp_stream_pcm_prepare(stream);
return 0;
}
static int playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err;
mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex);
if (err >= 0)
- amdtp_stream_pcm_prepare(&dice->rx_stream);
+ amdtp_stream_pcm_prepare(stream);
return err;
}
@@ -331,13 +251,14 @@ static int playback_prepare(struct snd_pcm_substream *substream)
static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
+ amdtp_stream_pcm_trigger(stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
- amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
+ amdtp_stream_pcm_trigger(stream, NULL);
break;
default:
return -EINVAL;
@@ -348,13 +269,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
+ amdtp_stream_pcm_trigger(stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
- amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
+ amdtp_stream_pcm_trigger(stream, NULL);
break;
default:
return -EINVAL;
@@ -366,14 +288,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
- return amdtp_stream_pcm_pointer(&dice->tx_stream);
+ return amdtp_stream_pcm_pointer(stream);
}
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_dice *dice = substream->private_data;
+ struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
- return amdtp_stream_pcm_pointer(&dice->rx_stream);
+ return amdtp_stream_pcm_pointer(stream);
}
int snd_dice_create_pcm(struct snd_dice *dice)
@@ -402,29 +326,53 @@ int snd_dice_create_pcm(struct snd_dice *dice)
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
+ __be32 reg;
struct snd_pcm *pcm;
- unsigned int i, capture, playback;
+ unsigned int i, max_capture, max_playback, capture, playback;
int err;
- capture = playback = 0;
- for (i = 0; i < 3; i++) {
- if (dice->tx_channels[i] > 0)
+ /* Check whether PCM substreams are required. */
+ if (dice->force_two_pcms) {
+ max_capture = max_playback = 2;
+ } else {
+ max_capture = max_playback = 0;
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+ }
+
+ for (i = 0; i < MAX_STREAMS; i++) {
+ capture = playback = 0;
+ if (i < max_capture)
capture = 1;
- if (dice->rx_channels[i] > 0)
+ if (i < max_playback)
playback = 1;
- }
+ if (capture == 0 && playback == 0)
+ break;
- err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
- if (err < 0)
- return err;
- pcm->private_data = dice;
- strcpy(pcm->name, dice->card->shortname);
+ err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
+ &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = dice;
+ strcpy(pcm->name, dice->card->shortname);
- if (capture > 0)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ if (capture > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &capture_ops);
- if (playback > 0)
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+ if (playback > 0)
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ &playback_ops);
+ }
return 0;
}
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index a6a39f7ef58d..845d5e5884a4 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -10,6 +10,12 @@
#include "dice.h"
#define CALLBACK_TIMEOUT 200
+#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
+
+struct reg_params {
+ unsigned int count;
+ unsigned int size;
+};
const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
/* mode 0 */
@@ -24,96 +30,126 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
[6] = 192000,
};
-int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
- unsigned int *mode)
+/*
+ * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
+ * to GLOBAL_STATUS. Especially, just after powering on, these are different.
+ */
+static int ensure_phase_lock(struct snd_dice *dice)
{
- int i;
+ __be32 reg, nominal;
+ int err;
+
+ err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
- if (!(dice->clock_caps & BIT(i)))
- continue;
- if (snd_dice_rates[i] != rate)
- continue;
+ if (completion_done(&dice->clock_accepted))
+ reinit_completion(&dice->clock_accepted);
- *mode = (i - 1) / 2;
- return 0;
+ err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (wait_for_completion_timeout(&dice->clock_accepted,
+ msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+ /*
+ * Old versions of Dice firmware transfer no notification when
+ * the same clock status as current one is set. In this case,
+ * just check current clock status.
+ */
+ err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
+ &nominal, sizeof(nominal));
+ if (err < 0)
+ return err;
+ if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
+ return -ETIMEDOUT;
}
- return -EINVAL;
-}
-static void release_resources(struct snd_dice *dice,
- struct fw_iso_resources *resources)
-{
- __be32 channel;
-
- /* Reset channel number */
- channel = cpu_to_be32((u32)-1);
- if (resources == &dice->tx_resources)
- snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, sizeof(channel));
- else
- snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, sizeof(channel));
-
- fw_iso_resources_free(resources);
+ return 0;
}
-static int keep_resources(struct snd_dice *dice,
- struct fw_iso_resources *resources,
- unsigned int max_payload_bytes)
+static int get_register_params(struct snd_dice *dice,
+ struct reg_params *tx_params,
+ struct reg_params *rx_params)
{
- __be32 channel;
+ __be32 reg[2];
int err;
- err = fw_iso_resources_allocate(resources, max_payload_bytes,
- fw_parent_device(dice->unit)->max_speed);
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
if (err < 0)
- goto end;
+ return err;
+ tx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ tx_params->size = be32_to_cpu(reg[1]) * 4;
- /* Set channel number */
- channel = cpu_to_be32(resources->channel);
- if (resources == &dice->tx_resources)
- err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
- &channel, sizeof(channel));
- else
- err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
- &channel, sizeof(channel));
+ err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
if (err < 0)
- release_resources(dice, resources);
-end:
- return err;
+ return err;
+ rx_params->count =
+ min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+ rx_params->size = be32_to_cpu(reg[1]) * 4;
+
+ return 0;
}
-static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static void release_resources(struct snd_dice *dice)
{
- amdtp_stream_pcm_abort(stream);
- amdtp_stream_stop(stream);
+ unsigned int i;
- if (stream == &dice->tx_stream)
- release_resources(dice, &dice->tx_resources);
- else
- release_resources(dice, &dice->rx_resources);
+ for (i = 0; i < MAX_STREAMS; i++) {
+ if (amdtp_stream_running(&dice->tx_stream[i])) {
+ amdtp_stream_pcm_abort(&dice->tx_stream[i]);
+ amdtp_stream_stop(&dice->tx_stream[i]);
+ }
+ if (amdtp_stream_running(&dice->rx_stream[i])) {
+ amdtp_stream_pcm_abort(&dice->rx_stream[i]);
+ amdtp_stream_stop(&dice->rx_stream[i]);
+ }
+
+ fw_iso_resources_free(&dice->tx_resources[i]);
+ fw_iso_resources_free(&dice->rx_resources[i]);
+ }
}
-static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
- unsigned int rate)
+static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ struct reg_params *params)
{
+ __be32 reg;
+ unsigned int i;
+
+ for (i = 0; i < params->count; i++) {
+ reg = cpu_to_be32((u32)-1);
+ if (dir == AMDTP_IN_STREAM) {
+ snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ } else {
+ snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ &reg, sizeof(reg));
+ }
+ }
+}
+
+static int keep_resources(struct snd_dice *dice,
+ enum amdtp_stream_direction dir, unsigned int index,
+ unsigned int rate, unsigned int pcm_chs,
+ unsigned int midi_ports)
+{
+ struct amdtp_stream *stream;
struct fw_iso_resources *resources;
- unsigned int i, mode, pcm_chs, midi_ports;
bool double_pcm_frames;
+ unsigned int i;
int err;
- err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
- if (err < 0)
- goto end;
- if (stream == &dice->tx_stream) {
- resources = &dice->tx_resources;
- pcm_chs = dice->tx_channels[mode];
- midi_ports = dice->tx_midi_ports[mode];
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
} else {
- resources = &dice->rx_resources;
- pcm_chs = dice->rx_channels[mode];
- midi_ports = dice->rx_midi_ports[mode];
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
}
/*
@@ -126,7 +162,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
* For this quirk, blocking mode is required and PCM buffer size should
* be aligned to SYT_INTERVAL.
*/
- double_pcm_frames = mode > 1;
+ double_pcm_frames = rate > 96000;
if (double_pcm_frames) {
rate /= 2;
pcm_chs *= 2;
@@ -135,7 +171,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
double_pcm_frames);
if (err < 0)
- goto end;
+ return err;
if (double_pcm_frames) {
pcm_chs /= 2;
@@ -147,158 +183,201 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
}
}
- err = keep_resources(dice, resources,
- amdtp_stream_get_max_payload(stream));
- if (err < 0) {
- dev_err(&dice->unit->device,
- "fail to keep isochronous resources\n");
- goto end;
- }
-
- err = amdtp_stream_start(stream, resources->channel,
- fw_parent_device(dice->unit)->max_speed);
- if (err < 0)
- release_resources(dice, resources);
-end:
- return err;
+ return fw_iso_resources_allocate(resources,
+ amdtp_stream_get_max_payload(stream),
+ fw_parent_device(dice->unit)->max_speed);
}
-static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode)
+static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int rate, struct reg_params *params)
{
- u32 source;
- int err;
+ __be32 reg[2];
+ unsigned int i, pcm_chs, midi_ports;
+ struct amdtp_stream *streams;
+ struct fw_iso_resources *resources;
+ int err = 0;
- err = snd_dice_transaction_get_clock_source(dice, &source);
- if (err < 0)
- goto end;
+ if (dir == AMDTP_IN_STREAM) {
+ streams = dice->tx_stream;
+ resources = dice->tx_resources;
+ } else {
+ streams = dice->rx_stream;
+ resources = dice->rx_resources;
+ }
+
+ for (i = 0; i < params->count; i++) {
+ if (dir == AMDTP_IN_STREAM) {
+ err = snd_dice_transaction_read_tx(dice,
+ params->size * i + TX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ } else {
+ err = snd_dice_transaction_read_rx(dice,
+ params->size * i + RX_NUMBER_AUDIO,
+ reg, sizeof(reg));
+ }
+ if (err < 0)
+ return err;
+ pcm_chs = be32_to_cpu(reg[0]);
+ midi_ports = be32_to_cpu(reg[1]);
+
+ err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
+ if (err < 0)
+ return err;
+
+ reg[0] = cpu_to_be32(resources[i].channel);
+ if (dir == AMDTP_IN_STREAM) {
+ err = snd_dice_transaction_write_tx(dice,
+ params->size * i + TX_ISOCHRONOUS,
+ reg, sizeof(reg[0]));
+ } else {
+ err = snd_dice_transaction_write_rx(dice,
+ params->size * i + RX_ISOCHRONOUS,
+ reg, sizeof(reg[0]));
+ }
+ if (err < 0)
+ return err;
- switch (source) {
- /* So-called 'SYT Match' modes, sync_to_syt value of packets received */
- case CLOCK_SOURCE_ARX4: /* in 4th stream */
- case CLOCK_SOURCE_ARX3: /* in 3rd stream */
- case CLOCK_SOURCE_ARX2: /* in 2nd stream */
- err = -ENOSYS;
- break;
- case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */
- *sync_mode = 0;
- break;
- default:
- *sync_mode = CIP_SYNC_TO_DEVICE;
- break;
+ err = amdtp_stream_start(&streams[i], resources[i].channel,
+ fw_parent_device(dice->unit)->max_speed);
+ if (err < 0)
+ return err;
}
-end:
+
return err;
}
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
{
- struct amdtp_stream *master, *slave;
unsigned int curr_rate;
- enum cip_flags sync_mode;
- int err = 0;
+ unsigned int i;
+ struct reg_params tx_params, rx_params;
+ bool need_to_start;
+ int err;
if (dice->substreams_counter == 0)
- goto end;
+ return -EIO;
- err = get_sync_mode(dice, &sync_mode);
+ err = get_register_params(dice, &tx_params, &rx_params);
if (err < 0)
- goto end;
- if (sync_mode == CIP_SYNC_TO_DEVICE) {
- master = &dice->tx_stream;
- slave = &dice->rx_stream;
- } else {
- master = &dice->rx_stream;
- slave = &dice->tx_stream;
- }
-
- /* Some packet queueing errors. */
- if (amdtp_streaming_error(master) || amdtp_streaming_error(slave))
- stop_stream(dice, master);
+ return err;
- /* Stop stream if rate is different. */
err = snd_dice_transaction_get_rate(dice, &curr_rate);
if (err < 0) {
dev_err(&dice->unit->device,
"fail to get sampling rate\n");
- goto end;
+ return err;
}
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate)
- stop_stream(dice, master);
+ return -EINVAL;
+
+ /* Judge to need to restart streams. */
+ for (i = 0; i < MAX_STREAMS; i++) {
+ if (i < tx_params.count) {
+ if (amdtp_streaming_error(&dice->tx_stream[i]) ||
+ !amdtp_stream_running(&dice->tx_stream[i]))
+ break;
+ }
+ if (i < rx_params.count) {
+ if (amdtp_streaming_error(&dice->rx_stream[i]) ||
+ !amdtp_stream_running(&dice->rx_stream[i]))
+ break;
+ }
+ }
+ need_to_start = (i < MAX_STREAMS);
- if (!amdtp_stream_running(master)) {
- stop_stream(dice, slave);
+ if (need_to_start) {
+ /* Stop transmission. */
snd_dice_transaction_clear_enable(dice);
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ release_resources(dice);
- amdtp_stream_set_sync(sync_mode, master, slave);
-
- err = snd_dice_transaction_set_rate(dice, rate);
+ err = ensure_phase_lock(dice);
if (err < 0) {
dev_err(&dice->unit->device,
- "fail to set sampling rate\n");
- goto end;
+ "fail to ensure phase lock\n");
+ return err;
}
/* Start both streams. */
- err = start_stream(dice, master, rate);
- if (err < 0) {
- dev_err(&dice->unit->device,
- "fail to start AMDTP master stream\n");
- goto end;
- }
- err = start_stream(dice, slave, rate);
- if (err < 0) {
- dev_err(&dice->unit->device,
- "fail to start AMDTP slave stream\n");
- stop_stream(dice, master);
- goto end;
- }
+ err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
+ if (err < 0)
+ goto error;
+ err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
+ if (err < 0)
+ goto error;
+
err = snd_dice_transaction_set_enable(dice);
if (err < 0) {
dev_err(&dice->unit->device,
"fail to enable interface\n");
- stop_stream(dice, master);
- stop_stream(dice, slave);
- goto end;
+ goto error;
}
- /* Wait first callbacks */
- if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) ||
- !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
- snd_dice_transaction_clear_enable(dice);
- stop_stream(dice, master);
- stop_stream(dice, slave);
- err = -ETIMEDOUT;
+ for (i = 0; i < MAX_STREAMS; i++) {
+ if ((i < tx_params.count &&
+ !amdtp_stream_wait_callback(&dice->tx_stream[i],
+ CALLBACK_TIMEOUT)) ||
+ (i < rx_params.count &&
+ !amdtp_stream_wait_callback(&dice->rx_stream[i],
+ CALLBACK_TIMEOUT))) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
}
}
-end:
+
+ return err;
+error:
+ snd_dice_transaction_clear_enable(dice);
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ release_resources(dice);
return err;
}
+/*
+ * MEMO: After this function, there're two states of streams:
+ * - None streams are running.
+ * - All streams are running.
+ */
void snd_dice_stream_stop_duplex(struct snd_dice *dice)
{
+ struct reg_params tx_params, rx_params;
+
if (dice->substreams_counter > 0)
return;
snd_dice_transaction_clear_enable(dice);
- stop_stream(dice, &dice->tx_stream);
- stop_stream(dice, &dice->rx_stream);
+ if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ }
+
+ release_resources(dice);
}
-static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
+ unsigned int index)
{
- int err;
+ struct amdtp_stream *stream;
struct fw_iso_resources *resources;
- enum amdtp_stream_direction dir;
+ int err;
- if (stream == &dice->tx_stream) {
- resources = &dice->tx_resources;
- dir = AMDTP_IN_STREAM;
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
} else {
- resources = &dice->rx_resources;
- dir = AMDTP_OUT_STREAM;
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
}
err = fw_iso_resources_init(resources, dice->unit);
@@ -319,14 +398,20 @@ end:
* This function should be called before starting streams or after stopping
* streams.
*/
-static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static void destroy_stream(struct snd_dice *dice,
+ enum amdtp_stream_direction dir,
+ unsigned int index)
{
+ struct amdtp_stream *stream;
struct fw_iso_resources *resources;
- if (stream == &dice->tx_stream)
- resources = &dice->tx_resources;
- else
- resources = &dice->rx_resources;
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &dice->tx_stream[index];
+ resources = &dice->tx_resources[index];
+ } else {
+ stream = &dice->rx_stream[index];
+ resources = &dice->rx_resources[index];
+ }
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
@@ -334,33 +419,51 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
int snd_dice_stream_init_duplex(struct snd_dice *dice)
{
- int err;
-
- dice->substreams_counter = 0;
+ int i, err;
- err = init_stream(dice, &dice->tx_stream);
- if (err < 0)
- goto end;
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_IN_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ goto end;
+ }
+ }
- err = init_stream(dice, &dice->rx_stream);
- if (err < 0)
- destroy_stream(dice, &dice->tx_stream);
+ for (i = 0; i < MAX_STREAMS; i++) {
+ err = init_stream(dice, AMDTP_OUT_STREAM, i);
+ if (err < 0) {
+ for (; i >= 0; i--)
+ destroy_stream(dice, AMDTP_OUT_STREAM, i);
+ for (i = 0; i < MAX_STREAMS; i++)
+ destroy_stream(dice, AMDTP_IN_STREAM, i);
+ break;
+ }
+ }
end:
return err;
}
void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
{
+ struct reg_params tx_params, rx_params;
+
snd_dice_transaction_clear_enable(dice);
- destroy_stream(dice, &dice->tx_stream);
- destroy_stream(dice, &dice->rx_stream);
+ if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ }
+
+ release_resources(dice);
dice->substreams_counter = 0;
}
void snd_dice_stream_update_duplex(struct snd_dice *dice)
{
+ struct reg_params tx_params, rx_params;
+
/*
* On a bus reset, the DICE firmware disables streaming and then goes
* off contemplating its own navel for hundreds of milliseconds before
@@ -371,11 +474,10 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
*/
dice->global_enabled = false;
- stop_stream(dice, &dice->rx_stream);
- stop_stream(dice, &dice->tx_stream);
-
- fw_iso_resources_update(&dice->rx_resources);
- fw_iso_resources_update(&dice->tx_resources);
+ if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+ stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+ stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+ }
}
static void dice_lock_changed(struct snd_dice *dice)
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
index a4ff4e0bc0af..0f0350320ae8 100644
--- a/sound/firewire/dice/dice-transaction.c
+++ b/sound/firewire/dice/dice-transaction.c
@@ -9,8 +9,6 @@
#include "dice.h"
-#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
-
static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
u64 offset)
{
@@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
info, 4);
}
-static int set_clock_info(struct snd_dice *dice,
- unsigned int rate, unsigned int source)
-{
- unsigned int i;
- __be32 info;
- u32 mask;
- u32 clock;
- int err;
-
- err = get_clock_info(dice, &info);
- if (err < 0)
- return err;
-
- clock = be32_to_cpu(info);
- if (source != UINT_MAX) {
- mask = CLOCK_SOURCE_MASK;
- clock &= ~mask;
- clock |= source;
- }
- if (rate != UINT_MAX) {
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
- if (snd_dice_rates[i] == rate)
- break;
- }
- if (i == ARRAY_SIZE(snd_dice_rates))
- return -EINVAL;
-
- mask = CLOCK_RATE_MASK;
- clock &= ~mask;
- clock |= i << CLOCK_RATE_SHIFT;
- }
- info = cpu_to_be32(clock);
-
- if (completion_done(&dice->clock_accepted))
- reinit_completion(&dice->clock_accepted);
-
- err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
- &info, 4);
- if (err < 0)
- return err;
-
- if (wait_for_completion_timeout(&dice->clock_accepted,
- msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
- return -ETIMEDOUT;
-
- return 0;
-}
-
int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
unsigned int *source)
{
@@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
end:
return err;
}
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
-{
- return set_clock_info(dice, rate, UINT_MAX);
-}
int snd_dice_transaction_set_enable(struct snd_dice *dice)
{
@@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
fw_send_response(card, request, RCODE_COMPLETE);
- if (bits & NOTIFY_CLOCK_ACCEPTED)
+ if (bits & NOTIFY_LOCK_CHG)
complete(&dice->clock_accepted);
wake_up(&dice->hwdep_wait);
}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index b91b3739c810..8b64aef31a86 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2");
#define OUI_WEISS 0x001c6a
#define OUI_LOUD 0x000ff2
+#define OUI_FOCUSRITE 0x00130e
+#define OUI_TCELECTRONIC 0x001486
#define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00
@@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2");
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
+/*
+ * Some models support several isochronous channels, while these streams are not
+ * always available. In this case, add the model name to this list.
+ */
+static bool force_two_pcm_support(struct fw_unit *unit)
+{
+ const char *const models[] = {
+ /* TC Electronic models. */
+ "StudioKonnekt48",
+ /* Focusrite models. */
+ "SAFFIRE_PRO_40",
+ "LIQUID_SAFFIRE_56",
+ "SAFFIRE_PRO_40_1",
+ };
+ char model[32];
+ unsigned int i;
+ int err;
+
+ err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model));
+ if (err < 0)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(models); i++) {
+ if (strcmp(models[i], model) == 0)
+ break;
+ }
+
+ return i < ARRAY_SIZE(models);
+}
+
static int check_dice_category(struct fw_unit *unit)
{
struct fw_device *device = fw_parent_device(unit);
@@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit)
break;
}
}
+
+ if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) {
+ if (force_two_pcm_support(unit))
+ return 0;
+ }
+
if (vendor == OUI_WEISS)
category = WEISS_CATEGORY_ID;
else if (vendor == OUI_LOUD)
@@ -57,65 +95,10 @@ static int check_dice_category(struct fw_unit *unit)
return 0;
}
-static int highest_supported_mode_rate(struct snd_dice *dice,
- unsigned int mode, unsigned int *rate)
-{
- unsigned int i, m;
-
- for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
- *rate = snd_dice_rates[i - 1];
- if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
- continue;
- if (mode == m)
- break;
- }
- if (i == 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
-{
- __be32 values[2];
- unsigned int rate;
- int err;
-
- if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
- dice->tx_channels[mode] = 0;
- dice->tx_midi_ports[mode] = 0;
- dice->rx_channels[mode] = 0;
- dice->rx_midi_ports[mode] = 0;
- return 0;
- }
-
- err = snd_dice_transaction_set_rate(dice, rate);
- if (err < 0)
- return err;
-
- err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
- values, sizeof(values));
- if (err < 0)
- return err;
-
- dice->tx_channels[mode] = be32_to_cpu(values[0]);
- dice->tx_midi_ports[mode] = be32_to_cpu(values[1]);
-
- err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
- values, sizeof(values));
- if (err < 0)
- return err;
-
- dice->rx_channels[mode] = be32_to_cpu(values[0]);
- dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
-
- return 0;
-}
-
-static int dice_read_params(struct snd_dice *dice)
+static int check_clock_caps(struct snd_dice *dice)
{
__be32 value;
- int mode, err;
+ int err;
/* some very old firmwares don't tell about their clock support */
if (dice->clock_caps > 0) {
@@ -133,12 +116,6 @@ static int dice_read_params(struct snd_dice *dice)
CLOCK_CAP_SOURCE_INTERNAL;
}
- for (mode = 2; mode >= 0; --mode) {
- err = dice_read_mode_params(dice, mode);
- if (err < 0)
- return err;
- }
-
return 0;
}
@@ -211,11 +188,14 @@ static void do_registration(struct work_struct *work)
if (err < 0)
return;
+ if (force_two_pcm_support(dice->unit))
+ dice->force_two_pcms = true;
+
err = snd_dice_transaction_init(dice);
if (err < 0)
goto error;
- err = dice_read_params(dice);
+ err = check_clock_caps(dice);
if (err < 0)
goto error;
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 3d5ebebe61ea..e6c07857f475 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -39,6 +39,29 @@
#include "../lib.h"
#include "dice-interface.h"
+/*
+ * This module support maximum 2 pairs of tx/rx isochronous streams for
+ * our convinience.
+ *
+ * In documents for ASICs called with a name of 'DICE':
+ * - ASIC for DICE II:
+ * - Maximum 2 tx and 4 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2210/2210-E (so-called 'Dice Mini'):
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD2220/2220-E (so-called 'Dice Jr.')
+ * - 2 tx and 2 rx are supported.
+ * - A packet supports maximum 16 data channels.
+ * - TCD3070-CH (so-called 'Dice III')
+ * - Maximum 2 tx and 2 rx are supported.
+ * - A packet supports maximum 32 data channels.
+ *
+ * For the above, MIDI conformant data channel is just on the first isochronous
+ * stream.
+ */
+#define MAX_STREAMS 2
+
struct snd_dice {
struct snd_card *card;
struct fw_unit *unit;
@@ -56,10 +79,6 @@ struct snd_dice {
unsigned int rsrv_offset;
unsigned int clock_caps;
- unsigned int tx_channels[3];
- unsigned int rx_channels[3];
- unsigned int tx_midi_ports[3];
- unsigned int rx_midi_ports[3];
struct fw_address_handler notification_handler;
int owner_generation;
@@ -71,13 +90,15 @@ struct snd_dice {
wait_queue_head_t hwdep_wait;
/* For streaming */
- struct fw_iso_resources tx_resources;
- struct fw_iso_resources rx_resources;
- struct amdtp_stream tx_stream;
- struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources[MAX_STREAMS];
+ struct fw_iso_resources rx_resources[MAX_STREAMS];
+ struct amdtp_stream tx_stream[MAX_STREAMS];
+ struct amdtp_stream rx_stream[MAX_STREAMS];
bool global_enabled;
struct completion clock_accepted;
unsigned int substreams_counter;
+
+ bool force_two_pcms;
};
enum snd_dice_addr_type {
@@ -158,7 +179,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
unsigned int *source);
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
int snd_dice_transaction_set_enable(struct snd_dice *dice);
void snd_dice_transaction_clear_enable(struct snd_dice *dice);
@@ -169,9 +189,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
#define SND_DICE_RATES_COUNT 7
extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
-int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
- unsigned int rate, unsigned int *mode);
-
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
void snd_dice_stream_stop_duplex(struct snd_dice *dice);
int snd_dice_stream_init_duplex(struct snd_dice *dice);
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index d5b19bc11e59..8f27b67503c8 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit)
struct snd_efw *efw = dev_get_drvdata(&unit->device);
snd_efw_transaction_bus_reset(efw->unit);
+
+ mutex_lock(&efw->mutex);
snd_efw_stream_update_duplex(efw);
+ mutex_unlock(&efw->mutex);
}
static void efw_remove(struct fw_unit *unit)
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 968a40a1beb2..425db8d88235 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
void snd_efw_stream_update_duplex(struct snd_efw *efw)
{
- if ((cmp_connection_update(&efw->out_conn) < 0) ||
- (cmp_connection_update(&efw->in_conn) < 0)) {
- mutex_lock(&efw->mutex);
+ if (cmp_connection_update(&efw->out_conn) < 0 ||
+ cmp_connection_update(&efw->in_conn) < 0) {
stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream);
- mutex_unlock(&efw->mutex);
} else {
amdtp_stream_update(&efw->rx_stream);
amdtp_stream_update(&efw->tx_stream);
diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c
index bb53eb35721b..f897c9831077 100644
--- a/sound/firewire/oxfw/oxfw-scs1x.c
+++ b/sound/firewire/oxfw/oxfw-scs1x.c
@@ -26,11 +26,13 @@ struct fw_scs1x {
u8 output_bytes;
bool output_escaped;
bool output_escape_high_nibble;
- struct tasklet_struct tasklet;
+ struct work_struct work;
wait_queue_head_t idle_wait;
u8 buffer[HSS1394_MAX_PACKET_SIZE];
bool transaction_running;
struct fw_transaction transaction;
+ unsigned int transaction_bytes;
+ bool error;
struct fw_device *fw_dev;
};
@@ -125,11 +127,16 @@ static void scs_write_callback(struct fw_card *card, int rcode,
{
struct fw_scs1x *scs = callback_data;
- if (rcode == RCODE_GENERATION)
- ; /* TODO: retry this packet */
+ if (!rcode_is_permanent_error(rcode)) {
+ /* Don't retry for this data. */
+ if (rcode == RCODE_COMPLETE)
+ scs->transaction_bytes = 0;
+ } else {
+ scs->error = true;
+ }
scs->transaction_running = false;
- tasklet_schedule(&scs->tasklet);
+ schedule_work(&scs->work);
}
static bool is_valid_running_status(u8 status)
@@ -165,9 +172,9 @@ static bool is_invalid_cmd(u8 status)
status == 0xfd;
}
-static void scs_output_tasklet(unsigned long data)
+static void scs_output_work(struct work_struct *work)
{
- struct fw_scs1x *scs = (struct fw_scs1x *)data;
+ struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
struct snd_rawmidi_substream *stream;
unsigned int i;
u8 byte;
@@ -177,12 +184,15 @@ static void scs_output_tasklet(unsigned long data)
return;
stream = ACCESS_ONCE(scs->output);
- if (!stream) {
+ if (!stream || scs->error) {
scs->output_idle = true;
wake_up(&scs->idle_wait);
return;
}
+ if (scs->transaction_bytes > 0)
+ goto retry;
+
i = scs->output_bytes;
for (;;) {
if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
@@ -253,13 +263,16 @@ static void scs_output_tasklet(unsigned long data)
scs->output_bytes = 1;
scs->output_escaped = false;
+ scs->transaction_bytes = i;
+retry:
scs->transaction_running = true;
generation = scs->fw_dev->generation;
smp_rmb(); /* node_id vs. generation */
fw_send_request(scs->fw_dev->card, &scs->transaction,
TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
- scs->buffer, i, scs_write_callback, scs);
+ scs->buffer, scs->transaction_bytes,
+ scs_write_callback, scs);
}
static int midi_capture_open(struct snd_rawmidi_substream *stream)
@@ -309,9 +322,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
scs->output_bytes = 1;
scs->output_escaped = false;
scs->output_idle = false;
+ scs->transaction_bytes = 0;
+ scs->error = false;
ACCESS_ONCE(scs->output) = stream;
- tasklet_schedule(&scs->tasklet);
+ schedule_work(&scs->work);
} else {
ACCESS_ONCE(scs->output) = NULL;
}
@@ -395,7 +410,7 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&midi_playback_ops);
- tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs);
+ INIT_WORK(&scs->work, scs_output_work);
init_waitqueue_head(&scs->idle_wait);
scs->output_idle = true;
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index 7e999c995cdc..3b9bedee2fa4 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -1,5 +1,5 @@
snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
- hdac_regmap.o hdac_controller.o hdac_stream.o array.o
+ hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
snd-hda-core-objs += trace.o
CFLAGS_trace.o := -I$(src)
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c
index e361024eabb6..d1a4d6973330 100644
--- a/sound/hda/hdac_device.c
+++ b/sound/hda/hdac_device.c
@@ -611,6 +611,22 @@ int snd_hdac_power_up_pm(struct hdac_device *codec)
}
EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
+/* like snd_hdac_power_up_pm(), but only increment the pm count when
+ * already powered up. Returns -1 if not powered up, 1 if incremented
+ * or 0 if unchanged. Only used in hdac_regmap.c
+ */
+int snd_hdac_keep_power_up(struct hdac_device *codec)
+{
+ if (!atomic_inc_not_zero(&codec->in_pm)) {
+ int ret = pm_runtime_get_if_in_use(&codec->dev);
+ if (!ret)
+ return -1;
+ if (ret < 0)
+ return 0;
+ }
+ return 1;
+}
+
/**
* snd_hdac_power_down_pm - power down the codec
* @codec: the codec object
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index f6854dbd7d8d..fb96aead8257 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -126,6 +126,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk);
*/
static int pin2port(hda_nid_t pin_nid)
{
+ if (WARN_ON(pin_nid < 5 || pin_nid > 7))
+ return -1;
return pin_nid - 4;
}
@@ -144,10 +146,14 @@ static int pin2port(hda_nid_t pin_nid)
int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate)
{
struct i915_audio_component *acomp = bus->audio_component;
+ int port;
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
return -ENODEV;
- return acomp->ops->sync_audio_rate(acomp->dev, pin2port(nid), rate);
+ port = pin2port(nid);
+ if (port < 0)
+ return -EINVAL;
+ return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
}
EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
@@ -175,11 +181,15 @@ int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
bool *audio_enabled, char *buffer, int max_bytes)
{
struct i915_audio_component *acomp = bus->audio_component;
+ int port;
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
return -ENODEV;
- return acomp->ops->get_eld(acomp->dev, pin2port(nid), audio_enabled,
+ port = pin2port(nid);
+ if (port < 0)
+ return -EINVAL;
+ return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
buffer, max_bytes);
}
EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index eb8f7c30cb09..bdbcd6b75ff6 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -21,13 +21,16 @@
#include <sound/hdaudio.h>
#include <sound/hda_regmap.h>
-#ifdef CONFIG_PM
-#define codec_is_running(codec) \
- (atomic_read(&(codec)->in_pm) || \
- !pm_runtime_suspended(&(codec)->dev))
-#else
-#define codec_is_running(codec) true
-#endif
+static int codec_pm_lock(struct hdac_device *codec)
+{
+ return snd_hdac_keep_power_up(codec);
+}
+
+static void codec_pm_unlock(struct hdac_device *codec, int lock)
+{
+ if (lock == 1)
+ snd_hdac_power_down_pm(codec);
+}
#define get_verb(reg) (((reg) >> 8) & 0xfff)
@@ -238,20 +241,28 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
struct hdac_device *codec = context;
int verb = get_verb(reg);
int err;
+ int pm_lock = 0;
- if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE)
- return -EAGAIN;
+ if (verb != AC_VERB_GET_POWER_STATE) {
+ pm_lock = codec_pm_lock(codec);
+ if (pm_lock < 0)
+ return -EAGAIN;
+ }
reg |= (codec->addr << 28);
- if (is_stereo_amp_verb(reg))
- return hda_reg_read_stereo_amp(codec, reg, val);
- if (verb == AC_VERB_GET_PROC_COEF)
- return hda_reg_read_coef(codec, reg, val);
+ if (is_stereo_amp_verb(reg)) {
+ err = hda_reg_read_stereo_amp(codec, reg, val);
+ goto out;
+ }
+ if (verb == AC_VERB_GET_PROC_COEF) {
+ err = hda_reg_read_coef(codec, reg, val);
+ goto out;
+ }
if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
reg &= ~AC_AMP_FAKE_MUTE;
err = snd_hdac_exec_verb(codec, reg, 0, val);
if (err < 0)
- return err;
+ goto out;
/* special handling for asymmetric reads */
if (verb == AC_VERB_GET_POWER_STATE) {
if (*val & AC_PWRST_ERROR)
@@ -259,7 +270,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
else /* take only the actual state */
*val = (*val >> 4) & 0x0f;
}
- return 0;
+ out:
+ codec_pm_unlock(codec, pm_lock);
+ return err;
}
static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
@@ -267,6 +280,7 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
struct hdac_device *codec = context;
unsigned int verb;
int i, bytes, err;
+ int pm_lock = 0;
if (codec->caps_overwriting)
return 0;
@@ -275,14 +289,21 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
reg |= (codec->addr << 28);
verb = get_verb(reg);
- if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE)
- return codec->lazy_cache ? 0 : -EAGAIN;
+ if (verb != AC_VERB_SET_POWER_STATE) {
+ pm_lock = codec_pm_lock(codec);
+ if (pm_lock < 0)
+ return codec->lazy_cache ? 0 : -EAGAIN;
+ }
- if (is_stereo_amp_verb(reg))
- return hda_reg_write_stereo_amp(codec, reg, val);
+ if (is_stereo_amp_verb(reg)) {
+ err = hda_reg_write_stereo_amp(codec, reg, val);
+ goto out;
+ }
- if (verb == AC_VERB_SET_PROC_COEF)
- return hda_reg_write_coef(codec, reg, val);
+ if (verb == AC_VERB_SET_PROC_COEF) {
+ err = hda_reg_write_coef(codec, reg, val);
+ goto out;
+ }
switch (verb & 0xf00) {
case AC_VERB_SET_AMP_GAIN_MUTE:
@@ -319,10 +340,12 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
err = snd_hdac_exec_verb(codec, reg, 0, NULL);
if (err < 0)
- return err;
+ goto out;
}
- return 0;
+ out:
+ codec_pm_unlock(codec, pm_lock);
+ return err;
}
static const struct regmap_config hda_regmap_cfg = {
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
new file mode 100644
index 000000000000..d7ec86263828
--- /dev/null
+++ b/sound/hda/hdmi_chmap.c
@@ -0,0 +1,791 @@
+/*
+ * HDMI Channel map support helpers
+ */
+
+#include <linux/module.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/hda_chmap.h>
+
+/*
+ * CEA speaker placement:
+ *
+ * FLH FCH FRH
+ * FLW FL FLC FC FRC FR FRW
+ *
+ * LFE
+ * TC
+ *
+ * RL RLC RC RRC RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+ FL = (1 << 0), /* Front Left */
+ FC = (1 << 1), /* Front Center */
+ FR = (1 << 2), /* Front Right */
+ FLC = (1 << 3), /* Front Left Center */
+ FRC = (1 << 4), /* Front Right Center */
+ RL = (1 << 5), /* Rear Left */
+ RC = (1 << 6), /* Rear Center */
+ RR = (1 << 7), /* Rear Right */
+ RLC = (1 << 8), /* Rear Left Center */
+ RRC = (1 << 9), /* Rear Right Center */
+ LFE = (1 << 10), /* Low Frequency Effect */
+ FLW = (1 << 11), /* Front Left Wide */
+ FRW = (1 << 12), /* Front Right Wide */
+ FLH = (1 << 13), /* Front Left High */
+ FCH = (1 << 14), /* Front Center High */
+ FRH = (1 << 15), /* Front Right High */
+ TC = (1 << 16), /* Top Center */
+};
+
+static const char * const cea_speaker_allocation_names[] = {
+ /* 0 */ "FL/FR",
+ /* 1 */ "LFE",
+ /* 2 */ "FC",
+ /* 3 */ "RL/RR",
+ /* 4 */ "RC",
+ /* 5 */ "FLC/FRC",
+ /* 6 */ "RLC/RRC",
+ /* 7 */ "FLW/FRW",
+ /* 8 */ "FLH/FRH",
+ /* 9 */ "TC",
+ /* 10 */ "FCH",
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+ [0] = FL | FR,
+ [1] = LFE,
+ [2] = FC,
+ [3] = RL | RR,
+ [4] = RC,
+ [5] = FLC | FRC,
+ [6] = RLC | RRC,
+ /* the following are not defined in ELD yet */
+ [7] = FLW | FRW,
+ [8] = FLH | FRH,
+ [9] = TC,
+ [10] = FCH,
+};
+
+/*
+ * ALSA sequence is:
+ *
+ * surround40 surround41 surround50 surround51 surround71
+ * ch0 front left = = = =
+ * ch1 front right = = = =
+ * ch2 rear left = = = =
+ * ch3 rear right = = = =
+ * ch4 LFE center center center
+ * ch5 LFE LFE
+ * ch6 side left
+ * ch7 side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+ /* stereo */
+ [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* 2.1 */
+ [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* Dolby Surround */
+ [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+ /* surround40 */
+ [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+ /* 4ch */
+ [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+ /* surround41 */
+ [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
+ /* surround50 */
+ [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+ /* surround51 */
+ [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+ /* 7.1 */
+ [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_channel_allocation().
+ */
+static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
+/* channel: 7 6 5 4 3 2 1 0 */
+{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
+ /* 2.1 */
+{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
+ /* Dolby Surround */
+{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
+ /* surround40 */
+{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
+ /* surround41 */
+{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
+ /* surround50 */
+{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
+ /* surround51 */
+{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
+ /* 6.1 */
+{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
+ /* surround71 */
+{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
+
+{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
+{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
+{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
+{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
+{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
+{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
+{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
+{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
+{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
+{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
+{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
+{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
+{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
+};
+
+static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot, int channel)
+{
+ return snd_hdac_codec_write(codec, pin_nid, 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
+ hda_nid_t pin_nid, int asp_slot)
+{
+ return (snd_hdac_codec_read(codec, pin_nid, 0,
+ AC_VERB_GET_HDMI_CHAN_SLOT,
+ asp_slot) & 0xf0) >> 4;
+}
+
+static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
+{
+ return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
+ AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hdac_device *codec,
+ hda_nid_t cvt_nid, int chs)
+{
+ if (chs != hdmi_get_channel_count(codec, cvt_nid))
+ snd_hdac_codec_write(codec, cvt_nid, 0,
+ AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+ int i, j;
+ struct hdac_cea_channel_speaker_allocation *p;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ p = channel_allocations + i;
+ p->channels = 0;
+ p->spk_mask = 0;
+ for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+ if (p->speakers[j]) {
+ p->channels++;
+ p->spk_mask |= p->speakers[j];
+ }
+ }
+}
+
+static int get_channel_allocation_order(int ca)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channel_allocations[i].ca_index == ca)
+ break;
+ }
+ return i;
+}
+
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+ if (spk_alloc & (1 << i))
+ j += snprintf(buf + j, buflen - j, " %s",
+ cea_speaker_allocation_names[i]);
+ }
+ buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
+
+/*
+ * The transformation takes two steps:
+ *
+ * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ * spk_mask => (channel_allocations[]) => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
+ int spk_alloc, int channels)
+{
+ int i;
+ int ca = 0;
+ int spk_mask = 0;
+ char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+ /*
+ * CA defaults to 0 for basic stereo audio
+ */
+ if (channels <= 2)
+ return 0;
+
+ /*
+ * expand ELD's speaker allocation mask
+ *
+ * ELD tells the speaker mask in a compact(paired) form,
+ * expand ELD's notions to match the ones used by Audio InfoFrame.
+ */
+ for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+ if (spk_alloc & (1 << i))
+ spk_mask |= eld_speaker_allocation_bits[i];
+ }
+
+ /* search for the first working match in the CA table */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+
+ if (!ca) {
+ /*
+ * if there was no match, select the regular ALSA channel
+ * allocation with the matching number of channels
+ */
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if (channels == channel_allocations[i].channels) {
+ ca = channel_allocations[i].ca_index;
+ break;
+ }
+ }
+ }
+
+ snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
+ dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+ ca, channels, buf);
+
+ return ca;
+}
+
+static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ int i;
+ int channel;
+
+ for (i = 0; i < 8; i++) {
+ channel = chmap->ops.pin_get_slot_channel(
+ chmap->hdac, pin_nid, i);
+ dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
+ channel, i);
+ }
+#endif
+}
+
+static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid,
+ bool non_pcm,
+ int ca)
+{
+ struct hdac_cea_channel_speaker_allocation *ch_alloc;
+ int i;
+ int err;
+ int order;
+ int non_pcm_mapping[8];
+
+ order = get_channel_allocation_order(ca);
+ ch_alloc = &channel_allocations[order];
+
+ if (hdmi_channel_mapping[ca][1] == 0) {
+ 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))
+ hdmi_slot++; /* skip zero slots */
+
+ hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+ }
+ /* fill the rest of the slots with ALSA channel 0xf */
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+ if (!ch_alloc->speakers[7 - hdmi_slot])
+ hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
+ }
+
+ if (non_pcm) {
+ for (i = 0; i < ch_alloc->channels; i++)
+ non_pcm_mapping[i] = (i << 4) | i;
+ for (; i < 8; i++)
+ non_pcm_mapping[i] = (0xf << 4) | i;
+ }
+
+ for (i = 0; i < 8; i++) {
+ int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+ int hdmi_slot = slotsetup & 0x0f;
+ int channel = (slotsetup & 0xf0) >> 4;
+
+ err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+ pin_nid, hdmi_slot, channel);
+ if (err) {
+ dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
+ break;
+ }
+ }
+}
+
+struct channel_map_table {
+ unsigned char map; /* ALSA API channel map position */
+ int spk_mask; /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+ { SNDRV_CHMAP_FL, FL },
+ { SNDRV_CHMAP_FR, FR },
+ { SNDRV_CHMAP_RL, RL },
+ { SNDRV_CHMAP_RR, RR },
+ { SNDRV_CHMAP_LFE, LFE },
+ { SNDRV_CHMAP_FC, FC },
+ { SNDRV_CHMAP_RLC, RLC },
+ { SNDRV_CHMAP_RRC, RRC },
+ { SNDRV_CHMAP_RC, RC },
+ { SNDRV_CHMAP_FLC, FLC },
+ { SNDRV_CHMAP_FRC, FRC },
+ { SNDRV_CHMAP_TFL, FLH },
+ { SNDRV_CHMAP_TFR, FRH },
+ { SNDRV_CHMAP_FLW, FLW },
+ { SNDRV_CHMAP_FRW, FRW },
+ { SNDRV_CHMAP_TC, TC },
+ { SNDRV_CHMAP_TFC, FCH },
+ {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+int snd_hdac_chmap_to_spk_mask(unsigned char c)
+{
+ struct channel_map_table *t = map_tables;
+
+ for (; t->map; t++) {
+ if (t->map == c)
+ return t->spk_mask;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(int ordered_ca, unsigned char pos)
+{
+ int mask = snd_hdac_chmap_to_spk_mask(pos);
+ int i;
+
+ if (mask) {
+ for (i = 0; i < 8; i++) {
+ if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+int snd_hdac_spk_to_chmap(int spk)
+{
+ struct channel_map_table *t = map_tables;
+
+ for (; t->map; t++) {
+ if (t->spk_mask == spk)
+ return t->map;
+ }
+ return 0;
+}
+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];
+
+ return snd_hdac_spk_to_chmap(mask);
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+ int i, spks = 0, spk_mask = 0;
+
+ for (i = 0; i < chs; i++) {
+ int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+
+ if (mask) {
+ spk_mask |= mask;
+ spks++;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+ if ((chs == channel_allocations[i].channels ||
+ spks == channel_allocations[i].channels) &&
+ (spk_mask & channel_allocations[i].spk_mask) ==
+ channel_allocations[i].spk_mask)
+ return channel_allocations[i].ca_index;
+ }
+ return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid,
+ int chs, unsigned char *map,
+ int ca)
+{
+ int ordered_ca = get_channel_allocation_order(ca);
+ int alsa_pos, hdmi_slot;
+ int assignments[8] = {[0 ... 7] = 0xf};
+
+ for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+ hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+ if (hdmi_slot < 0)
+ continue; /* unassigned channel */
+
+ assignments[hdmi_slot] = alsa_pos;
+ }
+
+ for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+ int err;
+
+ err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+ pin_nid, hdmi_slot, assignments[hdmi_slot]);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+ int i;
+ int ordered_ca = get_channel_allocation_order(ca);
+
+ for (i = 0; i < 8; i++) {
+ if (i < channel_allocations[ordered_ca].channels)
+ map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
+ else
+ map[i] = 0;
+ }
+}
+
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+ hda_nid_t pin_nid, bool non_pcm, int ca,
+ int channels, unsigned char *map,
+ bool chmap_set)
+{
+ if (!non_pcm && chmap_set) {
+ hdmi_manual_setup_channel_mapping(chmap, pin_nid,
+ channels, map, ca);
+ } else {
+ hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
+ hdmi_setup_fake_chmap(map, ca);
+ }
+
+ hdmi_debug_channel_mapping(chmap, pin_nid);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
+
+int snd_hdac_get_active_channels(int ca)
+{
+ int ordered_ca = get_channel_allocation_order(ca);
+
+ return channel_allocations[ordered_ca].channels;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
+
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
+{
+ return &channel_allocations[get_channel_allocation_order(ca)];
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
+
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+ int channels, bool chmap_set, bool non_pcm, unsigned char *map)
+{
+ int ca;
+
+ if (!non_pcm && chmap_set)
+ ca = hdmi_manual_channel_allocation(channels, map);
+ else
+ ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
+ spk_alloc, channels);
+
+ if (ca < 0)
+ ca = 0;
+
+ return ca;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
+
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = chmap->channels_max;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+ return 0;
+}
+
+static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+ /* If the speaker allocation matches the channel count, it is OK.*/
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
+{
+ int count = 0;
+ int c;
+
+ for (c = 7; c >= 0; c--) {
+ int spk = cap->speakers[c];
+
+ if (!spk)
+ continue;
+
+ chmap[count++] = snd_hdac_spk_to_chmap(spk);
+ }
+
+ WARN_ON(count != channels);
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+ unsigned int __user *dst;
+ int chs, count = 0;
+
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+ return -EFAULT;
+ size -= 8;
+ dst = tlv + 2;
+ for (chs = 2; chs <= chmap->channels_max; chs++) {
+ int i;
+ struct hdac_cea_channel_speaker_allocation *cap;
+
+ cap = channel_allocations;
+ for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+ int chs_bytes = chs * 4;
+ int type = chmap->ops.chmap_cea_alloc_validate_get_type(
+ chmap, cap, chs);
+ unsigned int tlv_chmap[8];
+
+ if (type < 0)
+ continue;
+ if (size < 8)
+ return -ENOMEM;
+ if (put_user(type, dst) ||
+ put_user(chs_bytes, dst + 1))
+ return -EFAULT;
+ dst += 2;
+ size -= 8;
+ count += 8;
+ if (size < chs_bytes)
+ return -ENOMEM;
+ size -= chs_bytes;
+ count += chs_bytes;
+ chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
+ tlv_chmap, chs);
+ if (copy_to_user(dst, tlv_chmap, chs_bytes))
+ return -EFAULT;
+ dst += chs;
+ }
+ }
+ if (put_user(count, tlv + 1))
+ return -EFAULT;
+ return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *chmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned char pcm_chmap[8];
+ int i;
+
+ memset(pcm_chmap, 0, sizeof(pcm_chmap));
+ chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
+
+ for (i = 0; i < sizeof(chmap); i++)
+ ucontrol->value.integer.value[i] = pcm_chmap[i];
+
+ return 0;
+}
+
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct hdac_chmap *hchmap = info->private_data;
+ int pcm_idx = kcontrol->private_value;
+ unsigned int ctl_idx;
+ struct snd_pcm_substream *substream;
+ unsigned char chmap[8], per_pin_chmap[8];
+ int i, err, ca, prepared = 0;
+
+ /* No monitor is connected in dyn_pcm_assign.
+ * It's invalid to setup the chmap
+ */
+ if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
+ return 0;
+
+ ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+ substream = snd_pcm_chmap_substream(info, ctl_idx);
+ if (!substream || !substream->runtime)
+ return 0; /* just for avoiding error from alsactl restore */
+ switch (substream->runtime->status->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_SETUP:
+ break;
+ case SNDRV_PCM_STATE_PREPARED:
+ prepared = 1;
+ break;
+ default:
+ return -EBUSY;
+ }
+ memset(chmap, 0, sizeof(chmap));
+ for (i = 0; i < ARRAY_SIZE(chmap); i++)
+ chmap[i] = ucontrol->value.integer.value[i];
+
+ hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
+ if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
+ return 0;
+ ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+ if (ca < 0)
+ return -EINVAL;
+ if (hchmap->ops.chmap_validate) {
+ err = hchmap->ops.chmap_validate(hchmap, ca,
+ ARRAY_SIZE(chmap), chmap);
+ if (err)
+ return err;
+ }
+
+ hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
+
+ return 0;
+}
+
+static const struct hdac_chmap_ops chmap_ops = {
+ .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
+ .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
+ .pin_get_slot_channel = hdmi_pin_get_slot_channel,
+ .pin_set_slot_channel = hdmi_pin_set_slot_channel,
+ .set_channel_count = hdmi_set_channel_count,
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+ struct hdac_chmap *chmap)
+{
+ chmap->ops = chmap_ops;
+ chmap->hdac = hdac;
+ init_channel_allocations();
+}
+EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
+
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+ struct hdac_chmap *hchmap)
+{
+ struct snd_pcm_chmap *chmap;
+ struct snd_kcontrol *kctl;
+ int err, i;
+
+ err = snd_pcm_add_chmap_ctls(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ NULL, 0, pcm_idx, &chmap);
+ if (err < 0)
+ return err;
+ /* override handlers */
+ chmap->private_data = hchmap;
+ kctl = chmap->kctl;
+ for (i = 0; i < kctl->count; i++)
+ kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ kctl->info = hdmi_chmap_ctl_info;
+ kctl->get = hdmi_chmap_ctl_get;
+ kctl->put = hdmi_chmap_ctl_put;
+ kctl->tlv.c = hdmi_chmap_ctl_tlv;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index 2153d31fb663..4a4705031cb9 100644
--- a/sound/mips/Kconfig
+++ b/sound/mips/Kconfig
@@ -23,17 +23,5 @@ config SND_SGI_HAL2
help
Sound support for the SGI Indy and Indigo2 Workstation.
-
-config SND_AU1X00
- tristate "Au1x00 AC97 Port Driver (DEPRECATED)"
- depends on MIPS_ALCHEMY
- select SND_PCM
- select SND_AC97_CODEC
- help
- ALSA Sound driver for the Au1x00's AC97 port.
-
- Newer drivers for ASoC are available, please do not use
- this driver as it will be removed in the future.
-
endif # SND_MIPS
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 861ec0a574b4..b977c44330d6 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -2,11 +2,9 @@
# Makefile for ALSA
#
-snd-au1x00-objs := au1x00.o
snd-sgi-o2-objs := sgio2audio.o ad1843.o
snd-sgi-hal2-objs := hal2.o
# Toplevel Module Dependency
-obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
deleted file mode 100644
index 1e30e8475431..000000000000
--- a/sound/mips/au1x00.c
+++ /dev/null
@@ -1,734 +0,0 @@
-/*
- * BRIEF MODULE DESCRIPTION
- * Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
- *
- * Copyright 2004 Cooper Street Innovations Inc.
- * Author: Charles Eidsness <charles@cooper-street.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * History:
- *
- * 2004-09-09 Charles Eidsness -- Original verion -- based on
- * sa11xx-uda1341.c ALSA driver and the
- * au1000.c OSS driver.
- * 2004-09-09 Matt Porter -- Added support for ALSA 1.0.6
- *
- */
-
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/ac97_codec.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1000_dma.h>
-
-MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
-MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
-
-#define PLAYBACK 0
-#define CAPTURE 1
-#define AC97_SLOT_3 0x01
-#define AC97_SLOT_4 0x02
-#define AC97_SLOT_6 0x08
-#define AC97_CMD_IRQ 31
-#define READ 0
-#define WRITE 1
-#define READ_WAIT 2
-#define RW_DONE 3
-
-struct au1000_period
-{
- u32 start;
- u32 relative_end; /*realtive to start of buffer*/
- struct au1000_period * next;
-};
-
-/*Au1000 AC97 Port Control Reisters*/
-struct au1000_ac97_reg {
- u32 volatile config;
- u32 volatile status;
- u32 volatile data;
- u32 volatile cmd;
- u32 volatile cntrl;
-};
-
-struct audio_stream {
- struct snd_pcm_substream *substream;
- int dma;
- spinlock_t dma_lock;
- struct au1000_period * buffer;
- unsigned int period_size;
- unsigned int periods;
-};
-
-struct snd_au1000 {
- struct snd_card *card;
- struct au1000_ac97_reg volatile *ac97_ioport;
-
- struct resource *ac97_res_port;
- spinlock_t ac97_lock;
- struct snd_ac97 *ac97;
-
- struct snd_pcm *pcm;
- struct audio_stream *stream[2]; /* playback & capture */
- int dmaid[2]; /* tx(0)/rx(1) DMA ids */
-};
-
-/*--------------------------- Local Functions --------------------------------*/
-static void
-au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots)
-{
- u32 volatile ac97_config;
-
- spin_lock(&au1000->ac97_lock);
- ac97_config = au1000->ac97_ioport->config;
- ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
- ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
- au1000->ac97_ioport->config = ac97_config;
- spin_unlock(&au1000->ac97_lock);
-}
-
-static void
-au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots)
-{
- u32 volatile ac97_config;
-
- spin_lock(&au1000->ac97_lock);
- ac97_config = au1000->ac97_ioport->config;
- ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
- ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
- au1000->ac97_ioport->config = ac97_config;
- spin_unlock(&au1000->ac97_lock);
-}
-
-
-static void
-au1000_release_dma_link(struct audio_stream *stream)
-{
- struct au1000_period * pointer;
- struct au1000_period * pointer_next;
-
- stream->period_size = 0;
- stream->periods = 0;
- pointer = stream->buffer;
- if (! pointer)
- return;
- do {
- pointer_next = pointer->next;
- kfree(pointer);
- pointer = pointer_next;
- } while (pointer != stream->buffer);
- stream->buffer = NULL;
-}
-
-static int
-au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
- unsigned int periods)
-{
- struct snd_pcm_substream *substream = stream->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct au1000_period *pointer;
- unsigned long dma_start;
- int i;
-
- dma_start = virt_to_phys(runtime->dma_area);
-
- if (stream->period_size == period_bytes &&
- stream->periods == periods)
- return 0; /* not changed */
-
- au1000_release_dma_link(stream);
-
- stream->period_size = period_bytes;
- stream->periods = periods;
-
- stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
- if (! stream->buffer)
- return -ENOMEM;
- pointer = stream->buffer;
- for (i = 0; i < periods; i++) {
- pointer->start = (u32)(dma_start + (i * period_bytes));
- pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
- if (i < periods - 1) {
- pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
- if (! pointer->next) {
- au1000_release_dma_link(stream);
- return -ENOMEM;
- }
- pointer = pointer->next;
- }
- }
- pointer->next = stream->buffer;
- return 0;
-}
-
-static void
-au1000_dma_stop(struct audio_stream *stream)
-{
- if (snd_BUG_ON(!stream->buffer))
- return;
- disable_dma(stream->dma);
-}
-
-static void
-au1000_dma_start(struct audio_stream *stream)
-{
- if (snd_BUG_ON(!stream->buffer))
- return;
-
- init_dma(stream->dma);
- if (get_dma_active_buffer(stream->dma) == 0) {
- clear_dma_done0(stream->dma);
- set_dma_addr0(stream->dma, stream->buffer->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- set_dma_addr1(stream->dma, stream->buffer->next->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- } else {
- clear_dma_done1(stream->dma);
- set_dma_addr1(stream->dma, stream->buffer->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- set_dma_addr0(stream->dma, stream->buffer->next->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- }
- enable_dma_buffers(stream->dma);
- start_dma(stream->dma);
-}
-
-static irqreturn_t
-au1000_dma_interrupt(int irq, void *dev_id)
-{
- struct audio_stream *stream = (struct audio_stream *) dev_id;
- struct snd_pcm_substream *substream = stream->substream;
-
- spin_lock(&stream->dma_lock);
- switch (get_dma_buffer_done(stream->dma)) {
- case DMA_D0:
- stream->buffer = stream->buffer->next;
- clear_dma_done0(stream->dma);
- set_dma_addr0(stream->dma, stream->buffer->next->start);
- set_dma_count0(stream->dma, stream->period_size >> 1);
- enable_dma_buffer0(stream->dma);
- break;
- case DMA_D1:
- stream->buffer = stream->buffer->next;
- clear_dma_done1(stream->dma);
- set_dma_addr1(stream->dma, stream->buffer->next->start);
- set_dma_count1(stream->dma, stream->period_size >> 1);
- enable_dma_buffer1(stream->dma);
- break;
- case (DMA_D0 | DMA_D1):
- printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
- au1000_dma_stop(stream);
- au1000_dma_start(stream);
- break;
- case (~DMA_D0 & ~DMA_D1):
- printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
- }
- spin_unlock(&stream->dma_lock);
- snd_pcm_period_elapsed(substream);
- return IRQ_HANDLED;
-}
-
-/*-------------------------- PCM Audio Streams -------------------------------*/
-
-static unsigned int rates[] = {8000, 11025, 16000, 22050};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
- .count = ARRAY_SIZE(rates),
- .list = rates,
- .mask = 0,
-};
-
-static struct snd_pcm_hardware snd_au1000_hw =
-{
- .info = (SNDRV_PCM_INFO_INTERLEAVED | \
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
- SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
- .rate_min = 8000,
- .rate_max = 22050,
- .channels_min = 1,
- .channels_max = 2,
- .buffer_bytes_max = 128*1024,
- .period_bytes_min = 32,
- .period_bytes_max = 16*1024,
- .periods_min = 8,
- .periods_max = 255,
- .fifo_size = 16,
-};
-
-static int
-snd_au1000_playback_open(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[PLAYBACK]->substream = substream;
- au1000->stream[PLAYBACK]->buffer = NULL;
- substream->private_data = au1000->stream[PLAYBACK];
- substream->runtime->hw = snd_au1000_hw;
- return (snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_capture_open(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[CAPTURE]->substream = substream;
- au1000->stream[CAPTURE]->buffer = NULL;
- substream->private_data = au1000->stream[CAPTURE];
- substream->runtime->hw = snd_au1000_hw;
- return (snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_playback_close(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[PLAYBACK]->substream = NULL;
- return 0;
-}
-
-static int
-snd_au1000_capture_close(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
-
- au1000->stream[CAPTURE]->substream = NULL;
- return 0;
-}
-
-static int
-snd_au1000_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct audio_stream *stream = substream->private_data;
- int err;
-
- err = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (err < 0)
- return err;
- return au1000_setup_dma_link(stream,
- params_period_bytes(hw_params),
- params_periods(hw_params));
-}
-
-static int
-snd_au1000_hw_free(struct snd_pcm_substream *substream)
-{
- struct audio_stream *stream = substream->private_data;
- au1000_release_dma_link(stream);
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int
-snd_au1000_playback_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (runtime->channels == 1)
- au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4);
- else
- au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
- snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
- return 0;
-}
-
-static int
-snd_au1000_capture_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_au1000 *au1000 = substream->pcm->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (runtime->channels == 1)
- au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4);
- else
- au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
- snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
- return 0;
-}
-
-static int
-snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct audio_stream *stream = substream->private_data;
- int err = 0;
-
- spin_lock(&stream->dma_lock);
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- au1000_dma_start(stream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- au1000_dma_stop(stream);
- break;
- default:
- err = -EINVAL;
- break;
- }
- spin_unlock(&stream->dma_lock);
- return err;
-}
-
-static snd_pcm_uframes_t
-snd_au1000_pointer(struct snd_pcm_substream *substream)
-{
- struct audio_stream *stream = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- long location;
-
- spin_lock(&stream->dma_lock);
- location = get_dma_residue(stream->dma);
- spin_unlock(&stream->dma_lock);
- location = stream->buffer->relative_end - location;
- if (location == -1)
- location = 0;
- return bytes_to_frames(runtime,location);
-}
-
-static struct snd_pcm_ops snd_card_au1000_playback_ops = {
- .open = snd_au1000_playback_open,
- .close = snd_au1000_playback_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_au1000_hw_params,
- .hw_free = snd_au1000_hw_free,
- .prepare = snd_au1000_playback_prepare,
- .trigger = snd_au1000_trigger,
- .pointer = snd_au1000_pointer,
-};
-
-static struct snd_pcm_ops snd_card_au1000_capture_ops = {
- .open = snd_au1000_capture_open,
- .close = snd_au1000_capture_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_au1000_hw_params,
- .hw_free = snd_au1000_hw_free,
- .prepare = snd_au1000_capture_prepare,
- .trigger = snd_au1000_trigger,
- .pointer = snd_au1000_pointer,
-};
-
-static int
-snd_au1000_pcm_new(struct snd_au1000 *au1000)
-{
- struct snd_pcm *pcm;
- int err;
- unsigned long flags;
-
- if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
- return err;
-
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
- &snd_card_au1000_playback_ops);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
- &snd_card_au1000_capture_ops);
-
- pcm->private_data = au1000;
- pcm->info_flags = 0;
- strcpy(pcm->name, "Au1000 AC97 PCM");
-
- spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
- spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
-
- flags = claim_dma_lock();
- au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0],
- "AC97 TX", au1000_dma_interrupt, 0,
- au1000->stream[PLAYBACK]);
- if (au1000->stream[PLAYBACK]->dma < 0) {
- release_dma_lock(flags);
- return -EBUSY;
- }
- au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1],
- "AC97 RX", au1000_dma_interrupt, 0,
- au1000->stream[CAPTURE]);
- if (au1000->stream[CAPTURE]->dma < 0){
- release_dma_lock(flags);
- return -EBUSY;
- }
- /* enable DMA coherency in read/write DMA channels */
- set_dma_mode(au1000->stream[PLAYBACK]->dma,
- get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
- set_dma_mode(au1000->stream[CAPTURE]->dma,
- get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
- release_dma_lock(flags);
- au1000->pcm = pcm;
- return 0;
-}
-
-
-/*-------------------------- AC97 CODEC Control ------------------------------*/
-
-static unsigned short
-snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
- struct snd_au1000 *au1000 = ac97->private_data;
- u32 volatile cmd;
- u16 volatile data;
- int i;
-
- spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000)
- printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
-
- cmd = (u32) reg & AC97C_INDEX_MASK;
- cmd |= AC97C_READ;
- au1000->ac97_ioport->cmd = cmd;
-
- /* now wait for the data */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000) {
- printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
- spin_unlock(&au1000->ac97_lock);
- return 0;
- }
-
- data = au1000->ac97_ioport->cmd & 0xffff;
- spin_unlock(&au1000->ac97_lock);
-
- return data;
-
-}
-
-
-static void
-snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
- struct snd_au1000 *au1000 = ac97->private_data;
- u32 cmd;
- int i;
-
- spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
- for (i = 0; i < 0x5000; i++)
- if (!(au1000->ac97_ioport->status & AC97C_CP))
- break;
- if (i == 0x5000)
- printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
-
- cmd = (u32) reg & AC97C_INDEX_MASK;
- cmd &= ~AC97C_READ;
- cmd |= ((u32) val << AC97C_WD_BIT);
- au1000->ac97_ioport->cmd = cmd;
- spin_unlock(&au1000->ac97_lock);
-}
-
-/*------------------------------ Setup / Destroy ----------------------------*/
-
-static void snd_au1000_free(struct snd_card *card)
-{
- struct snd_au1000 *au1000 = card->private_data;
-
- if (au1000->stream[PLAYBACK]) {
- if (au1000->stream[PLAYBACK]->dma >= 0)
- free_au1000_dma(au1000->stream[PLAYBACK]->dma);
- kfree(au1000->stream[PLAYBACK]);
- }
-
- if (au1000->stream[CAPTURE]) {
- if (au1000->stream[CAPTURE]->dma >= 0)
- free_au1000_dma(au1000->stream[CAPTURE]->dma);
- kfree(au1000->stream[CAPTURE]);
- }
-
- if (au1000->ac97_res_port) {
- /* put internal AC97 block into reset */
- if (au1000->ac97_ioport) {
- au1000->ac97_ioport->cntrl = AC97C_RS;
- iounmap(au1000->ac97_ioport);
- au1000->ac97_ioport = NULL;
- }
- release_and_free_resource(au1000->ac97_res_port);
- au1000->ac97_res_port = NULL;
- }
-}
-
-static struct snd_ac97_bus_ops ops = {
- .write = snd_au1000_ac97_write,
- .read = snd_au1000_ac97_read,
-};
-
-static int au1000_ac97_probe(struct platform_device *pdev)
-{
- int err;
- void __iomem *io;
- struct resource *r;
- struct snd_card *card;
- struct snd_au1000 *au1000;
- struct snd_ac97_bus *pbus;
- struct snd_ac97_template ac97;
-
- err = snd_card_new(&pdev->dev, -1, "AC97", THIS_MODULE,
- sizeof(struct snd_au1000), &card);
- if (err < 0)
- return err;
-
- au1000 = card->private_data;
- au1000->card = card;
- spin_lock_init(&au1000->ac97_lock);
-
- /* from here on let ALSA call the special freeing function */
- card->private_free = snd_au1000_free;
-
- /* TX DMA ID */
- r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!r) {
- err = -ENODEV;
- snd_printk(KERN_INFO "no TX DMA platform resource!\n");
- goto out;
- }
- au1000->dmaid[0] = r->start;
-
- /* RX DMA ID */
- r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!r) {
- err = -ENODEV;
- snd_printk(KERN_INFO "no RX DMA platform resource!\n");
- goto out;
- }
- au1000->dmaid[1] = r->start;
-
- au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream),
- GFP_KERNEL);
- if (!au1000->stream[PLAYBACK]) {
- err = -ENOMEM;
- goto out;
- }
- au1000->stream[PLAYBACK]->dma = -1;
-
- au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream),
- GFP_KERNEL);
- if (!au1000->stream[CAPTURE]) {
- err = -ENOMEM;
- goto out;
- }
- au1000->stream[CAPTURE]->dma = -1;
-
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- err = -ENODEV;
- goto out;
- }
-
- err = -EBUSY;
- au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
- pdev->name);
- if (!au1000->ac97_res_port) {
- snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
- goto out;
- }
-
- io = ioremap(r->start, resource_size(r));
- if (!io)
- goto out;
-
- au1000->ac97_ioport = (struct au1000_ac97_reg *)io;
-
- /* configure pins for AC'97
- TODO: move to board_setup.c */
- au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
-
- /* Initialise Au1000's AC'97 Control Block */
- au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
- udelay(10);
- au1000->ac97_ioport->cntrl = AC97C_CE;
- udelay(10);
-
- /* Initialise External CODEC -- cold reset */
- au1000->ac97_ioport->config = AC97C_RESET;
- udelay(10);
- au1000->ac97_ioport->config = 0x0;
- mdelay(5);
-
- /* Initialise AC97 middle-layer */
- err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus);
- if (err < 0)
- goto out;
-
- memset(&ac97, 0, sizeof(ac97));
- ac97.private_data = au1000;
- err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97);
- if (err < 0)
- goto out;
-
- err = snd_au1000_pcm_new(au1000);
- if (err < 0)
- goto out;
-
- strcpy(card->driver, "Au1000-AC97");
- strcpy(card->shortname, "AMD Au1000-AC97");
- sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
-
- err = snd_card_register(card);
- if (err < 0)
- goto out;
-
- printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
-
- platform_set_drvdata(pdev, card);
-
- return 0;
-
- out:
- snd_card_free(card);
- return err;
-}
-
-static int au1000_ac97_remove(struct platform_device *pdev)
-{
- return snd_card_free(platform_get_drvdata(pdev));
-}
-
-struct platform_driver au1000_ac97c_driver = {
- .driver = {
- .name = "au1000-ac97c",
- .owner = THIS_MODULE,
- },
- .probe = au1000_ac97_probe,
- .remove = au1000_ac97_remove,
-};
-
-module_platform_driver(au1000_ac97c_driver);
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 8f6594a7d37f..32151d8c6bb8 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -866,7 +866,7 @@ config SND_VIRTUOSO
select SND_OXYGEN_LIB
select SND_PCM
select SND_MPU401_UART
- select SND_JACK if INPUT=y || INPUT=SND
+ select SND_JACK
help
Say Y here to include support for sound cards based on the
Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX,
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index e94cfd5c69f7..bb02c2d48fd5 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -4,7 +4,7 @@ config SND_HDA
tristate
select SND_PCM
select SND_VMASTER
- select SND_JACK if INPUT=y || INPUT=SND
+ select SND_JACK
select SND_HDA_CORE
config SND_HDA_INTEL
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index bc2e08257c2e..ba7fe9b6655c 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <sound/core.h>
#include <asm/unaligned.h>
+#include <sound/hda_chmap.h>
#include "hda_codec.h"
#include "hda_local.h"
@@ -42,20 +43,6 @@ enum cea_edid_versions {
CEA_EDID_VER_RESERVED = 4,
};
-static const char * const cea_speaker_allocation_names[] = {
- /* 0 */ "FL/FR",
- /* 1 */ "LFE",
- /* 2 */ "FC",
- /* 3 */ "RL/RR",
- /* 4 */ "RC",
- /* 5 */ "FLC/FRC",
- /* 6 */ "RLC/RRC",
- /* 7 */ "FLW/FRW",
- /* 8 */ "FLH/FRH",
- /* 9 */ "TC",
- /* 10 */ "FCH",
-};
-
static const char * const eld_connection_type_names[4] = {
"HDMI",
"DisplayPort",
@@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec,
a->channels, buf, buf2);
}
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
-{
- int i, j;
-
- for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
- if (spk_alloc & (1 << i))
- j += snprintf(buf + j, buflen - j, " %s",
- cea_speaker_allocation_names[i]);
- }
- buf[j] = '\0'; /* necessary when j == 0 */
-}
-
void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
{
int i;
@@ -441,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
if (e->spk_alloc) {
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
- snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
codec_dbg(codec, "HDMI: available speakers:%s\n", buf);
}
@@ -516,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
- snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+ snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index e5240cb3749f..2624cfe98884 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -2145,7 +2145,7 @@ static int azx_probe_continue(struct azx *chip)
azx_add_card_list(chip);
snd_hda_set_power_save(&chip->bus, power_save * 1000);
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
- pm_runtime_put_noidle(&pci->dev);
+ pm_runtime_put_autosuspend(&pci->dev);
out_free:
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index c1c855a6c0af..a47e8ae0eb30 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec)
snd_hda_gen_update_outputs(codec);
if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
- spec->gpio_data = spec->gen.hp_jack_present ?
- spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ if (spec->gen.automute_speaker)
+ spec->gpio_data = spec->gen.hp_jack_present ?
+ spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ else
+ spec->gpio_data =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
snd_hda_codec_write(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 6122b8ca872f..56fefbd85782 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -204,8 +204,13 @@ static void cx_auto_reboot_notify(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
- if (codec->core.vendor_id != 0x14f150f2)
+ switch (codec->core.vendor_id) {
+ case 0x14f150f2: /* CX20722 */
+ case 0x14f150f4: /* CX20724 */
+ break;
+ default:
return;
+ }
/* Turn the CX20722 codec into D3 to avoid spurious noises
from the internal speaker during (and after) reboot */
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index bcbc4ee10130..49ee4e55dd16 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -39,6 +39,7 @@
#include <sound/tlv.h>
#include <sound/hdaudio.h>
#include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_jack.h"
@@ -75,6 +76,8 @@ struct hdmi_spec_per_cvt {
struct hdmi_spec_per_pin {
hda_nid_t pin_nid;
+ /* pin idx, different device entries on the same pin use the same idx */
+ int pin_nid_idx;
int num_mux_nids;
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
int mux_idx;
@@ -84,8 +87,8 @@ struct hdmi_spec_per_pin {
struct hdmi_eld sink_eld;
struct mutex lock;
struct delayed_work work;
- struct snd_kcontrol *eld_ctl;
- struct snd_jack *acomp_jack; /* jack via audio component */
+ struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/
+ int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */
int repoll_count;
bool setup; /* the stream has been set up by prepare callback */
int channels; /* current number of channels */
@@ -97,19 +100,11 @@ struct hdmi_spec_per_pin {
#endif
};
-struct cea_channel_speaker_allocation;
-
/* operations used by generic code that can be overridden by patches */
struct hdmi_ops {
int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
unsigned char *buf, int *eld_size);
- /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
- int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
- int asp_slot);
- int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
- int asp_slot, int channel);
-
void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
int ca, int active_channels, int conn_type);
@@ -119,15 +114,12 @@ struct hdmi_ops {
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format);
- /* Helpers for producing the channel map TLVs. These can be overridden
- * for devices that have non-standard mapping requirements. */
- int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap,
- int channels);
- void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap,
- unsigned int *chmap, int channels);
+};
- /* check that the user-given chmap is supported */
- int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
+struct hdmi_pcm {
+ struct hda_pcm *pcm;
+ struct snd_jack *jack;
+ struct snd_kcontrol *eld_ctl;
};
struct hdmi_spec {
@@ -137,14 +129,22 @@ struct hdmi_spec {
int num_pins;
struct snd_array pins; /* struct hdmi_spec_per_pin */
- struct hda_pcm *pcm_rec[16];
- unsigned int channels_max; /* max over all cvts */
+ struct hdmi_pcm pcm_rec[16];
+ struct mutex pcm_lock;
+ /* pcm_bitmap means which pcms have been assigned to pins*/
+ unsigned long pcm_bitmap;
+ int pcm_used; /* counter of pcm_rec[] */
+ /* bitmap shows whether the pcm is opened in user space
+ * bit 0 means the first playback PCM (PCM3);
+ * bit 1 means the second playback PCM, and so on.
+ */
+ unsigned long pcm_in_use;
struct hdmi_eld temp_eld;
struct hdmi_ops ops;
bool dyn_pin_out;
-
+ bool dyn_pcm_assign;
/*
* Non-generic VIA/NVIDIA specific
*/
@@ -154,6 +154,8 @@ struct hdmi_spec {
/* i915/powerwell (Haswell+/Valleyview+) specific */
struct i915_audio_component_audio_ops i915_audio_ops;
bool i915_bound; /* was i915 bound in this driver? */
+
+ struct hdac_chmap chmap;
};
#ifdef CONFIG_SND_HDA_I915
@@ -196,173 +198,6 @@ union audio_infoframe {
};
/*
- * CEA speaker placement:
- *
- * FLH FCH FRH
- * FLW FL FLC FC FRC FR FRW
- *
- * LFE
- * TC
- *
- * RL RLC RC RRC RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
- FL = (1 << 0), /* Front Left */
- FC = (1 << 1), /* Front Center */
- FR = (1 << 2), /* Front Right */
- FLC = (1 << 3), /* Front Left Center */
- FRC = (1 << 4), /* Front Right Center */
- RL = (1 << 5), /* Rear Left */
- RC = (1 << 6), /* Rear Center */
- RR = (1 << 7), /* Rear Right */
- RLC = (1 << 8), /* Rear Left Center */
- RRC = (1 << 9), /* Rear Right Center */
- LFE = (1 << 10), /* Low Frequency Effect */
- FLW = (1 << 11), /* Front Left Wide */
- FRW = (1 << 12), /* Front Right Wide */
- FLH = (1 << 13), /* Front Left High */
- FCH = (1 << 14), /* Front Center High */
- FRH = (1 << 15), /* Front Right High */
- TC = (1 << 16), /* Top Center */
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static int eld_speaker_allocation_bits[] = {
- [0] = FL | FR,
- [1] = LFE,
- [2] = FC,
- [3] = RL | RR,
- [4] = RC,
- [5] = FLC | FRC,
- [6] = RLC | RRC,
- /* the following are not defined in ELD yet */
- [7] = FLW | FRW,
- [8] = FLH | FRH,
- [9] = TC,
- [10] = FCH,
-};
-
-struct cea_channel_speaker_allocation {
- int ca_index;
- int speakers[8];
-
- /* derived values, just for convenience */
- int channels;
- int spk_mask;
-};
-
-/*
- * ALSA sequence is:
- *
- * surround40 surround41 surround50 surround51 surround71
- * ch0 front left = = = =
- * ch1 front right = = = =
- * ch2 rear left = = = =
- * ch3 rear right = = = =
- * ch4 LFE center center center
- * ch5 LFE LFE
- * ch6 side left
- * ch7 side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
- /* stereo */
- [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* 2.1 */
- [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* Dolby Surround */
- [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
- /* surround40 */
- [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
- /* 4ch */
- [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
- /* surround41 */
- [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
- /* surround50 */
- [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
- /* surround51 */
- [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
- /* 7.1 */
- [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/* channel: 7 6 5 4 3 2 1 0 */
-{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
- /* 2.1 */
-{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
- /* Dolby Surround */
-{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
- /* surround40 */
-{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
- /* surround41 */
-{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
- /* surround50 */
-{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
- /* surround51 */
-{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
- /* 6.1 */
-{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
- /* surround71 */
-{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
-
-{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
-{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
-{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
-{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
-{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
-{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
-{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
-{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
-{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
-{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
-{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
-{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
-{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
-{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
-{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
-{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
-{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
-{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
-};
-
-
-/*
* HDMI routines
*/
@@ -370,7 +205,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
#define get_cvt(spec, idx) \
((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx))
-#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx])
+/* obtain hdmi_pcm object assigned to idx */
+#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx])
+/* obtain hda_pcm object assigned to idx */
+#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm)
static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
{
@@ -385,20 +223,52 @@ static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
return -EINVAL;
}
+static int hinfo_to_pcm_index(struct hda_codec *codec,
+ struct hda_pcm_stream *hinfo)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int pcm_idx;
+
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++)
+ if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
+ return pcm_idx;
+
+ codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+ return -EINVAL;
+}
+
static int hinfo_to_pin_index(struct hda_codec *codec,
struct hda_pcm_stream *hinfo)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin;
int pin_idx;
- for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
- if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ per_pin = get_pin(spec, pin_idx);
+ if (per_pin->pcm &&
+ per_pin->pcm->pcm->stream == hinfo)
return pin_idx;
+ }
- codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+ codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo);
return -EINVAL;
}
+static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec,
+ int pcm_idx)
+{
+ int i;
+ struct hdmi_spec_per_pin *per_pin;
+
+ for (i = 0; i < spec->num_pins; i++) {
+ per_pin = get_pin(spec, i);
+ if (per_pin->pcm_idx == pcm_idx)
+ return per_pin;
+ }
+ return NULL;
+}
+
static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid)
{
struct hdmi_spec *spec = codec->spec;
@@ -419,17 +289,22 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin;
struct hdmi_eld *eld;
- int pin_idx;
+ int pcm_idx;
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- pin_idx = kcontrol->private_value;
- per_pin = get_pin(spec, pin_idx);
+ pcm_idx = kcontrol->private_value;
+ mutex_lock(&spec->pcm_lock);
+ per_pin = pcm_idx_to_pin(spec, pcm_idx);
+ if (!per_pin) {
+ /* no pin is bound to the pcm */
+ uinfo->count = 0;
+ mutex_unlock(&spec->pcm_lock);
+ return 0;
+ }
eld = &per_pin->sink_eld;
-
- mutex_lock(&per_pin->lock);
uinfo->count = eld->eld_valid ? eld->eld_size : 0;
- mutex_unlock(&per_pin->lock);
+ mutex_unlock(&spec->pcm_lock);
return 0;
}
@@ -441,16 +316,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin;
struct hdmi_eld *eld;
- int pin_idx;
-
- pin_idx = kcontrol->private_value;
- per_pin = get_pin(spec, pin_idx);
+ int pcm_idx;
+
+ pcm_idx = kcontrol->private_value;
+ mutex_lock(&spec->pcm_lock);
+ per_pin = pcm_idx_to_pin(spec, pcm_idx);
+ if (!per_pin) {
+ /* no pin is bound to the pcm */
+ memset(ucontrol->value.bytes.data, 0,
+ ARRAY_SIZE(ucontrol->value.bytes.data));
+ mutex_unlock(&spec->pcm_lock);
+ return 0;
+ }
eld = &per_pin->sink_eld;
- mutex_lock(&per_pin->lock);
if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
eld->eld_size > ELD_MAX_SIZE) {
- mutex_unlock(&per_pin->lock);
+ mutex_unlock(&spec->pcm_lock);
snd_BUG();
return -EINVAL;
}
@@ -460,7 +342,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
if (eld->eld_valid)
memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
eld->eld_size);
- mutex_unlock(&per_pin->lock);
+ mutex_unlock(&spec->pcm_lock);
return 0;
}
@@ -473,7 +355,7 @@ static struct snd_kcontrol_new eld_bytes_ctl = {
.get = hdmi_eld_ctl_get,
};
-static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx,
int device)
{
struct snd_kcontrol *kctl;
@@ -483,14 +365,17 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
if (!kctl)
return -ENOMEM;
- kctl->private_value = pin_idx;
+ kctl->private_value = pcm_idx;
kctl->id.device = device;
- err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
+ /* no pin nid is associated with the kctl now
+ * tbd: associate pin nid to eld ctl later
+ */
+ err = snd_hda_ctl_add(codec, 0, kctl);
if (err < 0)
return err;
- get_pin(spec, pin_idx)->eld_ctl = kctl;
+ get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl;
return 0;
}
@@ -547,20 +432,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
}
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
-{
- return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
- AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hda_codec *codec,
- hda_nid_t cvt_nid, int chs)
-{
- if (chs != hdmi_get_channel_count(codec, cvt_nid))
- snd_hda_codec_write(codec, cvt_nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
/*
* ELD proc files
*/
@@ -625,339 +496,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
#endif
/*
- * Channel mapping routines
- */
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
- int i, j;
- struct cea_channel_speaker_allocation *p;
-
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- p = channel_allocations + i;
- p->channels = 0;
- p->spk_mask = 0;
- for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
- if (p->speakers[j]) {
- p->channels++;
- p->spk_mask |= p->speakers[j];
- }
- }
-}
-
-static int get_channel_allocation_order(int ca)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- if (channel_allocations[i].ca_index == ca)
- break;
- }
- return i;
-}
-
-/*
- * The transformation takes two steps:
- *
- * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- * spk_mask => (channel_allocations[]) => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_channel_allocation(struct hda_codec *codec,
- struct hdmi_eld *eld, int channels)
-{
- int i;
- int ca = 0;
- int spk_mask = 0;
- char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
- /*
- * CA defaults to 0 for basic stereo audio
- */
- if (channels <= 2)
- return 0;
-
- /*
- * expand ELD's speaker allocation mask
- *
- * ELD tells the speaker mask in a compact(paired) form,
- * expand ELD's notions to match the ones used by Audio InfoFrame.
- */
- for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
- if (eld->info.spk_alloc & (1 << i))
- spk_mask |= eld_speaker_allocation_bits[i];
- }
-
- /* search for the first working match in the CA table */
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- if (channels == channel_allocations[i].channels &&
- (spk_mask & channel_allocations[i].spk_mask) ==
- channel_allocations[i].spk_mask) {
- ca = channel_allocations[i].ca_index;
- break;
- }
- }
-
- if (!ca) {
- /* if there was no match, select the regular ALSA channel
- * allocation with the matching number of channels */
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- if (channels == channel_allocations[i].channels) {
- ca = channel_allocations[i].ca_index;
- break;
- }
- }
- }
-
- snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf));
- codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
- ca, channels, buf);
-
- return ca;
-}
-
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
- struct hdmi_spec *spec = codec->spec;
- int i;
- int channel;
-
- for (i = 0; i < 8; i++) {
- channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i);
- codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n",
- channel, i);
- }
-#endif
-}
-
-static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid,
- bool non_pcm,
- int ca)
-{
- struct hdmi_spec *spec = codec->spec;
- struct cea_channel_speaker_allocation *ch_alloc;
- int i;
- int err;
- int order;
- int non_pcm_mapping[8];
-
- order = get_channel_allocation_order(ca);
- ch_alloc = &channel_allocations[order];
-
- if (hdmi_channel_mapping[ca][1] == 0) {
- 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))
- hdmi_slot++; /* skip zero slots */
-
- hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
- }
- /* fill the rest of the slots with ALSA channel 0xf */
- for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
- if (!ch_alloc->speakers[7 - hdmi_slot])
- hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
- }
-
- if (non_pcm) {
- for (i = 0; i < ch_alloc->channels; i++)
- non_pcm_mapping[i] = (i << 4) | i;
- for (; i < 8; i++)
- non_pcm_mapping[i] = (0xf << 4) | i;
- }
-
- for (i = 0; i < 8; i++) {
- int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
- int hdmi_slot = slotsetup & 0x0f;
- int channel = (slotsetup & 0xf0) >> 4;
- err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel);
- if (err) {
- codec_dbg(codec, "HDMI: channel mapping failed\n");
- break;
- }
- }
-}
-
-struct channel_map_table {
- unsigned char map; /* ALSA API channel map position */
- int spk_mask; /* speaker position bit mask */
-};
-
-static struct channel_map_table map_tables[] = {
- { SNDRV_CHMAP_FL, FL },
- { SNDRV_CHMAP_FR, FR },
- { SNDRV_CHMAP_RL, RL },
- { SNDRV_CHMAP_RR, RR },
- { SNDRV_CHMAP_LFE, LFE },
- { SNDRV_CHMAP_FC, FC },
- { SNDRV_CHMAP_RLC, RLC },
- { SNDRV_CHMAP_RRC, RRC },
- { SNDRV_CHMAP_RC, RC },
- { SNDRV_CHMAP_FLC, FLC },
- { SNDRV_CHMAP_FRC, FRC },
- { SNDRV_CHMAP_TFL, FLH },
- { SNDRV_CHMAP_TFR, FRH },
- { SNDRV_CHMAP_FLW, FLW },
- { SNDRV_CHMAP_FRW, FRW },
- { SNDRV_CHMAP_TC, TC },
- { SNDRV_CHMAP_TFC, FCH },
- {} /* terminator */
-};
-
-/* from ALSA API channel position to speaker bit mask */
-static int to_spk_mask(unsigned char c)
-{
- struct channel_map_table *t = map_tables;
- for (; t->map; t++) {
- if (t->map == c)
- return t->spk_mask;
- }
- return 0;
-}
-
-/* from ALSA API channel position to CEA slot */
-static int to_cea_slot(int ordered_ca, unsigned char pos)
-{
- int mask = to_spk_mask(pos);
- int i;
-
- if (mask) {
- for (i = 0; i < 8; i++) {
- if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
- return i;
- }
- }
-
- return -1;
-}
-
-/* from speaker bit mask to ALSA API channel position */
-static int spk_to_chmap(int spk)
-{
- struct channel_map_table *t = map_tables;
- for (; t->map; t++) {
- if (t->spk_mask == spk)
- return t->map;
- }
- return 0;
-}
-
-/* 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];
-
- return spk_to_chmap(mask);
-}
-
-/* get the CA index corresponding to the given ALSA API channel map */
-static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
-{
- int i, spks = 0, spk_mask = 0;
-
- for (i = 0; i < chs; i++) {
- int mask = to_spk_mask(map[i]);
- if (mask) {
- spk_mask |= mask;
- spks++;
- }
- }
-
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
- if ((chs == channel_allocations[i].channels ||
- spks == channel_allocations[i].channels) &&
- (spk_mask & channel_allocations[i].spk_mask) ==
- channel_allocations[i].spk_mask)
- return channel_allocations[i].ca_index;
- }
- return -1;
-}
-
-/* set up the channel slots for the given ALSA API channel map */
-static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid,
- int chs, unsigned char *map,
- int ca)
-{
- struct hdmi_spec *spec = codec->spec;
- int ordered_ca = get_channel_allocation_order(ca);
- int alsa_pos, hdmi_slot;
- int assignments[8] = {[0 ... 7] = 0xf};
-
- for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
-
- hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
-
- if (hdmi_slot < 0)
- continue; /* unassigned channel */
-
- assignments[hdmi_slot] = alsa_pos;
- }
-
- for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
- int err;
-
- err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot,
- assignments[hdmi_slot]);
- if (err)
- return -EINVAL;
- }
- return 0;
-}
-
-/* store ALSA API channel map from the current default map */
-static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
-{
- int i;
- int ordered_ca = get_channel_allocation_order(ca);
- for (i = 0; i < 8; i++) {
- if (i < channel_allocations[ordered_ca].channels)
- map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
- else
- map[i] = 0;
- }
-}
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
- hda_nid_t pin_nid, bool non_pcm, int ca,
- int channels, unsigned char *map,
- bool chmap_set)
-{
- if (!non_pcm && chmap_set) {
- hdmi_manual_setup_channel_mapping(codec, pin_nid,
- channels, map, ca);
- } else {
- hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
- hdmi_setup_fake_chmap(map, ca);
- }
-
- hdmi_debug_channel_mapping(codec, pin_nid);
-}
-
-static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
- int asp_slot, int channel)
-{
- return snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- (channel << 4) | asp_slot);
-}
-
-static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
- int asp_slot)
-{
- return (snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_HDMI_CHAN_SLOT,
- asp_slot) & 0xf0) >> 4;
-}
-
-/*
* Audio InfoFrame routines
*/
@@ -1132,11 +670,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
bool non_pcm)
{
struct hdmi_spec *spec = codec->spec;
+ struct hdac_chmap *chmap = &spec->chmap;
hda_nid_t pin_nid = per_pin->pin_nid;
int channels = per_pin->channels;
int active_channels;
struct hdmi_eld *eld;
- int ca, ordered_ca;
+ int ca;
if (!channels)
return;
@@ -1148,25 +687,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
eld = &per_pin->sink_eld;
- if (!non_pcm && per_pin->chmap_set)
- ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
- else
- ca = hdmi_channel_allocation(codec, eld, channels);
- if (ca < 0)
- ca = 0;
+ ca = snd_hdac_channel_allocation(&codec->core,
+ eld->info.spk_alloc, channels,
+ per_pin->chmap_set, non_pcm, per_pin->chmap);
- ordered_ca = get_channel_allocation_order(ca);
- active_channels = channel_allocations[ordered_ca].channels;
+ active_channels = snd_hdac_get_active_channels(ca);
- hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
+ chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid,
+ active_channels);
/*
* always configure channel mapping, it may have been changed by the
* user in the meantime
*/
- hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
- channels, per_pin->chmap,
- per_pin->chmap_set);
+ snd_hdac_setup_channel_mapping(&spec->chmap,
+ pin_nid, non_pcm, ca, channels,
+ per_pin->chmap, per_pin->chmap_set);
spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
eld->info.conn_type);
@@ -1338,6 +874,11 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
return 0;
}
+/* Try to find an available converter
+ * If pin_idx is less then zero, just try to find an available converter.
+ * Otherwise, try to find an available converter and get the cvt mux index
+ * of the pin.
+ */
static int hdmi_choose_cvt(struct hda_codec *codec,
int pin_idx, int *cvt_id, int *mux_id)
{
@@ -1346,7 +887,11 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
struct hdmi_spec_per_cvt *per_cvt = NULL;
int cvt_idx, mux_idx = 0;
- per_pin = get_pin(spec, pin_idx);
+ /* pin_idx < 0 means no pin will be bound to the converter */
+ if (pin_idx < 0)
+ per_pin = NULL;
+ else
+ per_pin = get_pin(spec, pin_idx);
/* Dynamically assign converter to stream */
for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
@@ -1355,6 +900,8 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
/* Must not already be assigned */
if (per_cvt->assigned)
continue;
+ if (per_pin == NULL)
+ break;
/* Must be in pin's mux's list of converters */
for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
@@ -1367,9 +914,10 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
/* No free converters */
if (cvt_idx == spec->num_cvts)
- return -ENODEV;
+ return -EBUSY;
- per_pin->mux_idx = mux_idx;
+ if (per_pin != NULL)
+ per_pin->mux_idx = mux_idx;
if (cvt_id)
*cvt_id = cvt_idx;
@@ -1395,6 +943,20 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
mux_idx);
}
+/* get the mux index for the converter of the pins
+ * converter's mux index is the same for all pins on Intel platform
+ */
+static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
+ hda_nid_t cvt_nid)
+{
+ int i;
+
+ for (i = 0; i < spec->num_cvts; i++)
+ if (spec->cvt_nids[i] == cvt_nid)
+ return i;
+ return -EINVAL;
+}
+
/* Intel HDMI workaround to fix audio routing issue:
* For some Intel display codecs, pins share the same connection list.
* So a conveter can be selected by multiple pins and playback on any of these
@@ -1446,6 +1008,74 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec,
}
}
+/* A wrapper of intel_not_share_asigned_cvt() */
+static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
+ hda_nid_t pin_nid, hda_nid_t cvt_nid)
+{
+ int mux_idx;
+ struct hdmi_spec *spec = codec->spec;
+
+ if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
+ return;
+
+ /* On Intel platform, the mapping of converter nid to
+ * mux index of the pins are always the same.
+ * The pin nid may be 0, this means all pins will not
+ * share the converter.
+ */
+ mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid);
+ if (mux_idx >= 0)
+ intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
+}
+
+/* called in hdmi_pcm_open when no pin is assigned to the PCM
+ * in dyn_pcm_assign mode.
+ */
+static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int cvt_idx, pcm_idx;
+ struct hdmi_spec_per_cvt *per_cvt = NULL;
+ int err;
+
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (pcm_idx < 0)
+ return -EINVAL;
+
+ err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+ if (err)
+ return err;
+
+ per_cvt = get_cvt(spec, cvt_idx);
+ per_cvt->assigned = 1;
+ hinfo->nid = per_cvt->cvt_nid;
+
+ intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+
+ set_bit(pcm_idx, &spec->pcm_in_use);
+ /* todo: setup spdif ctls assign */
+
+ /* Initially set the converter's capabilities */
+ hinfo->channels_min = per_cvt->channels_min;
+ hinfo->channels_max = per_cvt->channels_max;
+ hinfo->rates = per_cvt->rates;
+ hinfo->formats = per_cvt->formats;
+ hinfo->maxbps = per_cvt->maxbps;
+
+ /* Store the updated parameters */
+ runtime->hw.channels_min = hinfo->channels_min;
+ runtime->hw.channels_max = hinfo->channels_max;
+ runtime->hw.formats = hinfo->formats;
+ runtime->hw.rates = hinfo->rates;
+
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ return 0;
+}
+
/*
* HDA PCM callbacks
*/
@@ -1455,26 +1085,47 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
{
struct hdmi_spec *spec = codec->spec;
struct snd_pcm_runtime *runtime = substream->runtime;
- int pin_idx, cvt_idx, mux_idx = 0;
+ int pin_idx, cvt_idx, pcm_idx, mux_idx = 0;
struct hdmi_spec_per_pin *per_pin;
struct hdmi_eld *eld;
struct hdmi_spec_per_cvt *per_cvt = NULL;
int err;
/* Validate hinfo */
- pin_idx = hinfo_to_pin_index(codec, hinfo);
- if (snd_BUG_ON(pin_idx < 0))
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (pcm_idx < 0)
return -EINVAL;
- per_pin = get_pin(spec, pin_idx);
- eld = &per_pin->sink_eld;
+
+ mutex_lock(&spec->pcm_lock);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ if (!spec->dyn_pcm_assign) {
+ if (snd_BUG_ON(pin_idx < 0)) {
+ mutex_unlock(&spec->pcm_lock);
+ return -EINVAL;
+ }
+ } else {
+ /* no pin is assigned to the PCM
+ * PA need pcm open successfully when probe
+ */
+ if (pin_idx < 0) {
+ err = hdmi_pcm_open_no_pin(hinfo, codec, substream);
+ mutex_unlock(&spec->pcm_lock);
+ return err;
+ }
+ }
err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
- if (err < 0)
+ if (err < 0) {
+ mutex_unlock(&spec->pcm_lock);
return err;
+ }
per_cvt = get_cvt(spec, cvt_idx);
/* Claim converter */
per_cvt->assigned = 1;
+
+ set_bit(pcm_idx, &spec->pcm_in_use);
+ per_pin = get_pin(spec, pin_idx);
per_pin->cvt_nid = per_cvt->cvt_nid;
hinfo->nid = per_cvt->cvt_nid;
@@ -1486,7 +1137,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
if (is_haswell_plus(codec) || is_valleyview_plus(codec))
intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
- snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
+ snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
/* Initially set the converter's capabilities */
hinfo->channels_min = per_cvt->channels_min;
@@ -1495,6 +1146,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
hinfo->formats = per_cvt->formats;
hinfo->maxbps = per_cvt->maxbps;
+ eld = &per_pin->sink_eld;
/* Restrict capabilities by ELD if this isn't disabled */
if (!static_hdmi_pcm && eld->eld_valid) {
snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
@@ -1502,11 +1154,13 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
!hinfo->rates || !hinfo->formats) {
per_cvt->assigned = 0;
hinfo->nid = 0;
- snd_hda_spdif_ctls_unassign(codec, pin_idx);
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+ mutex_unlock(&spec->pcm_lock);
return -ENODEV;
}
}
+ mutex_unlock(&spec->pcm_lock);
/* Store the updated parameters */
runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max;
@@ -1541,6 +1195,125 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
return 0;
}
+static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int i;
+
+ /* try the prefer PCM */
+ if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
+ return per_pin->pin_nid_idx;
+
+ /* have a second try; check the "reserved area" over num_pins */
+ for (i = spec->num_pins; i < spec->pcm_used; i++) {
+ if (!test_bit(i, &spec->pcm_bitmap))
+ return i;
+ }
+
+ /* the last try; check the empty slots in pins */
+ for (i = 0; i < spec->num_pins; i++) {
+ if (!test_bit(i, &spec->pcm_bitmap))
+ return i;
+ }
+ return -EBUSY;
+}
+
+static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int idx;
+
+ /* pcm already be attached to the pin */
+ if (per_pin->pcm)
+ return;
+ idx = hdmi_find_pcm_slot(spec, per_pin);
+ if (idx == -EBUSY)
+ return;
+ per_pin->pcm_idx = idx;
+ per_pin->pcm = get_hdmi_pcm(spec, idx);
+ set_bit(idx, &spec->pcm_bitmap);
+}
+
+static void hdmi_detach_hda_pcm(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ int idx;
+
+ /* pcm already be detached from the pin */
+ if (!per_pin->pcm)
+ return;
+ idx = per_pin->pcm_idx;
+ per_pin->pcm_idx = -1;
+ per_pin->pcm = NULL;
+ if (idx >= 0 && idx < spec->pcm_used)
+ clear_bit(idx, &spec->pcm_bitmap);
+}
+
+static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid)
+{
+ int mux_idx;
+
+ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+ if (per_pin->mux_nids[mux_idx] == cvt_nid)
+ break;
+ return mux_idx;
+}
+
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid);
+
+static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hda_codec *codec = per_pin->codec;
+ struct hda_pcm *pcm;
+ struct hda_pcm_stream *hinfo;
+ struct snd_pcm_substream *substream;
+ int mux_idx;
+ bool non_pcm;
+
+ if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+ pcm = get_pcm_rec(spec, per_pin->pcm_idx);
+ else
+ return;
+ if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use))
+ return;
+
+ /* hdmi audio only uses playback and one substream */
+ hinfo = pcm->stream;
+ substream = pcm->pcm->streams[0].substream;
+
+ per_pin->cvt_nid = hinfo->nid;
+
+ mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid);
+ if (mux_idx < per_pin->num_mux_nids)
+ snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+ snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid);
+
+ non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid);
+ if (substream->runtime)
+ per_pin->channels = substream->runtime->channels;
+ per_pin->setup = true;
+ per_pin->mux_idx = mux_idx;
+
+ hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+}
+
+static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+ snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx);
+
+ per_pin->chmap_set = false;
+ memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+ per_pin->setup = false;
+ per_pin->channels = 0;
+}
+
/* update per_pin ELD from the given new ELD;
* setup info frame and notification accordingly
*/
@@ -1549,8 +1322,27 @@ static void update_eld(struct hda_codec *codec,
struct hdmi_eld *eld)
{
struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+ struct hdmi_spec *spec = codec->spec;
bool old_eld_valid = pin_eld->eld_valid;
bool eld_changed;
+ int pcm_idx = -1;
+
+ /* for monitor disconnection, save pcm_idx firstly */
+ pcm_idx = per_pin->pcm_idx;
+ if (spec->dyn_pcm_assign) {
+ if (eld->eld_valid) {
+ hdmi_attach_hda_pcm(spec, per_pin);
+ hdmi_pcm_setup_pin(spec, per_pin);
+ } else {
+ hdmi_pcm_reset_pin(spec, per_pin);
+ hdmi_detach_hda_pcm(spec, per_pin);
+ }
+ }
+ /* if pcm_idx == -1, it means this is in monitor connection event
+ * we can get the correct pcm_idx now.
+ */
+ if (pcm_idx == -1)
+ pcm_idx = per_pin->pcm_idx;
if (eld->eld_valid)
snd_hdmi_show_eld(codec, &eld->info);
@@ -1584,11 +1376,11 @@ static void update_eld(struct hda_codec *codec,
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
}
- if (eld_changed)
+ if (eld_changed && pcm_idx >= 0)
snd_ctl_notify(codec->card,
SNDRV_CTL_EVENT_MASK_VALUE |
SNDRV_CTL_EVENT_MASK_INFO,
- &per_pin->eld_ctl->id);
+ &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
}
/* update ELD and jack state via HD-audio verbs */
@@ -1613,7 +1405,6 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
bool ret;
bool do_repoll = false;
- snd_hda_power_up_pm(codec);
present = snd_hda_pin_sense(codec, pin_nid);
mutex_lock(&per_pin->lock);
@@ -1652,16 +1443,39 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
jack->block_report = !ret;
mutex_unlock(&per_pin->lock);
- snd_hda_power_down_pm(codec);
return ret;
}
+static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
+ struct hdmi_spec_per_pin *per_pin)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct snd_jack *jack = NULL;
+ struct hda_jack_tbl *jack_tbl;
+
+ /* if !dyn_pcm_assign, get jack from hda_jack_tbl
+ * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not
+ * NULL even after snd_hda_jack_tbl_clear() is called to
+ * free snd_jack. This may cause access invalid memory
+ * when calling snd_jack_report
+ */
+ if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign)
+ jack = spec->pcm_rec[per_pin->pcm_idx].jack;
+ else if (!spec->dyn_pcm_assign) {
+ jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+ if (jack_tbl)
+ jack = jack_tbl->jack;
+ }
+ return jack;
+}
+
/* update ELD and jack state via audio component */
static void sync_eld_via_acomp(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin)
{
struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld = &spec->temp_eld;
+ struct snd_jack *jack = NULL;
int size;
mutex_lock(&per_pin->lock);
@@ -1685,8 +1499,16 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
eld->eld_size = 0;
}
+ /* pcm_idx >=0 before update_eld() means it is in monitor
+ * disconnected event. Jack must be fetched before update_eld()
+ */
+ jack = pin_idx_to_jack(codec, per_pin);
update_eld(codec, per_pin, eld);
- snd_jack_report(per_pin->acomp_jack,
+ if (jack == NULL)
+ jack = pin_idx_to_jack(codec, per_pin);
+ if (jack == NULL)
+ goto unlock;
+ snd_jack_report(jack,
eld->monitor_present ? SND_JACK_AVOUT : 0);
unlock:
mutex_unlock(&per_pin->lock);
@@ -1695,13 +1517,26 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
{
struct hda_codec *codec = per_pin->codec;
+ struct hdmi_spec *spec = codec->spec;
+ int ret;
+
+ /* no temporary power up/down needed for component notifier */
+ if (!codec_has_acomp(codec))
+ snd_hda_power_up_pm(codec);
+ mutex_lock(&spec->pcm_lock);
if (codec_has_acomp(codec)) {
sync_eld_via_acomp(codec, per_pin);
- return false; /* don't call snd_hda_jack_report_sync() */
+ ret = false; /* don't call snd_hda_jack_report_sync() */
} else {
- return hdmi_present_sense_via_verbs(per_pin, repoll);
+ ret = hdmi_present_sense_via_verbs(per_pin, repoll);
}
+ mutex_unlock(&spec->pcm_lock);
+
+ if (!codec_has_acomp(codec))
+ snd_hda_power_down_pm(codec);
+
+ return ret;
}
static void hdmi_repoll_eld(struct work_struct *work)
@@ -1745,6 +1580,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
per_pin->pin_nid = pin_nid;
per_pin->non_pcm = false;
+ if (spec->dyn_pcm_assign)
+ per_pin->pcm_idx = -1;
+ else {
+ per_pin->pcm = get_hdmi_pcm(spec, pin_idx);
+ per_pin->pcm_idx = pin_idx;
+ }
+ per_pin->pin_nid_idx = pin_idx;
err = hdmi_read_pin_conn(codec, pin_idx);
if (err < 0)
@@ -1773,8 +1615,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
per_cvt->channels_min = 2;
if (chans <= 16) {
per_cvt->channels_max = chans;
- if (chans > spec->channels_max)
- spec->channels_max = chans;
+ if (chans > spec->chmap.channels_max)
+ spec->chmap.channels_max = chans;
}
err = snd_hda_query_supported_pcm(codec, cvt_nid,
@@ -1851,13 +1693,34 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
{
hda_nid_t cvt_nid = hinfo->nid;
struct hdmi_spec *spec = codec->spec;
- int pin_idx = hinfo_to_pin_index(codec, hinfo);
- struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
- hda_nid_t pin_nid = per_pin->pin_nid;
+ int pin_idx;
+ struct hdmi_spec_per_pin *per_pin;
+ hda_nid_t pin_nid;
struct snd_pcm_runtime *runtime = substream->runtime;
bool non_pcm;
int pinctl;
+ int err;
+
+ mutex_lock(&spec->pcm_lock);
+ pin_idx = hinfo_to_pin_index(codec, hinfo);
+ if (spec->dyn_pcm_assign && pin_idx < 0) {
+ /* when dyn_pcm_assign and pcm is not bound to a pin
+ * skip pin setup and return 0 to make audio playback
+ * be ongoing
+ */
+ intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+ snd_hda_codec_setup_stream(codec, cvt_nid,
+ stream_tag, 0, format);
+ mutex_unlock(&spec->pcm_lock);
+ return 0;
+ }
+ if (snd_BUG_ON(pin_idx < 0)) {
+ mutex_unlock(&spec->pcm_lock);
+ return -EINVAL;
+ }
+ per_pin = get_pin(spec, pin_idx);
+ pin_nid = per_pin->pin_nid;
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
/* Verify pin:cvt selections to avoid silent audio after S3.
* After S3, the audio driver restores pin:cvt selections
@@ -1882,7 +1745,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
mutex_unlock(&per_pin->lock);
-
if (spec->dyn_pin_out) {
pinctl = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
@@ -1891,7 +1753,10 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
pinctl | PIN_OUT);
}
- return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+ err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
+ stream_tag, format);
+ mutex_unlock(&spec->pcm_lock);
+ return err;
}
static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1907,12 +1772,15 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct hdmi_spec *spec = codec->spec;
- int cvt_idx, pin_idx;
+ int cvt_idx, pin_idx, pcm_idx;
struct hdmi_spec_per_cvt *per_cvt;
struct hdmi_spec_per_pin *per_pin;
int pinctl;
if (hinfo->nid) {
+ pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+ if (snd_BUG_ON(pcm_idx < 0))
+ return -EINVAL;
cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
if (snd_BUG_ON(cvt_idx < 0))
return -EINVAL;
@@ -1922,9 +1790,19 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
per_cvt->assigned = 0;
hinfo->nid = 0;
+ mutex_lock(&spec->pcm_lock);
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+ clear_bit(pcm_idx, &spec->pcm_in_use);
pin_idx = hinfo_to_pin_index(codec, hinfo);
- if (snd_BUG_ON(pin_idx < 0))
+ if (spec->dyn_pcm_assign && pin_idx < 0) {
+ mutex_unlock(&spec->pcm_lock);
+ return 0;
+ }
+
+ if (snd_BUG_ON(pin_idx < 0)) {
+ mutex_unlock(&spec->pcm_lock);
return -EINVAL;
+ }
per_pin = get_pin(spec, pin_idx);
if (spec->dyn_pin_out) {
@@ -1935,8 +1813,6 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
pinctl & ~PIN_OUT);
}
- snd_hda_spdif_ctls_unassign(codec, pin_idx);
-
mutex_lock(&per_pin->lock);
per_pin->chmap_set = false;
memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
@@ -1944,6 +1820,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
per_pin->setup = false;
per_pin->channels = 0;
mutex_unlock(&per_pin->lock);
+ mutex_unlock(&spec->pcm_lock);
}
return 0;
@@ -1956,162 +1833,42 @@ static const struct hda_pcm_ops generic_ops = {
.cleanup = generic_hdmi_playback_pcm_cleanup,
};
-/*
- * ALSA API channel-map control callbacks
- */
-static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap)
{
- struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
- struct hda_codec *codec = info->private_data;
+ struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
struct hdmi_spec *spec = codec->spec;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = spec->channels_max;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = SNDRV_CHMAP_LAST;
- return 0;
-}
-
-static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
- int channels)
-{
- /* If the speaker allocation matches the channel count, it is OK.*/
- if (cap->channels != channels)
- return -1;
-
- /* all channels are remappable freely */
- return SNDRV_CTL_TLVT_CHMAP_VAR;
-}
-
-static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
- unsigned int *chmap, int channels)
-{
- int count = 0;
- int c;
-
- for (c = 7; c >= 0; c--) {
- int spk = cap->speakers[c];
- if (!spk)
- continue;
-
- chmap[count++] = spk_to_chmap(spk);
- }
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
- WARN_ON(count != channels);
-}
-
-static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
-{
- struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
- struct hda_codec *codec = info->private_data;
- struct hdmi_spec *spec = codec->spec;
- unsigned int __user *dst;
- int chs, count = 0;
+ /* chmap is already set to 0 in caller */
+ if (!per_pin)
+ return;
- if (size < 8)
- return -ENOMEM;
- if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
- return -EFAULT;
- size -= 8;
- dst = tlv + 2;
- for (chs = 2; chs <= spec->channels_max; chs++) {
- int i;
- struct cea_channel_speaker_allocation *cap;
- cap = channel_allocations;
- for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
- int chs_bytes = chs * 4;
- int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs);
- unsigned int tlv_chmap[8];
-
- if (type < 0)
- continue;
- if (size < 8)
- return -ENOMEM;
- if (put_user(type, dst) ||
- put_user(chs_bytes, dst + 1))
- return -EFAULT;
- dst += 2;
- size -= 8;
- count += 8;
- if (size < chs_bytes)
- return -ENOMEM;
- size -= chs_bytes;
- count += chs_bytes;
- spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs);
- if (copy_to_user(dst, tlv_chmap, chs_bytes))
- return -EFAULT;
- dst += chs;
- }
- }
- if (put_user(count, tlv + 1))
- return -EFAULT;
- return 0;
+ memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap));
}
-static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+ unsigned char *chmap, int prepared)
{
- struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
- struct hda_codec *codec = info->private_data;
+ struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
struct hdmi_spec *spec = codec->spec;
- int pin_idx = kcontrol->private_value;
- struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
- int i;
-
- for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
- ucontrol->value.integer.value[i] = per_pin->chmap[i];
- return 0;
-}
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
-static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
- struct hda_codec *codec = info->private_data;
- struct hdmi_spec *spec = codec->spec;
- int pin_idx = kcontrol->private_value;
- struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
- unsigned int ctl_idx;
- struct snd_pcm_substream *substream;
- unsigned char chmap[8];
- int i, err, ca, prepared = 0;
-
- ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- substream = snd_pcm_chmap_substream(info, ctl_idx);
- if (!substream || !substream->runtime)
- return 0; /* just for avoiding error from alsactl restore */
- switch (substream->runtime->status->state) {
- case SNDRV_PCM_STATE_OPEN:
- case SNDRV_PCM_STATE_SETUP:
- break;
- case SNDRV_PCM_STATE_PREPARED:
- prepared = 1;
- break;
- default:
- return -EBUSY;
- }
- memset(chmap, 0, sizeof(chmap));
- for (i = 0; i < ARRAY_SIZE(chmap); i++)
- chmap[i] = ucontrol->value.integer.value[i];
- if (!memcmp(chmap, per_pin->chmap, sizeof(chmap)))
- return 0;
- ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
- if (ca < 0)
- return -EINVAL;
- if (spec->ops.chmap_validate) {
- err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
- if (err)
- return err;
- }
mutex_lock(&per_pin->lock);
per_pin->chmap_set = true;
- memcpy(per_pin->chmap, chmap, sizeof(chmap));
+ memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
if (prepared)
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
mutex_unlock(&per_pin->lock);
+}
- return 0;
+static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+{
+ struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+ struct hdmi_spec *spec = codec->spec;
+ struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+ return per_pin ? true:false;
}
static int generic_hdmi_build_pcms(struct hda_codec *codec)
@@ -2126,7 +1883,9 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
if (!info)
return -ENOMEM;
- spec->pcm_rec[pin_idx] = info;
+
+ spec->pcm_rec[pin_idx].pcm = info;
+ spec->pcm_used++;
info->pcm_type = HDA_PCM_TYPE_HDMI;
info->own_chmap = true;
@@ -2139,15 +1898,16 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
return 0;
}
-static void free_acomp_jack_priv(struct snd_jack *jack)
+static void free_hdmi_jack_priv(struct snd_jack *jack)
{
- struct hdmi_spec_per_pin *per_pin = jack->private_data;
+ struct hdmi_pcm *pcm = jack->private_data;
- per_pin->acomp_jack = NULL;
+ pcm->jack = NULL;
}
-static int add_acomp_jack_kctl(struct hda_codec *codec,
- struct hdmi_spec_per_pin *per_pin,
+static int add_hdmi_jack_kctl(struct hda_codec *codec,
+ struct hdmi_spec *spec,
+ int pcm_idx,
const char *name)
{
struct snd_jack *jack;
@@ -2157,88 +1917,107 @@ static int add_acomp_jack_kctl(struct hda_codec *codec,
true, false);
if (err < 0)
return err;
- per_pin->acomp_jack = jack;
- jack->private_data = per_pin;
- jack->private_free = free_acomp_jack_priv;
+
+ spec->pcm_rec[pcm_idx].jack = jack;
+ jack->private_data = &spec->pcm_rec[pcm_idx];
+ jack->private_free = free_hdmi_jack_priv;
return 0;
}
-static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
{
char hdmi_str[32] = "HDMI/DP";
struct hdmi_spec *spec = codec->spec;
- struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
- int pcmdev = get_pcm_rec(spec, pin_idx)->device;
+ struct hdmi_spec_per_pin *per_pin;
+ struct hda_jack_tbl *jack;
+ int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
bool phantom_jack;
+ int ret;
if (pcmdev > 0)
sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
- if (codec_has_acomp(codec))
- return add_acomp_jack_kctl(codec, per_pin, hdmi_str);
+
+ if (spec->dyn_pcm_assign)
+ return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str);
+
+ /* for !dyn_pcm_assign, we still use hda_jack for compatibility */
+ /* if !dyn_pcm_assign, it must be non-MST mode.
+ * This means pcms and pins are statically mapped.
+ * And pcm_idx is pin_idx.
+ */
+ per_pin = get_pin(spec, pcm_idx);
phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
if (phantom_jack)
strncat(hdmi_str, " Phantom",
sizeof(hdmi_str) - strlen(hdmi_str) - 1);
-
- return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
- phantom_jack);
+ ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
+ phantom_jack);
+ if (ret < 0)
+ return ret;
+ jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+ if (jack == NULL)
+ return 0;
+ /* assign jack->jack to pcm_rec[].jack to
+ * align with dyn_pcm_assign mode
+ */
+ spec->pcm_rec[pcm_idx].jack = jack->jack;
+ return 0;
}
static int generic_hdmi_build_controls(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
int err;
- int pin_idx;
+ int pin_idx, pcm_idx;
- for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
- struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
- err = generic_hdmi_build_jack(codec, pin_idx);
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ err = generic_hdmi_build_jack(codec, pcm_idx);
if (err < 0)
return err;
- err = snd_hda_create_dig_out_ctls(codec,
+ /* create the spdif for each pcm
+ * pin will be bound when monitor is connected
+ */
+ if (spec->dyn_pcm_assign)
+ err = snd_hda_create_dig_out_ctls(codec,
+ 0, spec->cvt_nids[0],
+ HDA_PCM_TYPE_HDMI);
+ else {
+ struct hdmi_spec_per_pin *per_pin =
+ get_pin(spec, pcm_idx);
+ err = snd_hda_create_dig_out_ctls(codec,
per_pin->pin_nid,
per_pin->mux_nids[0],
HDA_PCM_TYPE_HDMI);
+ }
if (err < 0)
return err;
- snd_hda_spdif_ctls_unassign(codec, pin_idx);
+ snd_hda_spdif_ctls_unassign(codec, pcm_idx);
/* add control for ELD Bytes */
- err = hdmi_create_eld_ctl(codec, pin_idx,
- get_pcm_rec(spec, pin_idx)->device);
-
+ err = hdmi_create_eld_ctl(codec, pcm_idx,
+ get_pcm_rec(spec, pcm_idx)->device);
if (err < 0)
return err;
+ }
+
+ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
hdmi_present_sense(per_pin, 0);
}
/* add channel maps */
- for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
struct hda_pcm *pcm;
- struct snd_pcm_chmap *chmap;
- struct snd_kcontrol *kctl;
- int i;
- pcm = spec->pcm_rec[pin_idx];
+ pcm = get_pcm_rec(spec, pcm_idx);
if (!pcm || !pcm->pcm)
break;
- err = snd_pcm_add_chmap_ctls(pcm->pcm,
- SNDRV_PCM_STREAM_PLAYBACK,
- NULL, 0, pin_idx, &chmap);
+ err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap);
if (err < 0)
return err;
- /* override handlers */
- chmap->private_data = codec;
- kctl = chmap->kctl;
- for (i = 0; i < kctl->count; i++)
- kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
- kctl->info = hdmi_chmap_ctl_info;
- kctl->get = hdmi_chmap_ctl_get;
- kctl->put = hdmi_chmap_ctl_put;
- kctl->tlv.c = hdmi_chmap_ctl_tlv;
}
return 0;
@@ -2293,18 +2072,25 @@ static void hdmi_array_free(struct hdmi_spec *spec)
static void generic_hdmi_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
- int pin_idx;
+ int pin_idx, pcm_idx;
if (codec_has_acomp(codec))
snd_hdac_i915_register_notifier(NULL);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-
cancel_delayed_work_sync(&per_pin->work);
eld_proc_free(per_pin);
- if (per_pin->acomp_jack)
- snd_device_free(codec->card, per_pin->acomp_jack);
+ }
+
+ for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+ if (spec->pcm_rec[pcm_idx].jack == NULL)
+ continue;
+ if (spec->dyn_pcm_assign)
+ snd_device_free(codec->card,
+ spec->pcm_rec[pcm_idx].jack);
+ else
+ spec->pcm_rec[pcm_idx].jack = NULL;
}
if (spec->i915_bound)
@@ -2343,16 +2129,11 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
static const struct hdmi_ops generic_standard_hdmi_ops = {
.pin_get_eld = snd_hdmi_get_eld,
- .pin_get_slot_channel = hdmi_pin_get_slot_channel,
- .pin_set_slot_channel = hdmi_pin_set_slot_channel,
.pin_setup_infoframe = hdmi_pin_setup_infoframe,
.pin_hbr_setup = hdmi_pin_hbr_setup,
.setup_stream = hdmi_setup_stream,
- .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
- .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
};
-
static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
hda_nid_t nid)
{
@@ -2432,6 +2213,10 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
struct hda_codec *codec = audio_ptr;
int pin_nid = port + 0x04;
+ /* we assume only from port-B to port-D */
+ if (port < 1 || port > 3)
+ return;
+
/* skip notification during system suspend (but not in runtime PM);
* the state will be updated at resume
*/
@@ -2453,12 +2238,20 @@ static int patch_generic_hdmi(struct hda_codec *codec)
return -ENOMEM;
spec->ops = generic_standard_hdmi_ops;
+ mutex_init(&spec->pcm_lock);
+ snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+ spec->chmap.ops.get_chmap = hdmi_get_chmap;
+ spec->chmap.ops.set_chmap = hdmi_set_chmap;
+ spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+
codec->spec = spec;
hdmi_array_init(spec, 4);
- /* Try to bind with i915 for any Intel codecs (if not done yet) */
+ /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */
if (!codec_has_acomp(codec) &&
- (codec->core.vendor_id >> 16) == 0x8086)
+ (codec->core.vendor_id >> 16) == 0x8086 &&
+ is_haswell_plus(codec))
if (!snd_hdac_i915_init(&codec->bus->core))
spec->i915_bound = true;
@@ -2496,7 +2289,6 @@ static int patch_generic_hdmi(struct hda_codec *codec)
generic_hdmi_init_per_pins(codec);
- init_channel_allocations();
if (codec_has_acomp(codec)) {
codec->depop_delay = 0;
@@ -2510,6 +2302,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
}
+ WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
return 0;
}
@@ -2532,7 +2325,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
info = snd_hda_codec_pcm_new(codec, "HDMI 0");
if (!info)
return -ENOMEM;
- spec->pcm_rec[0] = info;
+ spec->pcm_rec[0].pcm = info;
info->pcm_type = HDA_PCM_TYPE_HDMI;
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
*pstr = spec->pcm_playback;
@@ -3043,16 +2836,22 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
* - 0x10de0015
* - 0x10de0040
*/
-static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
- int channels)
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap, int channels)
{
if (cap->ca_index == 0x00 && channels == 2)
return SNDRV_CTL_TLVT_CHMAP_FIXED;
- return hdmi_chmap_cea_alloc_validate_get_type(cap, channels);
+ /* If the speaker allocation matches the channel count, it is OK. */
+ if (cap->channels != channels)
+ return -1;
+
+ /* all channels are remappable freely */
+ return SNDRV_CTL_TLVT_CHMAP_VAR;
}
-static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
+static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
+ int ca, int chs, unsigned char *map)
{
if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
return -EINVAL;
@@ -3072,9 +2871,9 @@ static int patch_nvhdmi(struct hda_codec *codec)
spec = codec->spec;
spec->dyn_pin_out = true;
- spec->ops.chmap_cea_alloc_validate_get_type =
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
nvhdmi_chmap_cea_alloc_validate_get_type;
- spec->ops.chmap_validate = nvhdmi_chmap_validate;
+ spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
return 0;
}
@@ -3322,16 +3121,17 @@ static int atihdmi_paired_swap_fc_lfe(int pos)
return pos;
}
-static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
+static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
+ int ca, int chs, unsigned char *map)
{
- struct cea_channel_speaker_allocation *cap;
+ struct hdac_cea_channel_speaker_allocation *cap;
int i, j;
/* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
- cap = &channel_allocations[get_channel_allocation_order(ca)];
+ cap = snd_hdac_get_ch_alloc_from_ca(ca);
for (i = 0; i < chs; ++i) {
- int mask = to_spk_mask(map[i]);
+ int mask = snd_hdac_chmap_to_spk_mask(map[i]);
bool ok = false;
bool companion_ok = false;
@@ -3347,7 +3147,7 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
if (i % 2 == 0 && i + 1 < chs) {
/* even channel, check the odd companion */
int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
- int comp_mask_req = to_spk_mask(map[i+1]);
+ int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]);
int comp_mask_act = cap->speakers[comp_chan_idx];
if (comp_mask_req == comp_mask_act)
@@ -3369,9 +3169,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
return 0;
}
-static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
- int hdmi_slot, int stream_channel)
+static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
+ hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
{
+ struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
int verb;
int ati_channel_setup = 0;
@@ -3404,9 +3205,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
}
-static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
- int asp_slot)
+static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
+ hda_nid_t pin_nid, int asp_slot)
{
+ struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
bool was_odd = false;
int ati_asp_slot = asp_slot;
int verb;
@@ -3433,8 +3235,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
}
-static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
- int channels)
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(
+ struct hdac_chmap *chmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ int channels)
{
int c;
@@ -3461,8 +3265,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_s
return SNDRV_CTL_TLVT_CHMAP_PAIRED;
}
-static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
- unsigned int *chmap, int channels)
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+ struct hdac_cea_channel_speaker_allocation *cap,
+ unsigned int *chmap, int channels)
{
/* produce paired maps for pre-rev3 ATI/AMD codecs */
int count = 0;
@@ -3479,7 +3284,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_all
continue;
}
- chmap[count++] = spk_to_chmap(spk);
+ chmap[count++] = snd_hdac_spk_to_chmap(spk);
}
WARN_ON(count != channels);
@@ -3573,18 +3378,21 @@ static int patch_atihdmi(struct hda_codec *codec)
spec = codec->spec;
spec->ops.pin_get_eld = atihdmi_pin_get_eld;
- spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
- spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
spec->ops.setup_stream = atihdmi_setup_stream;
if (!has_amd_full_remap_support(codec)) {
/* override to ATI/AMD-specific versions with pairwise mapping */
- spec->ops.chmap_cea_alloc_validate_get_type =
+ spec->chmap.ops.chmap_cea_alloc_validate_get_type =
atihdmi_paired_chmap_cea_alloc_validate_get_type;
- spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap;
- spec->ops.chmap_validate = atihdmi_paired_chmap_validate;
+ spec->chmap.ops.cea_alloc_to_tlv_chmap =
+ atihdmi_paired_cea_alloc_to_tlv_chmap;
+ spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
+ spec->chmap.ops.pin_get_slot_channel =
+ atihdmi_pin_get_slot_channel;
+ spec->chmap.ops.pin_set_slot_channel =
+ atihdmi_pin_set_slot_channel;
}
/* ATI/AMD converters do not advertise all of their capabilities */
@@ -3596,7 +3404,7 @@ static int patch_atihdmi(struct hda_codec *codec)
per_cvt->maxbps = max(per_cvt->maxbps, 24u);
}
- spec->channels_max = max(spec->channels_max, 8u);
+ spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
return 0;
}
@@ -3659,6 +3467,7 @@ HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi),
HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch),
HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 93d2156b6241..4f5ca0b9ce27 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5556,6 +5556,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
index 0a4ad5feb82e..59ab6cee1ad8 100644
--- a/sound/pci/hda/thinkpad_helper.c
+++ b/sound/pci/hda/thinkpad_helper.c
@@ -10,23 +10,10 @@
static int (*led_set_func)(int, bool);
static void (*old_vmaster_hook)(void *, int);
-static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
- void **rv)
-{
- bool *found = context;
- *found = true;
- return AE_OK;
-}
-
static bool is_thinkpad(struct hda_codec *codec)
{
- bool found = false;
- if (codec->core.subsystem_id >> 16 != 0x17aa)
- return false;
- if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
- return true;
- found = false;
- return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
+ return (codec->core.subsystem_id >> 16 == 0x17aa) &&
+ (acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068"));
}
static void update_tpacpi_mute_led(void *private_data, int enabled)
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 12c2c180e407..8151318a69a2 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -2879,6 +2879,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
static struct snd_pci_quirk intel8x0_clock_list[] = {
SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+ SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index bc81b9f75ed0..25c0ddd3a53b 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -132,7 +132,7 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
}
if(start) {
- u32 stat;
+ u32 stat = 0;
group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 24a1955b8c29..58fd79ebac20 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -726,7 +726,7 @@ int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int
int volume[2];
struct mixart_msg request;
struct mixart_set_out_stream_level_req set_level;
- u32 status;
+ u32 status = 0;
struct mixart_pipe *pipe;
memset(&set_level, 0, sizeof(set_level));
@@ -778,7 +778,7 @@ int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes)
struct mixart_pipe *pipe;
struct mixart_msg request;
struct mixart_set_in_audio_level_req set_level;
- u32 status;
+ u32 status = 0;
if(is_aes) {
idx = 1;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 7ea66ee3653f..182d92efc7c8 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -6,7 +6,7 @@ menuconfig SND_SOC
tristate "ALSA for SoC audio support"
select SND_PCM
select AC97_BUS if SND_SOC_AC97_BUS
- select SND_JACK if INPUT=y || INPUT=SND
+ select SND_JACK
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
---help---
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index ba8def5665c4..276897033639 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -285,7 +285,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
int ret;
@@ -346,7 +347,8 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
@@ -392,7 +394,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
ssc_p->daifmt = fmt;
return 0;
@@ -404,7 +407,8 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
switch (div_id) {
case ATMEL_SSC_CMR_DIV:
@@ -445,7 +449,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- int id = dai->id;
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ int id = pdev->id;
struct atmel_ssc_info *ssc_p = &ssc_info[id];
struct ssc_device *ssc = ssc_p->ssc;
struct atmel_pcm_dma_params *dma_params;
@@ -772,7 +777,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir;
@@ -795,7 +801,8 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+ struct platform_device *pdev = to_platform_device(dai->dev);
+ struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params;
int dir;
@@ -824,11 +831,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
{
struct atmel_ssc_info *ssc_p;
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
if (!cpu_dai->active)
return 0;
- ssc_p = &ssc_info[cpu_dai->id];
+ ssc_p = &ssc_info[pdev->id];
/* Save the status register before disabling transmit and receive */
ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
@@ -852,12 +860,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
{
struct atmel_ssc_info *ssc_p;
+ struct platform_device *pdev = to_platform_device(cpu_dai->dev);
u32 cr;
if (!cpu_dai->active)
return 0;
- ssc_p = &ssc_info[cpu_dai->id];
+ ssc_p = &ssc_info[pdev->id];
/* restore SSC register settings */
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
index 3303d5f58082..1c1f2210387b 100644
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -37,6 +37,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/of_address.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -46,55 +47,6 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
-/* Clock registers */
-#define BCM2835_CLK_PCMCTL_REG 0x00
-#define BCM2835_CLK_PCMDIV_REG 0x04
-
-/* Clock register settings */
-#define BCM2835_CLK_PASSWD (0x5a000000)
-#define BCM2835_CLK_PASSWD_MASK (0xff000000)
-#define BCM2835_CLK_MASH(v) ((v) << 9)
-#define BCM2835_CLK_FLIP BIT(8)
-#define BCM2835_CLK_BUSY BIT(7)
-#define BCM2835_CLK_KILL BIT(5)
-#define BCM2835_CLK_ENAB BIT(4)
-#define BCM2835_CLK_SRC(v) (v)
-
-#define BCM2835_CLK_SHIFT (12)
-#define BCM2835_CLK_DIVI(v) ((v) << BCM2835_CLK_SHIFT)
-#define BCM2835_CLK_DIVF(v) (v)
-#define BCM2835_CLK_DIVF_MASK (0xFFF)
-
-enum {
- BCM2835_CLK_MASH_0 = 0,
- BCM2835_CLK_MASH_1,
- BCM2835_CLK_MASH_2,
- BCM2835_CLK_MASH_3,
-};
-
-enum {
- BCM2835_CLK_SRC_GND = 0,
- BCM2835_CLK_SRC_OSC,
- BCM2835_CLK_SRC_DBG0,
- BCM2835_CLK_SRC_DBG1,
- BCM2835_CLK_SRC_PLLA,
- BCM2835_CLK_SRC_PLLC,
- BCM2835_CLK_SRC_PLLD,
- BCM2835_CLK_SRC_HDMI,
-};
-
-/* Most clocks are not useable (freq = 0) */
-static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
- [BCM2835_CLK_SRC_GND] = 0,
- [BCM2835_CLK_SRC_OSC] = 19200000,
- [BCM2835_CLK_SRC_DBG0] = 0,
- [BCM2835_CLK_SRC_DBG1] = 0,
- [BCM2835_CLK_SRC_PLLA] = 0,
- [BCM2835_CLK_SRC_PLLC] = 0,
- [BCM2835_CLK_SRC_PLLD] = 500000000,
- [BCM2835_CLK_SRC_HDMI] = 0,
-};
-
/* I2S registers */
#define BCM2835_I2S_CS_A_REG 0x00
#define BCM2835_I2S_FIFO_A_REG 0x04
@@ -158,10 +110,6 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
#define BCM2835_I2S_INT_RXR BIT(1)
#define BCM2835_I2S_INT_TXW BIT(0)
-/* I2S DMA interface */
-/* FIXME: Needs IOMMU support */
-#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000)
-
/* General device struct */
struct bcm2835_i2s_dev {
struct device *dev;
@@ -169,21 +117,23 @@ struct bcm2835_i2s_dev {
unsigned int fmt;
unsigned int bclk_ratio;
- struct regmap *i2s_regmap;
- struct regmap *clk_regmap;
+ struct regmap *i2s_regmap;
+ struct clk *clk;
+ bool clk_prepared;
};
static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
{
- /* Start the clock if in master mode */
unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ if (dev->clk_prepared)
+ return;
+
switch (master) {
case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM:
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
- BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
+ clk_prepare_enable(dev->clk);
+ dev->clk_prepared = true;
break;
default:
break;
@@ -192,28 +142,9 @@ static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev)
{
- uint32_t clkreg;
- int timeout = 1000;
-
- /* Stop clock */
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
- BCM2835_CLK_PASSWD);
-
- /* Wait for the BUSY flag going down */
- while (--timeout) {
- regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
- if (!(clkreg & BCM2835_CLK_BUSY))
- break;
- }
-
- if (!timeout) {
- /* KILL the clock */
- dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n");
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_KILL | BCM2835_CLK_PASSWD_MASK,
- BCM2835_CLK_KILL | BCM2835_CLK_PASSWD);
- }
+ if (dev->clk_prepared)
+ clk_disable_unprepare(dev->clk);
+ dev->clk_prepared = false;
}
static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
@@ -223,8 +154,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
uint32_t syncval;
uint32_t csreg;
uint32_t i2s_active_state;
- uint32_t clkreg;
- uint32_t clk_active_state;
+ bool clk_was_prepared;
uint32_t off;
uint32_t clr;
@@ -238,15 +168,10 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON);
- regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
- clk_active_state = clkreg & BCM2835_CLK_ENAB;
-
/* Start clock if not running */
- if (!clk_active_state) {
- regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
- BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
- BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
- }
+ clk_was_prepared = dev->clk_prepared;
+ if (!clk_was_prepared)
+ bcm2835_i2s_start_clock(dev);
/* Stop I2S module */
regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0);
@@ -280,7 +205,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
dev_err(dev->dev, "I2S SYNC error!\n");
/* Stop clock if it was not running before */
- if (!clk_active_state)
+ if (!clk_was_prepared)
bcm2835_i2s_stop_clock(dev);
/* Restore I2S state */
@@ -309,19 +234,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
-
unsigned int sampling_rate = params_rate(params);
unsigned int data_length, data_delay, bclk_ratio;
unsigned int ch1pos, ch2pos, mode, format;
- unsigned int mash = BCM2835_CLK_MASH_1;
- unsigned int divi, divf, target_frequency;
- int clk_src = -1;
- unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
- bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS
- || master == SND_SOC_DAIFMT_CBS_CFM);
-
- bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS
- || master == SND_SOC_DAIFMT_CBM_CFS);
uint32_t csreg;
/*
@@ -343,11 +258,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
data_length = 16;
- bclk_ratio = 40;
break;
case SNDRV_PCM_FORMAT_S32_LE:
data_length = 32;
- bclk_ratio = 80;
break;
default:
return -EINVAL;
@@ -356,69 +269,12 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
/* If bclk_ratio already set, use that one. */
if (dev->bclk_ratio)
bclk_ratio = dev->bclk_ratio;
+ else
+ /* otherwise calculate a fitting block ratio */
+ bclk_ratio = 2 * data_length;
- /*
- * Clock Settings
- *
- * The target frequency of the bit clock is
- * sampling rate * frame length
- *
- * Integer mode:
- * Sampling rates that are multiples of 8000 kHz
- * can be driven by the oscillator of 19.2 MHz
- * with an integer divider as long as the frame length
- * is an integer divider of 19200000/8000=2400 as set up above.
- * This is no longer possible if the sampling rate
- * is too high (e.g. 192 kHz), because the oscillator is too slow.
- *
- * MASH mode:
- * For all other sampling rates, it is not possible to
- * have an integer divider. Approximate the clock
- * with the MASH module that induces a slight frequency
- * variance. To minimize that it is best to have the fastest
- * clock here. That is PLLD with 500 MHz.
- */
- target_frequency = sampling_rate * bclk_ratio;
- clk_src = BCM2835_CLK_SRC_OSC;
- mash = BCM2835_CLK_MASH_0;
-
- if (bcm2835_clk_freq[clk_src] % target_frequency == 0
- && bit_master && frame_master) {
- divi = bcm2835_clk_freq[clk_src] / target_frequency;
- divf = 0;
- } else {
- uint64_t dividend;
-
- if (!dev->bclk_ratio) {
- /*
- * Overwrite bclk_ratio, because the
- * above trick is not needed or can
- * not be used.
- */
- bclk_ratio = 2 * data_length;
- }
-
- target_frequency = sampling_rate * bclk_ratio;
-
- clk_src = BCM2835_CLK_SRC_PLLD;
- mash = BCM2835_CLK_MASH_1;
-
- dividend = bcm2835_clk_freq[clk_src];
- dividend <<= BCM2835_CLK_SHIFT;
- do_div(dividend, target_frequency);
- divi = dividend >> BCM2835_CLK_SHIFT;
- divf = dividend & BCM2835_CLK_DIVF_MASK;
- }
-
- /* Set clock divider */
- regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD
- | BCM2835_CLK_DIVI(divi)
- | BCM2835_CLK_DIVF(divf));
-
- /* Setup clock, but don't start it yet */
- regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD
- | BCM2835_CLK_MASH(mash)
- | BCM2835_CLK_SRC(clk_src));
+ /* set target clock rate*/
+ clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
/* Setup the frame format */
format = BCM2835_I2S_CHEN;
@@ -692,7 +548,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
.trigger = bcm2835_i2s_trigger,
.hw_params = bcm2835_i2s_hw_params,
.set_fmt = bcm2835_i2s_set_dai_fmt,
- .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio
+ .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio,
};
static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai)
@@ -750,34 +606,14 @@ static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
};
}
-static bool bcm2835_clk_volatile_reg(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case BCM2835_CLK_PCMCTL_REG:
- return true;
- default:
- return false;
- };
-}
-
-static const struct regmap_config bcm2835_regmap_config[] = {
- {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = BCM2835_I2S_GRAY_REG,
- .precious_reg = bcm2835_i2s_precious_reg,
- .volatile_reg = bcm2835_i2s_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
- },
- {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = BCM2835_CLK_PCMDIV_REG,
- .volatile_reg = bcm2835_clk_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
- },
+static const struct regmap_config bcm2835_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = BCM2835_I2S_GRAY_REG,
+ .precious_reg = bcm2835_i2s_precious_reg,
+ .volatile_reg = bcm2835_i2s_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
};
static const struct snd_soc_component_driver bcm2835_i2s_component = {
@@ -787,42 +623,50 @@ static const struct snd_soc_component_driver bcm2835_i2s_component = {
static int bcm2835_i2s_probe(struct platform_device *pdev)
{
struct bcm2835_i2s_dev *dev;
- int i;
int ret;
- struct regmap *regmap[2];
- struct resource *mem[2];
-
- /* Request both ioareas */
- for (i = 0; i <= 1; i++) {
- void __iomem *base;
-
- mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
- base = devm_ioremap_resource(&pdev->dev, mem[i]);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- regmap[i] = devm_regmap_init_mmio(&pdev->dev, base,
- &bcm2835_regmap_config[i]);
- if (IS_ERR(regmap[i]))
- return PTR_ERR(regmap[i]);
- }
+ struct resource *mem;
+ void __iomem *base;
+ const __be32 *addr;
+ dma_addr_t dma_base;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
GFP_KERNEL);
if (!dev)
return -ENOMEM;
- dev->i2s_regmap = regmap[0];
- dev->clk_regmap = regmap[1];
+ /* get the clock */
+ dev->clk_prepared = false;
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk)) {
+ dev_err(&pdev->dev, "could not get clk: %ld\n",
+ PTR_ERR(dev->clk));
+ return PTR_ERR(dev->clk);
+ }
+
+ /* Request ioarea */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &bcm2835_regmap_config);
+ if (IS_ERR(dev->i2s_regmap))
+ return PTR_ERR(dev->i2s_regmap);
+
+ /* Set the DMA address - we have to parse DT ourselves */
+ addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(&pdev->dev, "could not get DMA-register address\n");
+ return -EINVAL;
+ }
+ dma_base = be32_to_cpup(addr);
- /* Set the DMA address */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
- (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
- + BCM2835_VCMMU_SHIFT;
+ dma_base + BCM2835_I2S_FIFO_A_REG;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
- (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
- + BCM2835_VCMMU_SHIFT;
+ dma_base + BCM2835_I2S_FIFO_A_REG;
/* Set the bus width */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 50693c867e71..649e92a252ae 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -79,7 +79,9 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX98090 if I2C
select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX98357A if GPIOLIB
+ select SND_SOC_MAX9867 if I2C
select SND_SOC_MAX98925 if I2C
+ select SND_SOC_MAX98926 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9768 if I2C
select SND_SOC_MAX9877 if I2C
@@ -87,7 +89,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ML26124 if I2C
select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C
- select SND_SOC_PCM179X if SPI_MASTER
+ select SND_SOC_PCM179X_I2C if I2C
+ select SND_SOC_PCM179X_SPI if SPI_MASTER
select SND_SOC_PCM3008
select SND_SOC_PCM3168A_I2C if I2C
select SND_SOC_PCM3168A_SPI if SPI_MASTER
@@ -95,6 +98,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
select SND_SOC_RT298 if I2C
+ select SND_SOC_RT5514 if I2C
select SND_SOC_RT5616 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
@@ -490,6 +494,7 @@ config SND_SOC_GTM601
config SND_SOC_HDAC_HDMI
tristate
select SND_HDA_EXT_CORE
+ select SND_PCM_ELD
select HDMI
config SND_SOC_ICS43432
@@ -497,6 +502,7 @@ config SND_SOC_ICS43432
config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC"
+ select REGMAP_MMIO
config SND_SOC_ISABELLE
tristate
@@ -516,9 +522,15 @@ config SND_SOC_MAX98095
config SND_SOC_MAX98357A
tristate
+config SND_SOC_MAX9867
+ tristate
+
config SND_SOC_MAX98925
tristate
+config SND_SOC_MAX98926
+ tristate
+
config SND_SOC_MAX9850
tristate
@@ -527,8 +539,23 @@ config SND_SOC_PCM1681
depends on I2C
config SND_SOC_PCM179X
- tristate "Texas Instruments PCM179X CODEC"
+ tristate
+
+config SND_SOC_PCM179X_I2C
+ tristate "Texas Instruments PCM179X CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_PCM179X
+ help
+ Enable support for Texas Instruments PCM179x CODEC.
+ Select this if your PCM179x is connected via an I2C bus.
+
+config SND_SOC_PCM179X_SPI
+ tristate "Texas Instruments PCM179X CODEC (SPI)"
depends on SPI_MASTER
+ select SND_SOC_PCM179X
+ help
+ Enable support for Texas Instruments PCM179x CODEC.
+ Select this if your PCM179x is connected via an SPI bus.
config SND_SOC_PCM3008
tristate
@@ -565,6 +592,7 @@ config SND_SOC_PCM512x_SPI
config SND_SOC_RL6231
tristate
+ default y if SND_SOC_RT5514=y
default y if SND_SOC_RT5616=y
default y if SND_SOC_RT5640=y
default y if SND_SOC_RT5645=y
@@ -572,6 +600,7 @@ config SND_SOC_RL6231
default y if SND_SOC_RT5659=y
default y if SND_SOC_RT5670=y
default y if SND_SOC_RT5677=y
+ default m if SND_SOC_RT5514=m
default m if SND_SOC_RT5616=m
default m if SND_SOC_RT5640=m
default m if SND_SOC_RT5645=m
@@ -595,9 +624,12 @@ config SND_SOC_RT298
tristate
depends on I2C
-config SND_SOC_RT5616
+config SND_SOC_RT5514
tristate
+config SND_SOC_RT5616
+ tristate "Realtek RT5616 CODEC"
+
config SND_SOC_RT5631
tristate "Realtek ALC5631/RT5631 CODEC"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d44f7d347183..185a712a7fe7 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -74,13 +74,17 @@ snd-soc-max98088-objs := max98088.o
snd-soc-max98090-objs := max98090.o
snd-soc-max98095-objs := max98095.o
snd-soc-max98357a-objs := max98357a.o
+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-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
snd-soc-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm179x-codec-objs := pcm179x.o
+snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
+snd-soc-pcm179x-spi-objs := pcm179x-spi.o
snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm3168a-objs := pcm3168a.o
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
@@ -92,6 +96,7 @@ snd-soc-rl6231-objs := rl6231.o
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-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
@@ -278,13 +283,17 @@ obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
+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_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
+obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
@@ -296,6 +305,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
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_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index faae6936bae4..8b1d0c1a7839 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2134,7 +2134,6 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
"%s: ERROR: Unsupporter master mask 0x%x\n",
__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL;
- break;
}
snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 348ccb17d3cc..8de010f758cd 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -1,5 +1,5 @@
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
@@ -44,9 +44,21 @@ static const struct i2c_device_id adau1761_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_i2c_dt_ids[] = {
+ { .compatible = "adi,adau1361", },
+ { .compatible = "adi,adau1461", },
+ { .compatible = "adi,adau1761", },
+ { .compatible = "adi,adau1961", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids);
+#endif
+
static struct i2c_driver adau1761_i2c_driver = {
.driver = {
.name = "adau1761",
+ .of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
},
.probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index 8bc1fbd25fcc..d9171245bd9f 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -1,5 +1,5 @@
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
@@ -61,9 +61,21 @@ static const struct spi_device_id adau1761_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_spi_dt_ids[] = {
+ { .compatible = "adi,adau1361", },
+ { .compatible = "adi,adau1461", },
+ { .compatible = "adi,adau1761", },
+ { .compatible = "adi,adau1961", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids);
+#endif
+
static struct spi_driver adau1761_spi_driver = {
.driver = {
.name = "adau1761",
+ .of_match_table = of_match_ptr(adau1761_spi_dt_ids),
},
.probe = adau1761_spi_probe,
.remove = adau1761_spi_remove,
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 2f12477e539e..b95d29dbd13d 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -1,5 +1,5 @@
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
@@ -456,13 +456,17 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
+ regcache_cache_only(adau->regmap, false);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+ regcache_sync(adau->regmap);
break;
case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+ regcache_cache_only(adau->regmap, true);
break;
}
@@ -783,6 +787,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
+ /* Enable cache only mode as we could miss writes before bias level
+ * reaches standby and the core clock is enabled */
+ regcache_cache_only(regmap, true);
+
return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
}
EXPORT_SYMBOL_GPL(adau1761_probe);
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 0e32bba92339..06cbca84cf02 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -42,9 +42,19 @@ static const struct i2c_device_id adau1781_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_i2c_dt_ids[] = {
+ { .compatible = "adi,adau1381", },
+ { .compatible = "adi,adau1781", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids);
+#endif
+
static struct i2c_driver adau1781_i2c_driver = {
.driver = {
.name = "adau1781",
+ .of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
},
.probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove,
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 33a73ff78de4..3d965a01b99c 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -59,9 +59,19 @@ static const struct spi_device_id adau1781_spi_id[] = {
};
MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_spi_dt_ids[] = {
+ { .compatible = "adi,adau1381", },
+ { .compatible = "adi,adau1781", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids);
+#endif
+
static struct spi_driver adau1781_spi_driver = {
.driver = {
.name = "adau1781",
+ .of_match_table = of_match_ptr(adau1781_spi_dt_ids),
},
.probe = adau1781_spi_probe,
.remove = adau1781_spi_remove,
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index fde9068550a6..bc1bb56dae63 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -1,5 +1,5 @@
/*
- * Driver for ADAU1781/ADAU1781 codec
+ * Driver for ADAU1381/ADAU1781 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index 1222282e93c3..c5be1bdc2c9a 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -20,6 +20,8 @@
#include <sound/initval.h>
#include <sound/soc.h>
+#include <linux/of.h>
+
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
@@ -75,9 +77,19 @@ static int ads117x_remove(struct platform_device *pdev)
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id ads117x_dt_ids[] = {
+ { .compatible = "ti,ads1174" },
+ { .compatible = "ti,ads1178" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads117x_dt_ids);
+#endif
+
static struct platform_driver ads117x_codec_driver = {
.driver = {
.name = "ads117x-codec",
+ .of_match_table = of_match_ptr(ads117x_dt_ids),
},
.probe = ads117x_probe,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 91785318b283..92d22a018d68 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1398,29 +1398,6 @@ static const int arizona_48k_bclk_rates[] = {
24576000,
};
-static const unsigned int arizona_48k_rates[] = {
- 12000,
- 24000,
- 48000,
- 96000,
- 192000,
- 384000,
- 768000,
- 4000,
- 8000,
- 16000,
- 32000,
- 64000,
- 128000,
- 256000,
- 512000,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
- .count = ARRAY_SIZE(arizona_48k_rates),
- .list = arizona_48k_rates,
-};
-
static const int arizona_44k1_bclk_rates[] = {
-1,
44100,
@@ -1443,22 +1420,7 @@ static const int arizona_44k1_bclk_rates[] = {
22579200,
};
-static const unsigned int arizona_44k1_rates[] = {
- 11025,
- 22050,
- 44100,
- 88200,
- 176400,
- 352800,
- 705600,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
- .count = ARRAY_SIZE(arizona_44k1_rates),
- .list = arizona_44k1_rates,
-};
-
-static int arizona_sr_vals[] = {
+static const unsigned int arizona_sr_vals[] = {
0,
12000,
24000,
@@ -1485,13 +1447,21 @@ static int arizona_sr_vals[] = {
512000,
};
+#define ARIZONA_48K_RATE_MASK 0x0F003E
+#define ARIZONA_44K1_RATE_MASK 0x003E00
+#define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK)
+
+static const struct snd_pcm_hw_constraint_list arizona_constraint = {
+ .count = ARRAY_SIZE(arizona_sr_vals),
+ .list = arizona_sr_vals,
+};
+
static int arizona_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
- const struct snd_pcm_hw_constraint_list *constraint;
unsigned int base_rate;
if (!substream->runtime)
@@ -1509,16 +1479,15 @@ static int arizona_startup(struct snd_pcm_substream *substream,
}
if (base_rate == 0)
- return 0;
-
- if (base_rate % 8000)
- constraint = &arizona_44k1_constraint;
+ dai_priv->constraint.mask = ARIZONA_RATE_MASK;
+ else if (base_rate % 8000)
+ dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
else
- constraint = &arizona_48k_constraint;
+ dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
- constraint);
+ &dai_priv->constraint);
}
static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
@@ -1911,6 +1880,7 @@ int arizona_init_dai(struct arizona_priv *priv, int id)
struct arizona_dai_priv *dai_priv = &priv->dai[id];
dai_priv->clk = ARIZONA_CLK_SYSCLK;
+ dai_priv->constraint = arizona_constraint;
return 0;
}
@@ -2179,11 +2149,12 @@ static int arizona_calc_fll(struct arizona_fll *fll,
return -EINVAL;
}
- arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
+ arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
cfg->n, cfg->theta, cfg->lambda);
- arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
- cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
- arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
+ arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
+ cfg->fratio, ratio, cfg->outdiv,
+ cfg->refdiv, 1 << cfg->refdiv);
+ arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
return 0;
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 8b6adb5419bb..1ea8e4ecf8d4 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -57,7 +57,7 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
-#define ARIZONA_MAX_DAI 8
+#define ARIZONA_MAX_DAI 10
#define ARIZONA_MAX_ADSP 4
#define ARIZONA_DVFS_SR1_RQ 0x001
@@ -68,6 +68,8 @@ struct wm_adsp;
struct arizona_dai_priv {
int clk;
+
+ struct snd_pcm_hw_constraint_list constraint;
};
struct arizona_priv {
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index d562e1b9a5d1..1179101b2b05 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -44,6 +44,7 @@ struct cs42xx8_priv {
bool slave_mode;
unsigned long sysclk;
+ u32 tx_channels;
};
/* -127.5dB to 0dB with step of 0.5dB */
@@ -257,6 +258,9 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
u32 ratio = cs42xx8->sysclk / params_rate(params);
u32 i, fm, val, mask;
+ if (tx)
+ cs42xx8->tx_channels = params_channels(params);
+
for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
if (cs42xx8_ratios[i].ratio == ratio)
break;
@@ -283,9 +287,11 @@ static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ u8 dac_unmute = cs42xx8->tx_channels ?
+ ~((0x1 << cs42xx8->tx_channels) - 1) : 0;
- regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE,
- CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
+ regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE,
+ mute ? CS42XX8_DACMUTE_ALL : dac_unmute);
return 0;
}
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index dc5ae7f7a1bd..576087bda330 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -57,6 +57,25 @@ static const struct wm_adsp_region *cs47l24_dsp_regions[] = {
cs47l24_dsp3_regions,
};
+static int cs47l24_adsp_power_ev(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 arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
+ return ret;
+ }
+
+ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+ return wm_adsp2_early_event(w, kcontrol, event, v);
+}
+
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
@@ -405,8 +424,8 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
NULL, 0),
-WM_ADSP2("DSP2", 1),
-WM_ADSP2("DSP3", 2),
+WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev),
SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@@ -779,6 +798,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "Voice Control DSP", NULL, "DSP3" },
+ { "Voice Control DSP", NULL, "SYSCLK" },
+
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -901,7 +923,7 @@ static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000
+#define CS47L24_RATES SNDRV_PCM_RATE_KNOT
#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -973,12 +995,68 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.symmetric_rates = 1,
.symmetric_samplebits = 1,
},
+ {
+ .name = "cs47l24-cpu-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control CPU",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "cs47l24-dsp-voicectrl",
+ .capture = {
+ .stream_name = "Voice Control DSP",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS47L24_RATES,
+ .formats = CS47L24_FORMATS,
+ },
+ },
};
+static int cs47l24_open(struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct arizona *arizona = priv->core.arizona;
+ int n_adsp;
+
+ if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else {
+ dev_err(arizona->dev,
+ "No suitable compressed stream for DAI '%s'\n",
+ rtd->codec_dai->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
+{
+ struct cs47l24_priv *priv = data;
+ struct arizona *arizona = priv->core.arizona;
+ int ret;
+
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]);
+ if (ret == -ENODEV) {
+ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
static int cs47l24_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->core.arizona;
int ret;
priv->core.arizona->dapm = dapm;
@@ -987,6 +1065,14 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
+ priv);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
if (ret)
goto err_adsp2_codec_probe;
@@ -1014,13 +1100,14 @@ err_adsp2_codec_probe:
static int cs47l24_codec_remove(struct snd_soc_codec *codec)
{
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
-
+ struct arizona *arizona = priv->core.arizona;
wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
priv->core.arizona->dapm = NULL;
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
return 0;
}
@@ -1057,6 +1144,19 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
.num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes),
};
+static struct snd_compr_ops cs47l24_compr_ops = {
+ .open = cs47l24_open,
+ .free = wm_adsp_compr_free,
+ .set_params = wm_adsp_compr_set_params,
+ .get_caps = wm_adsp_compr_get_caps,
+ .trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
+};
+
+static struct snd_soc_platform_driver cs47l24_compr_platform = {
+ .compr_ops = &cs47l24_compr_ops,
+};
static int cs47l24_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -1120,12 +1220,25 @@ static int cs47l24_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
+ ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
+ return ret;
+ }
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+ snd_soc_unregister_platform(&pdev->dev);
+ }
+
+ return ret;
}
static int cs47l24_remove(struct platform_device *pdev)
{
+ snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 5a1ec0f7a1a6..26f9459cb3bc 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -22,11 +22,17 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/hdmi.h>
+#include <drm/drm_edid.h>
#include <sound/pcm_params.h>
+#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/hdaudio_ext.h>
#include <sound/hda_i915.h>
+#include <sound/pcm_drm_eld.h>
#include "../../hda/local.h"
+#include "hdac_hdmi.h"
+
+#define NAME_SIZE 32
#define AMP_OUT_MUTE 0xb080
#define AMP_OUT_UNMUTE 0xb000
@@ -34,6 +40,11 @@
#define HDA_MAX_CONNECTIONS 32
+#define HDA_MAX_CVTS 3
+
+#define ELD_MAX_SIZE 256
+#define ELD_FIXED_BYTES 20
+
struct hdac_hdmi_cvt_params {
unsigned int channels_min;
unsigned int channels_max;
@@ -45,14 +56,34 @@ struct hdac_hdmi_cvt_params {
struct hdac_hdmi_cvt {
struct list_head head;
hda_nid_t nid;
+ const char *name;
struct hdac_hdmi_cvt_params params;
};
+struct hdac_hdmi_eld {
+ bool monitor_present;
+ bool eld_valid;
+ int eld_size;
+ char eld_buffer[ELD_MAX_SIZE];
+};
+
struct hdac_hdmi_pin {
struct list_head head;
hda_nid_t nid;
int num_mux_nids;
hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+ struct hdac_hdmi_eld eld;
+ struct hdac_ext_device *edev;
+ int repoll_count;
+ struct delayed_work work;
+};
+
+struct hdac_hdmi_pcm {
+ struct list_head head;
+ int pcm_id;
+ struct hdac_hdmi_pin *pin;
+ struct hdac_hdmi_cvt *cvt;
+ struct snd_jack *jack;
};
struct hdac_hdmi_dai_pin_map {
@@ -62,11 +93,13 @@ struct hdac_hdmi_dai_pin_map {
};
struct hdac_hdmi_priv {
- struct hdac_hdmi_dai_pin_map dai_map[3];
+ struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS];
struct list_head pin_list;
struct list_head cvt_list;
+ struct list_head pcm_list;
int num_pin;
int num_cvt;
+ struct mutex pin_mutex;
};
static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
@@ -76,6 +109,119 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
return to_ehdac_device(hdac);
}
+static unsigned int sad_format(const u8 *sad)
+{
+ return ((sad[0] >> 0x3) & 0x1f);
+}
+
+static unsigned int sad_sample_bits_lpcm(const u8 *sad)
+{
+ return (sad[2] & 7);
+}
+
+static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
+ void *eld)
+{
+ u64 formats = SNDRV_PCM_FMTBIT_S16;
+ int i;
+ const u8 *sad, *eld_buf = eld;
+
+ sad = drm_eld_sad(eld_buf);
+ if (!sad)
+ goto format_constraint;
+
+ for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
+ if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
+
+ /*
+ * the controller support 20 and 24 bits in 32 bit
+ * container so we set S32
+ */
+ if (sad_sample_bits_lpcm(sad) & 0x6)
+ formats |= SNDRV_PCM_FMTBIT_S32;
+ }
+ }
+
+format_constraint:
+ return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+ formats);
+
+}
+
+ /* HDMI ELD routines */
+static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
+ hda_nid_t nid, int byte_index)
+{
+ unsigned int val;
+
+ val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
+ byte_index);
+
+ dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
+ byte_index, val);
+
+ return val;
+}
+
+static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
+{
+ return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+ AC_DIPSIZE_ELD_BUF);
+}
+
+/*
+ * This function queries the ELD size and ELD data and fills in the buffer
+ * passed by user
+ */
+static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
+ unsigned char *buf, int *eld_size)
+{
+ int i, size, ret = 0;
+
+ /*
+ * ELD size is initialized to zero in caller function. If no errors and
+ * ELD is valid, actual eld_size is assigned.
+ */
+
+ size = hdac_hdmi_get_eld_size(codec, nid);
+ if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+ dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
+ return -ERANGE;
+ }
+
+ /* set ELD buffer */
+ for (i = 0; i < size; i++) {
+ unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
+ /*
+ * Graphics driver might be writing to ELD buffer right now.
+ * Just abort. The caller will repoll after a while.
+ */
+ if (!(val & AC_ELDD_ELD_VALID)) {
+ dev_err(&codec->dev,
+ "HDMI: invalid ELD data byte %d\n", i);
+ ret = -EINVAL;
+ goto error;
+ }
+ val &= AC_ELDD_ELD_DATA;
+ /*
+ * The first byte cannot be zero. This can happen on some DVI
+ * connections. Some Intel chips may also need some 250ms delay
+ * to return non-zero ELD data, even when the graphics driver
+ * correctly writes ELD content before setting ELD_valid bit.
+ */
+ if (!val && !i) {
+ dev_err(&codec->dev, "HDMI: 0 ELD data\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ buf[i] = val;
+ }
+
+ *eld_size = size;
+error:
+ return ret;
+}
+
static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
hda_nid_t cvt_nid, hda_nid_t pin_nid,
u32 stream_tag, int format)
@@ -107,27 +253,74 @@ hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
AC_VERB_SET_HDMI_DIP_INDEX, val);
}
+struct dp_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 len; /* 0x1b */
+ u8 ver; /* 0x11 << 2 */
+
+ u8 CC02_CT47; /* match with HDMI infoframe from this on */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
+};
+
static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
hda_nid_t cvt_nid, hda_nid_t pin_nid)
{
uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
struct hdmi_audio_infoframe frame;
- u8 *dip = (u8 *)&frame;
+ struct dp_audio_infoframe dp_ai;
+ struct hdac_hdmi_priv *hdmi = hdac->private_data;
+ struct hdac_hdmi_pin *pin;
+ u8 *dip;
int ret;
int i;
+ const u8 *eld_buf;
+ u8 conn_type;
+ int channels = 2;
- hdmi_audio_infoframe_init(&frame);
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ if (pin->nid == pin_nid)
+ break;
+ }
- /* Default stereo for now */
- frame.channels = 2;
+ eld_buf = pin->eld.eld_buffer;
+ conn_type = drm_eld_get_conn_type(eld_buf);
/* setup channel count */
snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
- AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
+ AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
- ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
- if (ret < 0)
- return ret;
+ switch (conn_type) {
+ case DRM_ELD_CONN_TYPE_HDMI:
+ hdmi_audio_infoframe_init(&frame);
+
+ /* Default stereo for now */
+ frame.channels = channels;
+
+ ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case DRM_ELD_CONN_TYPE_DP:
+ memset(&dp_ai, 0, sizeof(dp_ai));
+ dp_ai.type = 0x84;
+ dp_ai.len = 0x1b;
+ dp_ai.ver = 0x11 << 2;
+ dp_ai.CC02_CT47 = channels - 1;
+ dp_ai.CA = 0;
+
+ dip = (u8 *)&dp_ai;
+ break;
+
+ default:
+ dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n",
+ conn_type);
+ return -EIO;
+ }
/* stop infoframe transmission */
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
@@ -137,9 +330,15 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
/* Fill infoframe. Index auto-incremented */
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(frame); i++)
- snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+ if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
+ for (i = 0; i < sizeof(buffer); i++)
+ snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+ AC_VERB_SET_HDMI_DIP_DATA, buffer[i]);
+ } else {
+ for (i = 0; i < sizeof(dp_ai); i++)
+ snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
+ }
/* Start infoframe */
hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
@@ -174,11 +373,6 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
struct hdac_ext_dma_params *dd;
int ret;
- if (dai->id > 0) {
- dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
- return -ENODEV;
- }
-
dai_map = &hdmi->dai_map[dai->id];
dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
@@ -198,16 +392,30 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
{
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
+ struct hdac_hdmi_priv *hdmi = hdac->private_data;
+ struct hdac_hdmi_dai_pin_map *dai_map;
+ struct hdac_hdmi_pin *pin;
struct hdac_ext_dma_params *dd;
- if (dai->id > 0) {
- dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
+ dai_map = &hdmi->dai_map[dai->id];
+ pin = dai_map->pin;
+
+ if (!pin)
+ return -ENODEV;
+
+ if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) {
+ dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n",
+ pin->nid);
return -ENODEV;
}
- dd = kzalloc(sizeof(*dd), GFP_KERNEL);
- if (!dd)
- return -ENOMEM;
+ dd = snd_soc_dai_get_dma_data(dai, substream);
+ if (!dd) {
+ dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+ if (!dd)
+ return -ENOMEM;
+ }
+
dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
params_channels(hparams), params_format(hparams),
24, 0);
@@ -227,50 +435,187 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id];
+ dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
+
+ if (dd) {
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+ kfree(dd);
+ }
+
+ return 0;
+}
+
+static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
+ struct hdac_hdmi_dai_pin_map *dai_map)
+{
+ /* Enable transmission */
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID, 0);
+ AC_VERB_SET_DIGI_CONVERT_1, 1);
+
+ /* Category Code (CC) to zero */
snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
- AC_VERB_SET_STREAM_FORMAT, 0);
+ AC_VERB_SET_DIGI_CONVERT_2, 0);
+}
- dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
- snd_soc_dai_set_dma_data(dai, substream, NULL);
+static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
+ struct hdac_hdmi_dai_pin_map *dai_map)
+{
+ int mux_idx;
+ struct hdac_hdmi_pin *pin = dai_map->pin;
+
+ for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
+ if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
+ snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+ AC_VERB_SET_CONNECT_SEL, mux_idx);
+ break;
+ }
+ }
+
+ if (mux_idx == pin->num_mux_nids)
+ return -EIO;
+
+ /* Enable out path for this pin widget */
+ snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
- kfree(dd);
+ hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+
+ snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
return 0;
}
+static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
+ struct hdac_hdmi_pin *pin)
+{
+ if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
+ dev_warn(&hdac->hdac.dev,
+ "HDMI: pin %d wcaps %#x does not support connection list\n",
+ pin->nid, get_wcaps(&hdac->hdac, pin->nid));
+ return -EINVAL;
+ }
+
+ pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
+ pin->mux_nids, HDA_MAX_CONNECTIONS);
+ if (pin->num_mux_nids == 0)
+ dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n",
+ pin->nid);
+
+ dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n",
+ pin->num_mux_nids, pin->nid);
+
+ return pin->num_mux_nids;
+}
+
+/*
+ * Query pcm list and return pin widget to which stream is routed.
+ *
+ * Also query connection list of the pin, to validate the cvt to pin map.
+ *
+ * Same stream rendering to multiple pins simultaneously can be done
+ * possibly, but not supported for now in driver. So return the first pin
+ * connected.
+ */
+static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
+ struct hdac_ext_device *edev,
+ struct hdac_hdmi_priv *hdmi,
+ struct hdac_hdmi_cvt *cvt)
+{
+ struct hdac_hdmi_pcm *pcm;
+ struct hdac_hdmi_pin *pin = NULL;
+ int ret, i;
+
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->cvt == cvt) {
+ pin = pcm->pin;
+ break;
+ }
+ }
+
+ if (pin) {
+ ret = hdac_hdmi_query_pin_connlist(edev, pin);
+ if (ret < 0)
+ return NULL;
+
+ for (i = 0; i < pin->num_mux_nids; i++) {
+ if (pin->mux_nids[i] == cvt->nid)
+ return pin;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * This tries to get a valid pin and set the HW constraints based on the
+ * ELD. Even if a valid pin is not found return success so that device open
+ * doesn't fail.
+ */
static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
struct hdac_hdmi_priv *hdmi = hdac->private_data;
struct hdac_hdmi_dai_pin_map *dai_map;
- int val;
-
- if (dai->id > 0) {
- dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
- return -ENODEV;
- }
+ struct hdac_hdmi_cvt *cvt;
+ struct hdac_hdmi_pin *pin;
+ int ret;
dai_map = &hdmi->dai_map[dai->id];
- val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
- AC_VERB_GET_PIN_SENSE, 0);
- dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
+ cvt = dai_map->cvt;
+ pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
- if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
- dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
- return -ENODEV;
+ /*
+ * To make PA and other userland happy.
+ * userland scans devices so returning error does not help.
+ */
+ if (!pin)
+ return 0;
+
+ if ((!pin->eld.monitor_present) ||
+ (!pin->eld.eld_valid)) {
+
+ dev_warn(&hdac->hdac.dev,
+ "Failed: montior present? %d ELD valid?: %d for pin: %d\n",
+ pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
+
+ return 0;
}
- hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+ dai_map->pin = pin;
- snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+ hdac_hdmi_enable_cvt(hdac, dai_map);
+ ret = hdac_hdmi_enable_pin(hdac, dai_map);
+ if (ret < 0)
+ return ret;
- snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ ret = hdac_hdmi_eld_limit_formats(substream->runtime,
+ pin->eld.eld_buffer);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_eld(substream->runtime,
+ pin->eld.eld_buffer);
+}
+
+static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct hdac_hdmi_dai_pin_map *dai_map;
+ struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
+ struct hdac_hdmi_priv *hdmi = hdac->private_data;
+ int ret;
+
+ dai_map = &hdmi->dai_map[dai->id];
+ if (cmd == SNDRV_PCM_TRIGGER_RESUME) {
+ ret = hdac_hdmi_enable_pin(hdac, dai_map);
+ if (ret < 0)
+ return ret;
+
+ return hdac_hdmi_playback_prepare(substream, dai);
+ }
return 0;
}
@@ -284,10 +629,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
dai_map = &hdmi->dai_map[dai->id];
- hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
+ if (dai_map->pin) {
+ snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, 0);
+ snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
+ AC_VERB_SET_STREAM_FORMAT, 0);
+
+ hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
- snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
+ snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ dai_map->pin = NULL;
+ }
}
static int
@@ -310,85 +664,326 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
return err;
}
-static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
- enum snd_soc_dapm_type id,
- const char *wname, const char *stream)
+static int hdac_hdmi_fill_widget_info(struct device *dev,
+ struct snd_soc_dapm_widget *w,
+ enum snd_soc_dapm_type id, void *priv,
+ const char *wname, const char *stream,
+ struct snd_kcontrol_new *wc, int numkc)
{
w->id = id;
- w->name = wname;
+ w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
+ if (!w->name)
+ return -ENOMEM;
+
w->sname = stream;
w->reg = SND_SOC_NOPM;
w->shift = 0;
- w->kcontrol_news = NULL;
- w->num_kcontrols = 0;
- w->priv = NULL;
+ w->kcontrol_news = wc;
+ w->num_kcontrols = numkc;
+ w->priv = priv;
+
+ return 0;
}
static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
- const char *sink, const char *control, const char *src)
+ const char *sink, const char *control, const char *src,
+ int (*handler)(struct snd_soc_dapm_widget *src,
+ struct snd_soc_dapm_widget *sink))
{
route->sink = sink;
route->source = src;
route->control = control;
- route->connected = NULL;
+ route->connected = handler;
}
-static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
- struct hdac_hdmi_dai_pin_map *dai_map)
+static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
+ struct hdac_hdmi_pin *pin)
{
- struct snd_soc_dapm_route route[1];
- struct snd_soc_dapm_widget widgets[2] = { {0} };
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pcm *pcm = NULL;
- memset(&route, 0, sizeof(route));
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->pin == pin)
+ return pcm;
+ }
- hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
- "hif1 Output", NULL);
- hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
- "Coverter 1", "hif1");
+ return NULL;
+}
- hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
+/*
+ * Based on user selection, map the PINs with the PCMs.
+ */
+static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct hdac_hdmi_pin *pin = w->priv;
+ struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pcm *pcm = NULL;
+ const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
- snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
- snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&hdmi->pin_mutex);
+ list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+ if (pcm->pin == pin)
+ pcm->pin = NULL;
+
+ /*
+ * Jack status is not reported during device probe as the
+ * PCMs are not registered by then. So report it here.
+ */
+ if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) {
+ pcm->pin = pin;
+ if (pin->eld.monitor_present && pin->eld.eld_valid) {
+ dev_dbg(&edev->hdac.dev,
+ "jack report for pcm=%d\n",
+ pcm->pcm_id);
+
+ snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+ }
+ mutex_unlock(&hdmi->pin_mutex);
+ return ret;
+ }
+ }
+ mutex_unlock(&hdmi->pin_mutex);
+
+ return ret;
}
-static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
+/*
+ * Ideally the Mux inputs should be based on the num_muxs enumerated, but
+ * the display driver seem to be programming the connection list for the pin
+ * widget runtime.
+ *
+ * So programming all the possible inputs for the mux, the user has to take
+ * care of selecting the right one and leaving all other inputs selected to
+ * "NONE"
+ */
+static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
+ struct hdac_hdmi_pin *pin,
+ struct snd_soc_dapm_widget *widget,
+ const char *widget_name)
+{
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct snd_kcontrol_new *kc;
+ struct hdac_hdmi_cvt *cvt;
+ struct soc_enum *se;
+ char kc_name[NAME_SIZE];
+ char mux_items[NAME_SIZE];
+ /* To hold inputs to the Pin mux */
+ char *items[HDA_MAX_CONNECTIONS];
+ int i = 0;
+ int num_items = hdmi->num_cvt + 1;
+
+ kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL);
+ if (!kc)
+ return -ENOMEM;
+
+ se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL);
+ if (!se)
+ return -ENOMEM;
+
+ sprintf(kc_name, "Pin %d Input", pin->nid);
+ kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);
+ if (!kc->name)
+ return -ENOMEM;
+
+ kc->private_value = (long)se;
+ kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kc->access = 0;
+ kc->info = snd_soc_info_enum_double;
+ kc->put = hdac_hdmi_set_pin_mux;
+ kc->get = snd_soc_dapm_get_enum_double;
+
+ se->reg = SND_SOC_NOPM;
+
+ /* enum texts: ["NONE", "cvt #", "cvt #", ...] */
+ se->items = num_items;
+ se->mask = roundup_pow_of_two(se->items) - 1;
+
+ sprintf(mux_items, "NONE");
+ items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
+ if (!items[i])
+ return -ENOMEM;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ i++;
+ sprintf(mux_items, "cvt %d", cvt->nid);
+ items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
+ if (!items[i])
+ return -ENOMEM;
+ }
+
+ se->texts = devm_kmemdup(&edev->hdac.dev, items,
+ (num_items * sizeof(char *)), GFP_KERNEL);
+ if (!se->texts)
+ return -ENOMEM;
+
+ return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
+ snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
+}
+
+/* Add cvt <- input <- mux route map */
+static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
+ struct snd_soc_dapm_widget *widgets,
+ struct snd_soc_dapm_route *route, int rindex)
+{
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ const struct snd_kcontrol_new *kc;
+ struct soc_enum *se;
+ int mux_index = hdmi->num_cvt + hdmi->num_pin;
+ int i, j;
+
+ for (i = 0; i < hdmi->num_pin; i++) {
+ kc = widgets[mux_index].kcontrol_news;
+ se = (struct soc_enum *)kc->private_value;
+ for (j = 0; j < hdmi->num_cvt; j++) {
+ hdac_hdmi_fill_route(&route[rindex],
+ widgets[mux_index].name,
+ se->texts[j + 1],
+ widgets[j].name, NULL);
+
+ rindex++;
+ }
+
+ mux_index++;
+ }
+}
+
+/*
+ * Widgets are added in the below sequence
+ * Converter widgets for num converters enumerated
+ * Pin widgets for num pins enumerated
+ * Pin mux widgets to represent connenction list of pin widget
+ *
+ * Total widgets elements = num_cvt + num_pin + num_pin;
+ *
+ * Routes are added as below:
+ * pin mux -> pin (based on num_pins)
+ * cvt -> "Input sel control" -> pin_mux
+ *
+ * Total route elements:
+ * num_pins + (pin_muxes * num_cvt)
+ */
+static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
{
+ struct snd_soc_dapm_widget *widgets;
+ struct snd_soc_dapm_route *route;
+ struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
struct hdac_hdmi_priv *hdmi = edev->private_data;
- struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
+ struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv;
+ char widget_name[NAME_SIZE];
struct hdac_hdmi_cvt *cvt;
struct hdac_hdmi_pin *pin;
+ int ret, i = 0, num_routes = 0;
if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
return -EINVAL;
- /*
- * Currently on board only 1 pin and 1 converter is enabled for
- * simplification, more will be added eventually
- * So using fixed map for dai_id:pin:cvt
- */
- cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
- pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
+ widgets = devm_kzalloc(dapm->dev,
+ (sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)),
+ GFP_KERNEL);
- dai_map->dai_id = 0;
- dai_map->pin = pin;
+ if (!widgets)
+ return -ENOMEM;
- dai_map->cvt = cvt;
+ /* DAPM widgets to represent each converter widget */
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ sprintf(widget_name, "Converter %d", cvt->nid);
+ ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+ snd_soc_dapm_aif_in, &cvt->nid,
+ widget_name, dai_drv[i].playback.stream_name, NULL, 0);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
- /* Enable out path for this pin widget */
- snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ sprintf(widget_name, "hif%d Output", pin->nid);
+ ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+ snd_soc_dapm_output, &pin->nid,
+ widget_name, NULL, NULL, 0);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
- /* Enable transmission */
- snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
- AC_VERB_SET_DIGI_CONVERT_1, 1);
+ /* DAPM widgets to represent the connection list to pin widget */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ sprintf(widget_name, "Pin %d Mux", pin->nid);
+ ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i],
+ widget_name);
+ if (ret < 0)
+ return ret;
+ i++;
- /* Category Code (CC) to zero */
- snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
- AC_VERB_SET_DIGI_CONVERT_2, 0);
+ /* For cvt to pin_mux mapping */
+ num_routes += hdmi->num_cvt;
+
+ /* For pin_mux to pin mapping */
+ num_routes++;
+ }
- snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
- AC_VERB_SET_CONNECT_SEL, 0);
+ route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
+ GFP_KERNEL);
+ if (!route)
+ return -ENOMEM;
+
+ i = 0;
+ /* Add pin <- NULL <- mux route map */
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ int sink_index = i + hdmi->num_cvt;
+ int src_index = sink_index + hdmi->num_pin;
+
+ hdac_hdmi_fill_route(&route[i],
+ widgets[sink_index].name, NULL,
+ widgets[src_index].name, NULL);
+ i++;
+
+ }
+
+ hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);
+
+ snd_soc_dapm_new_controls(dapm, widgets,
+ ((2 * hdmi->num_pin) + hdmi->num_cvt));
+
+ snd_soc_dapm_add_routes(dapm, route, num_routes);
+ snd_soc_dapm_new_widgets(dapm->card);
+
+ return 0;
+
+}
+
+static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
+{
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_dai_pin_map *dai_map;
+ struct hdac_hdmi_cvt *cvt;
+ int dai_id = 0;
+
+ if (list_empty(&hdmi->cvt_list))
+ return -EINVAL;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ dai_map = &hdmi->dai_map[dai_id];
+ dai_map->dai_id = dai_id;
+ dai_map->cvt = cvt;
+
+ dai_id++;
+
+ if (dai_id == HDA_MAX_CVTS) {
+ dev_warn(&edev->hdac.dev,
+ "Max dais supported: %d\n", dai_id);
+ break;
+ }
+ }
return 0;
}
@@ -397,12 +992,15 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
{
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_cvt *cvt;
+ char name[NAME_SIZE];
cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
if (!cvt)
return -ENOMEM;
cvt->nid = nid;
+ sprintf(name, "cvt %d", cvt->nid);
+ cvt->name = kstrdup(name, GFP_KERNEL);
list_add_tail(&cvt->head, &hdmi->cvt_list);
hdmi->num_cvt++;
@@ -410,6 +1008,106 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
}
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
+{
+ struct hdac_ext_device *edev = pin->edev;
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pcm *pcm;
+ int val;
+
+ pin->repoll_count = repoll;
+
+ pm_runtime_get_sync(&edev->hdac.dev);
+ val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+
+ dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
+ val, pin->nid);
+
+
+ mutex_lock(&hdmi->pin_mutex);
+ pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
+ pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
+
+ pcm = hdac_hdmi_get_pcm(edev, pin);
+
+ if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
+
+ dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
+ __func__, pin->nid);
+
+ /*
+ * PCMs are not registered during device probe, so don't
+ * report jack here. It will be done in usermode mux
+ * control select.
+ */
+ if (pcm) {
+ dev_dbg(&edev->hdac.dev,
+ "jack report for pcm=%d\n", pcm->pcm_id);
+
+ snd_jack_report(pcm->jack, 0);
+ }
+
+ mutex_unlock(&hdmi->pin_mutex);
+ goto put_hdac_device;
+ }
+
+ if (pin->eld.monitor_present && pin->eld.eld_valid) {
+ /* TODO: use i915 component for reading ELD later */
+ if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
+ pin->eld.eld_buffer,
+ &pin->eld.eld_size) == 0) {
+
+ if (pcm) {
+ dev_dbg(&edev->hdac.dev,
+ "jack report for pcm=%d\n",
+ pcm->pcm_id);
+
+ snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+ }
+
+ print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
+ pin->eld.eld_buffer, pin->eld.eld_size);
+ } else {
+ pin->eld.monitor_present = false;
+ pin->eld.eld_valid = false;
+
+ if (pcm) {
+ dev_dbg(&edev->hdac.dev,
+ "jack report for pcm=%d\n",
+ pcm->pcm_id);
+
+ snd_jack_report(pcm->jack, 0);
+ }
+ }
+ }
+
+ mutex_unlock(&hdmi->pin_mutex);
+
+ /*
+ * Sometimes the pin_sense may present invalid monitor
+ * present and eld_valid. If ELD data is not valid, loop few
+ * more times to get correct pin sense and valid ELD.
+ */
+ if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
+ schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
+
+put_hdac_device:
+ pm_runtime_put_sync(&edev->hdac.dev);
+}
+
+static void hdac_hdmi_repoll_eld(struct work_struct *work)
+{
+ struct hdac_hdmi_pin *pin =
+ container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
+
+ /* picked from legacy HDA driver */
+ if (pin->repoll_count++ > 6)
+ pin->repoll_count = 0;
+
+ hdac_hdmi_present_sense(pin, pin->repoll_count);
+}
+
static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
{
struct hdac_hdmi_priv *hdmi = edev->private_data;
@@ -424,6 +1122,120 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
list_add_tail(&pin->head, &hdmi->pin_list);
hdmi->num_pin++;
+ pin->edev = edev;
+ INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
+
+ return 0;
+}
+
+#define INTEL_VENDOR_NID 0x08
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac)
+{
+ unsigned int vendor_param;
+
+ vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+ return;
+
+ vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+ vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+}
+
+static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac)
+{
+ unsigned int vendor_param;
+
+ vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+ INTEL_GET_VENDOR_VERB, 0);
+ if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+ return;
+
+ /* enable DP1.2 mode */
+ vendor_param |= INTEL_EN_DP12;
+ vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+ INTEL_SET_VENDOR_VERB, vendor_param);
+ if (vendor_param == -1)
+ return;
+
+}
+
+static struct snd_soc_dai_ops hdmi_dai_ops = {
+ .startup = hdac_hdmi_pcm_open,
+ .shutdown = hdac_hdmi_pcm_close,
+ .hw_params = hdac_hdmi_set_hw_params,
+ .prepare = hdac_hdmi_playback_prepare,
+ .trigger = hdac_hdmi_trigger,
+ .hw_free = hdac_hdmi_playback_cleanup,
+};
+
+/*
+ * Each converter can support a stream independently. So a dai is created
+ * based on the number of converter queried.
+ */
+static int hdac_hdmi_create_dais(struct hdac_device *hdac,
+ struct snd_soc_dai_driver **dais,
+ struct hdac_hdmi_priv *hdmi, int num_dais)
+{
+ struct snd_soc_dai_driver *hdmi_dais;
+ struct hdac_hdmi_cvt *cvt;
+ char name[NAME_SIZE], dai_name[NAME_SIZE];
+ int i = 0;
+ u32 rates, bps;
+ unsigned int rate_max = 384000, rate_min = 8000;
+ u64 formats;
+ int ret;
+
+ hdmi_dais = devm_kzalloc(&hdac->dev,
+ (sizeof(*hdmi_dais) * num_dais),
+ GFP_KERNEL);
+ if (!hdmi_dais)
+ return -ENOMEM;
+
+ list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+ ret = snd_hdac_query_supported_pcm(hdac, cvt->nid,
+ &rates, &formats, &bps);
+ if (ret)
+ return ret;
+
+ sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
+ hdmi_dais[i].name = devm_kstrdup(&hdac->dev,
+ dai_name, GFP_KERNEL);
+
+ if (!hdmi_dais[i].name)
+ return -ENOMEM;
+
+ snprintf(name, sizeof(name), "hifi%d", i+1);
+ hdmi_dais[i].playback.stream_name =
+ devm_kstrdup(&hdac->dev, name, GFP_KERNEL);
+ if (!hdmi_dais[i].playback.stream_name)
+ return -ENOMEM;
+
+ /*
+ * Set caps based on capability queried from the converter.
+ * It will be constrained runtime based on ELD queried.
+ */
+ hdmi_dais[i].playback.formats = formats;
+ hdmi_dais[i].playback.rates = rates;
+ hdmi_dais[i].playback.rate_max = rate_max;
+ hdmi_dais[i].playback.rate_min = rate_min;
+ hdmi_dais[i].playback.channels_min = 2;
+ hdmi_dais[i].playback.channels_max = 2;
+ hdmi_dais[i].ops = &hdmi_dai_ops;
+
+ i++;
+ }
+
+ *dais = hdmi_dais;
+
return 0;
}
@@ -431,7 +1243,8 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
* Parse all nodes and store the cvt/pin nids in array
* Add one time initialization for pin and cvt widgets
*/
-static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
+static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
+ struct snd_soc_dai_driver **dais, int *num_dais)
{
hda_nid_t nid;
int i, num_nodes;
@@ -439,6 +1252,9 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
struct hdac_hdmi_priv *hdmi = edev->private_data;
int ret;
+ hdac_hdmi_skl_enable_all_pins(hdac);
+ hdac_hdmi_skl_enable_dp12(hdac);
+
num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
if (!nid || num_nodes <= 0) {
dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
@@ -479,19 +1295,107 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
if (!hdmi->num_pin || !hdmi->num_cvt)
return -EIO;
+ ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt);
+ if (ret) {
+ dev_err(&hdac->dev, "Failed to create dais with err: %d\n",
+ ret);
+ return ret;
+ }
+
+ *num_dais = hdmi->num_cvt;
+
return hdac_hdmi_init_dai_map(edev);
}
+static void hdac_hdmi_eld_notify_cb(void *aptr, int port)
+{
+ struct hdac_ext_device *edev = aptr;
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pin *pin;
+ struct snd_soc_codec *codec = edev->scodec;
+
+ /* Don't know how this mapping is derived */
+ hda_nid_t pin_nid = port + 0x04;
+
+ dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
+
+ /*
+ * skip notification during system suspend (but not in runtime PM);
+ * the state will be updated at resume. Also since the ELD and
+ * connection states are updated in anyway at the end of the resume,
+ * we can skip it when received during PM process.
+ */
+ if (snd_power_get_state(codec->component.card->snd_card) !=
+ SNDRV_CTL_POWER_D0)
+ return;
+
+ if (atomic_read(&edev->hdac.in_pm))
+ return;
+
+ list_for_each_entry(pin, &hdmi->pin_list, head) {
+ if (pin->nid == pin_nid)
+ hdac_hdmi_present_sense(pin, 1);
+ }
+}
+
+static struct i915_audio_component_audio_ops aops = {
+ .pin_eld_notify = hdac_hdmi_eld_notify_cb,
+};
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
+{
+ char jack_name[NAME_SIZE];
+ struct snd_soc_codec *codec = dai->codec;
+ struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(&codec->component);
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pcm *pcm;
+
+ /*
+ * this is a new PCM device, create new pcm and
+ * add to the pcm list
+ */
+ pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+ pcm->pcm_id = device;
+ pcm->cvt = hdmi->dai_map[dai->id].cvt;
+
+ list_add_tail(&pcm->head, &hdmi->pcm_list);
+
+ sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
+
+ return snd_jack_new(dapm->card->snd_card, jack_name,
+ SND_JACK_AVOUT, &pcm->jack, true, false);
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
+
static int hdmi_codec_probe(struct snd_soc_codec *codec)
{
struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct snd_soc_dapm_context *dapm =
snd_soc_component_get_dapm(&codec->component);
+ struct hdac_hdmi_pin *pin;
+ int ret;
edev->scodec = codec;
- create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
+ ret = create_fill_widget_route_map(dapm);
+ if (ret < 0)
+ return ret;
+
+ aops.audio_ptr = edev;
+ ret = snd_hdac_i915_register_notifier(&aops);
+ if (ret < 0) {
+ dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n",
+ ret);
+ return ret;
+ }
+
+ list_for_each_entry(pin, &hdmi->pin_list, head)
+ hdac_hdmi_present_sense(pin, 1);
/* Imp: Store the card pointer in hda_codec */
edev->card = dapm->card->snd_card;
@@ -515,44 +1419,73 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec)
return 0;
}
+#ifdef CONFIG_PM
+static int hdmi_codec_resume(struct snd_soc_codec *codec)
+{
+ struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+ struct hdac_hdmi_priv *hdmi = edev->private_data;
+ struct hdac_hdmi_pin *pin;
+ struct hdac_device *hdac = &edev->hdac;
+ struct hdac_bus *bus = hdac->bus;
+ int err;
+ unsigned long timeout;
+
+ hdac_hdmi_skl_enable_all_pins(&edev->hdac);
+ hdac_hdmi_skl_enable_dp12(&edev->hdac);
+
+ /* Power up afg */
+ if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) {
+
+ snd_hdac_codec_write(hdac, hdac->afg, 0,
+ AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+ /* Wait till power state is set to D0 */
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)
+ && time_before(jiffies, timeout)) {
+ msleep(50);
+ }
+ }
+
+ /*
+ * As the ELD notify callback request is not entertained while the
+ * device is in suspend state. Need to manually check detection of
+ * all pins here.
+ */
+ list_for_each_entry(pin, &hdmi->pin_list, head)
+ hdac_hdmi_present_sense(pin, 1);
+
+ /*
+ * Codec power is turned ON during controller resume.
+ * Turn it OFF here
+ */
+ err = snd_hdac_display_power(bus, false);
+ if (err < 0) {
+ dev_err(bus->dev,
+ "Cannot turn OFF display power on i915, err: %d\n",
+ err);
+ return err;
+ }
+
+ return 0;
+}
+#else
+#define hdmi_codec_resume NULL
+#endif
+
static struct snd_soc_codec_driver hdmi_hda_codec = {
.probe = hdmi_codec_probe,
.remove = hdmi_codec_remove,
+ .resume = hdmi_codec_resume,
.idle_bias_off = true,
};
-static struct snd_soc_dai_ops hdmi_dai_ops = {
- .startup = hdac_hdmi_pcm_open,
- .shutdown = hdac_hdmi_pcm_close,
- .hw_params = hdac_hdmi_set_hw_params,
- .prepare = hdac_hdmi_playback_prepare,
- .hw_free = hdac_hdmi_playback_cleanup,
-};
-
-static struct snd_soc_dai_driver hdmi_dais[] = {
- { .name = "intel-hdmi-hif1",
- .playback = {
- .stream_name = "hif1",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
- SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
-
- },
- .ops = &hdmi_dai_ops,
- },
-};
-
static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
{
struct hdac_device *codec = &edev->hdac;
struct hdac_hdmi_priv *hdmi_priv;
+ struct snd_soc_dai_driver *hdmi_dais = NULL;
+ int num_dais = 0;
int ret = 0;
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
@@ -565,14 +1498,31 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
INIT_LIST_HEAD(&hdmi_priv->pin_list);
INIT_LIST_HEAD(&hdmi_priv->cvt_list);
+ INIT_LIST_HEAD(&hdmi_priv->pcm_list);
+ mutex_init(&hdmi_priv->pin_mutex);
- ret = hdac_hdmi_parse_and_map_nid(edev);
- if (ret < 0)
+ /*
+ * Turned off in the runtime_suspend during the first explicit
+ * pm_runtime_suspend call.
+ */
+ ret = snd_hdac_display_power(edev->hdac.bus, true);
+ if (ret < 0) {
+ dev_err(&edev->hdac.dev,
+ "Cannot turn on display power on i915 err: %d\n",
+ ret);
return ret;
+ }
+
+ ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
+ if (ret < 0) {
+ dev_err(&codec->dev,
+ "Failed in parse and map nid with err: %d\n", ret);
+ return ret;
+ }
/* ASoC specific initialization */
return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
- hdmi_dais, ARRAY_SIZE(hdmi_dais));
+ hdmi_dais, num_dais);
}
static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
@@ -580,11 +1530,20 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
struct hdac_hdmi_priv *hdmi = edev->private_data;
struct hdac_hdmi_pin *pin, *pin_next;
struct hdac_hdmi_cvt *cvt, *cvt_next;
+ struct hdac_hdmi_pcm *pcm, *pcm_next;
snd_soc_unregister_codec(&edev->hdac.dev);
+ list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
+ pcm->cvt = NULL;
+ pcm->pin = NULL;
+ list_del(&pcm->head);
+ kfree(pcm);
+ }
+
list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
list_del(&cvt->head);
+ kfree(cvt->name);
kfree(cvt);
}
@@ -602,6 +1561,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
struct hdac_ext_device *edev = to_hda_ext_device(dev);
struct hdac_device *hdac = &edev->hdac;
struct hdac_bus *bus = hdac->bus;
+ unsigned long timeout;
int err;
dev_dbg(dev, "Enter: %s\n", __func__);
@@ -611,10 +1571,19 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
return 0;
/* Power down afg */
- if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
+ if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) {
snd_hdac_codec_write(hdac, hdac->afg, 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ /* Wait till power state is set to D3 */
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)
+ && time_before(jiffies, timeout)) {
+
+ msleep(50);
+ }
+ }
+
err = snd_hdac_display_power(bus, false);
if (err < 0) {
dev_err(bus->dev, "Cannot turn on display power on i915\n");
@@ -643,6 +1612,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
return err;
}
+ hdac_hdmi_skl_enable_all_pins(&edev->hdac);
+ hdac_hdmi_skl_enable_dp12(&edev->hdac);
+
/* Power up afg */
if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
snd_hdac_codec_write(hdac, hdac->afg, 0,
@@ -661,6 +1633,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),
{}
};
diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h
new file mode 100644
index 000000000000..8dfd1e0b57b3
--- /dev/null
+++ b/sound/soc/codecs/hdac_hdmi.h
@@ -0,0 +1,6 @@
+#ifndef __HDAC_HDMI_H__
+#define __HDAC_HDMI_H__
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm);
+
+#endif /* __HDAC_HDMI_H__ */
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
new file mode 100755
index 000000000000..2a22fddeb6af
--- /dev/null
+++ b/sound/soc/codecs/max9867.c
@@ -0,0 +1,546 @@
+/*
+ * max9867.c -- max9867 ALSA SoC Audio driver
+ *
+ * Copyright 2013-15 Maxim Integrated Products
+ *
+ * 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/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max9867.h"
+
+static const char *const max9867_spmode[] = {
+ "Stereo Diff", "Mono Diff",
+ "Stereo Cap", "Mono Cap",
+ "Stereo Single", "Mono Single",
+ "Stereo Single Fast", "Mono Single Fast"
+};
+static const char *const max9867_sidetone_text[] = {
+ "None", "Left", "Right", "LeftRight", "LeftRightDiv2",
+};
+static const char *const max9867_filter_text[] = {"IIR", "FIR"};
+
+static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7,
+ max9867_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0,
+ max9867_spmode);
+static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6,
+ max9867_sidetone_text);
+static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0);
+static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1);
+static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1);
+static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1);
+static const unsigned int max98088_micboost_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const struct snd_kcontrol_new max9867_snd_controls[] = {
+ SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL,
+ MAX9867_RIGHTVOL, 0, 63, 1),
+ SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN,
+ MAX9867_RIGHTMICGAIN,
+ 0, 15, 1, max9860_capture_tlv),
+ SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN,
+ MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv),
+ SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN,
+ MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv),
+ SOC_ENUM("Digital Sidetone Src", max9867_sidetone),
+ SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1),
+ SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0),
+ SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1),
+ SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL,
+ 4, 15, 1, max9860_adc_left_tlv),
+ SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL,
+ 0, 15, 1, max9860_adc_right_tlv),
+ SOC_ENUM("Speaker Mode", max9867_spkmode),
+ SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
+ SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0),
+ SOC_ENUM("DSP Filter", max9867_filter),
+};
+
+static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"};
+
+static SOC_ENUM_SINGLE_DECL(max9867_mux_enum,
+ MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT,
+ max9867_mux);
+
+static const struct snd_kcontrol_new max9867_dapm_mux_controls =
+ SOC_DAPM_ENUM("Route", max9867_mux_enum);
+
+static const struct snd_kcontrol_new max9867_left_dapm_control =
+ SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0);
+static const struct snd_kcontrol_new max9867_right_dapm_control =
+ SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0);
+static const struct snd_kcontrol_new max9867_line_dapm_control =
+ SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1);
+
+static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0),
+ SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HPOUT"),
+
+ SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+ &max9867_dapm_mux_controls),
+
+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1,
+ &max9867_left_dapm_control),
+ SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1,
+ &max9867_right_dapm_control),
+ SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0,
+ &max9867_line_dapm_control),
+ SND_SOC_DAPM_INPUT("LINE_IN"),
+};
+
+static const struct snd_soc_dapm_route max9867_audio_map[] = {
+ {"Left DAC", NULL, "DAI_OUT"},
+ {"Right DAC", NULL, "DAI_OUT"},
+ {"Output Mixer", NULL, "Left DAC"},
+ {"Output Mixer", NULL, "Right DAC"},
+ {"HPOUT", NULL, "Output Mixer"},
+
+ {"Left ADC", NULL, "DAI_IN"},
+ {"Right ADC", NULL, "DAI_IN"},
+ {"Input Mixer", NULL, "Left ADC"},
+ {"Input Mixer", NULL, "Right ADC"},
+ {"Input Mux", "Line", "Input Mixer"},
+ {"Input Mux", "Mic", "Input Mixer"},
+ {"Input Mux", "Mic_Line", "Input Mixer"},
+ {"Right Line", "Switch", "Input Mux"},
+ {"Left Line", "Switch", "Input Mux"},
+ {"LINE_IN", NULL, "Left Line"},
+ {"LINE_IN", NULL, "Right Line"},
+};
+
+enum rates {
+ pcm_rate_8, pcm_rate_16, pcm_rate_24,
+ pcm_rate_32, pcm_rate_44,
+ pcm_rate_48, max_pcm_rate,
+};
+
+struct ni_div_rates {
+ u32 mclk;
+ u16 ni[max_pcm_rate];
+} ni_div[] = {
+ {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} },
+ {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} },
+ {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} },
+ {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} },
+ {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} },
+ {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} },
+ {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} },
+ {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} },
+};
+
+static inline int get_ni_value(int mclk, int rate)
+{
+ int i, ret = 0;
+
+ /* find the closest rate index*/
+ for (i = 0; i < ARRAY_SIZE(ni_div); i++) {
+ if (ni_div[i].mclk >= mclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(ni_div))
+ return -EINVAL;
+
+ switch (rate) {
+ case 8000:
+ return ni_div[i].ni[pcm_rate_8];
+ case 16000:
+ return ni_div[i].ni[pcm_rate_16];
+ case 32000:
+ return ni_div[i].ni[pcm_rate_32];
+ case 44100:
+ return ni_div[i].ni[pcm_rate_44];
+ case 48000:
+ return ni_div[i].ni[pcm_rate_48];
+ default:
+ pr_err("%s wrong rate %d\n", __func__, rate);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int max9867_dai_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 max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+ unsigned int ni_h, ni_l;
+ int value;
+
+ value = get_ni_value(max9867->sysclk, params_rate(params));
+ if (value < 0)
+ return value;
+
+ ni_h = (0xFF00 & value) >> 8;
+ ni_l = 0x00FF & value;
+ /* set up the ni value */
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+ MAX9867_NI_HIGH_MASK, ni_h);
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+ MAX9867_NI_LOW_MASK, ni_l);
+ if (!max9867->master) {
+ /*
+ * digital pll locks on to any externally supplied LRCLK signal
+ * and also enable rapid lock mode.
+ */
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+ MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
+ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+ MAX9867_PLL, MAX9867_PLL);
+ } else {
+ unsigned long int bclk_rate, pclk_bclk_ratio;
+ int bclk_value;
+
+ bclk_rate = params_rate(params) * 2 * params_width(params);
+ pclk_bclk_ratio = max9867->pclk/bclk_rate;
+ switch (params_width(params)) {
+ case 8:
+ case 16:
+ switch (pclk_bclk_ratio) {
+ case 2:
+ bclk_value = MAX9867_IFC1B_PCLK_2;
+ break;
+ case 4:
+ bclk_value = MAX9867_IFC1B_PCLK_4;
+ break;
+ case 8:
+ bclk_value = MAX9867_IFC1B_PCLK_8;
+ break;
+ case 16:
+ bclk_value = MAX9867_IFC1B_PCLK_16;
+ break;
+ default:
+ dev_err(codec->dev,
+ "unsupported sampling rate\n");
+ return -EINVAL;
+ }
+ break;
+ case 24:
+ bclk_value = MAX9867_IFC1B_24BIT;
+ break;
+ case 32:
+ bclk_value = MAX9867_IFC1B_32BIT;
+ break;
+ default:
+ dev_err(codec->dev, "unsupported sampling rate\n");
+ return -EINVAL;
+ }
+ regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
+ MAX9867_IFC1B_BCLK_MASK, bclk_value);
+ }
+ return 0;
+}
+
+static int max9867_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
+ return 0;
+}
+
+static int max9867_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+
+ if (mute)
+ regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
+ MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK);
+ else
+ regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
+ MAX9867_DAC_MUTE_MASK, 0);
+ return 0;
+}
+
+static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+ int value = 0;
+
+ /* Set the prescaler based on the master clock frequency*/
+ if (freq >= 10000000 && freq <= 20000000) {
+ value |= MAX9867_PSCLK_10_20;
+ max9867->pclk = freq;
+ } else if (freq >= 20000000 && freq <= 40000000) {
+ value |= MAX9867_PSCLK_20_40;
+ max9867->pclk = freq/2;
+ } else if (freq >= 40000000 && freq <= 60000000) {
+ value |= MAX9867_PSCLK_40_60;
+ max9867->pclk = freq/4;
+ } else {
+ pr_err("bad clock frequency %d", freq);
+ return -EINVAL;
+ }
+ value = value << MAX9867_PSCLK_SHIFT;
+ max9867->sysclk = freq;
+ /* exact integer mode is not supported */
+ value &= ~MAX9867_FREQ_MASK;
+ regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
+ MAX9867_PSCLK_MASK, value);
+ return 0;
+}
+
+static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+ u8 iface1A = 0, iface1B = 0;
+ int ret;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ max9867->master = 1;
+ iface1A |= MAX9867_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ max9867->master = 0;
+ iface1A &= ~MAX9867_MASTER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* for i2s compatible mode */
+ iface1A |= MAX9867_I2S_DLY;
+ /* SDOUT goes to hiz state after all data is transferred */
+ iface1A |= MAX9867_SDOUT_HIZ;
+
+ /* Clock inversion bits, BCI and WCI */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface1A |= MAX9867_BCI_MODE;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface1A |= MAX9867_WCI_MODE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
+ ret = regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
+ return 0;
+}
+
+static struct snd_soc_dai_ops max9867_dai_ops = {
+ .set_fmt = max9867_dai_set_fmt,
+ .set_sysclk = max9867_set_dai_sysclk,
+ .prepare = max9867_prepare,
+ .digital_mute = max9867_mute,
+ .hw_params = max9867_dai_hw_params,
+};
+
+#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_driver max9867_dai[] = {
+ {
+ .name = "max9867-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX9867_RATES,
+ .formats = MAX9867_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX9867_RATES,
+ .formats = MAX9867_FORMATS,
+ },
+ .ops = &max9867_dai_ops,
+ }
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int max9867_suspend(struct device *dev)
+{
+ struct max9867_priv *max9867 = dev_get_drvdata(dev);
+
+ /* Drop down to power saving mode when system is suspended */
+ regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK);
+ return 0;
+}
+
+static int max9867_resume(struct device *dev)
+{
+ struct max9867_priv *max9867 = dev_get_drvdata(dev);
+
+ regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+ MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
+ return 0;
+}
+#endif
+
+static int max9867_probe(struct snd_soc_codec *codec)
+{
+ struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "max98090_probe\n");
+ max9867->codec = codec;
+ return 0;
+}
+
+static struct snd_soc_codec_driver max9867_codec = {
+ .probe = max9867_probe,
+ .controls = max9867_snd_controls,
+ .num_controls = ARRAY_SIZE(max9867_snd_controls),
+ .dapm_routes = max9867_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max9867_audio_map),
+ .dapm_widgets = max9867_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets),
+};
+
+static bool max9867_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX9867_STATUS:
+ case MAX9867_JACKSTATUS:
+ case MAX9867_AUXHIGH:
+ case MAX9867_AUXLOW:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default max9867_reg[] = {
+ { 0x04, 0x00 },
+ { 0x05, 0x00 },
+ { 0x06, 0x00 },
+ { 0x07, 0x00 },
+ { 0x08, 0x00 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x00 },
+ { 0x0B, 0x00 },
+ { 0x0C, 0x00 },
+ { 0x0D, 0x00 },
+ { 0x0E, 0x00 },
+ { 0x0F, 0x00 },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x00 },
+ { 0x14, 0x00 },
+ { 0x15, 0x00 },
+ { 0x16, 0x00 },
+ { 0x17, 0x00 },
+};
+
+static const struct regmap_config max9867_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX9867_REVISION,
+ .reg_defaults = max9867_reg,
+ .num_reg_defaults = ARRAY_SIZE(max9867_reg),
+ .volatile_reg = max9867_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max9867_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct max9867_priv *max9867;
+ int ret = 0, reg;
+
+ max9867 = devm_kzalloc(&i2c->dev,
+ sizeof(*max9867), GFP_KERNEL);
+ if (!max9867)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max9867);
+ max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap);
+ if (IS_ERR(max9867->regmap)) {
+ ret = PTR_ERR(max9867->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_read(max9867->regmap,
+ MAX9867_REVISION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: %d\n", ret);
+ return ret;
+ }
+ dev_info(&i2c->dev, "device revision: %x\n", reg);
+ ret = snd_soc_register_codec(&i2c->dev, &max9867_codec,
+ max9867_dai, ARRAY_SIZE(max9867_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+static int max9867_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max9867_i2c_id[] = {
+ { "max9867", 0 },
+ { }
+};
+
+static const struct of_device_id max9867_of_match[] = {
+ { .compatible = "maxim,max9867", },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
+
+static const struct dev_pm_ops max9867_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume)
+};
+
+static struct i2c_driver max9867_i2c_driver = {
+ .driver = {
+ .name = "max9867",
+ .of_match_table = of_match_ptr(max9867_of_match),
+ .pm = &max9867_pm_ops,
+ },
+ .probe = max9867_i2c_probe,
+ .remove = max9867_i2c_remove,
+ .id_table = max9867_i2c_id,
+};
+
+module_i2c_driver(max9867_i2c_driver);
+
+MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC MAX9867 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h
new file mode 100755
index 000000000000..65590b4ad62a
--- /dev/null
+++ b/sound/soc/codecs/max9867.h
@@ -0,0 +1,83 @@
+/*
+ * max9867.h -- MAX9867 ALSA SoC Audio driver
+ *
+ * Copyright 2013-2015 Maxim Integrated Products
+ *
+ * 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 _MAX9867_H
+#define _MAX9867_H
+
+/* MAX9867 register space */
+
+#define MAX9867_STATUS 0x00
+#define MAX9867_JACKSTATUS 0x01
+#define MAX9867_AUXHIGH 0x02
+#define MAX9867_AUXLOW 0x03
+#define MAX9867_INTEN 0x04
+#define MAX9867_SYSCLK 0x05
+#define MAX9867_FREQ_MASK 0xF
+#define MAX9867_PSCLK_SHIFT 0x4
+#define MAX9867_PSCLK_WIDTH 0x2
+#define MAX9867_PSCLK_MASK (0x03<<MAX9867_PSCLK_SHIFT)
+#define MAX9867_PSCLK_10_20 0x1
+#define MAX9867_PSCLK_20_40 0x2
+#define MAX9867_PSCLK_40_60 0x3
+#define MAX9867_AUDIOCLKHIGH 0x06
+#define MAX9867_NI_HIGH_WIDTH 0x7
+#define MAX9867_NI_HIGH_MASK 0x7F
+#define MAX9867_NI_LOW_MASK 0x7F
+#define MAX9867_NI_LOW_SHIFT 0x1
+#define MAX9867_PLL (1<<7)
+#define MAX9867_AUDIOCLKLOW 0x07
+#define MAX9867_RAPID_LOCK 0x01
+#define MAX9867_IFC1A 0x08
+#define MAX9867_MASTER (1<<7)
+#define MAX9867_I2S_DLY (1<<4)
+#define MAX9867_SDOUT_HIZ (1<<3)
+#define MAX9867_TDM_MODE (1<<2)
+#define MAX9867_WCI_MODE (1<<6)
+#define MAX9867_BCI_MODE (1<<5)
+#define MAX9867_IFC1B 0x09
+#define MAX9867_IFC1B_BCLK_MASK 7
+#define MAX9867_IFC1B_32BIT 0x01
+#define MAX9867_IFC1B_24BIT 0x02
+#define MAX9867_IFC1B_PCLK_2 4
+#define MAX9867_IFC1B_PCLK_4 5
+#define MAX9867_IFC1B_PCLK_8 6
+#define MAX9867_IFC1B_PCLK_16 7
+#define MAX9867_CODECFLTR 0x0a
+#define MAX9867_DACGAIN 0x0b
+#define MAX9867_DACLEVEL 0x0c
+#define MAX9867_DAC_MUTE_SHIFT 0x6
+#define MAX9867_DAC_MUTE_WIDTH 0x1
+#define MAX9867_DAC_MUTE_MASK (0x1<<MAX9867_DAC_MUTE_SHIFT)
+#define MAX9867_ADCLEVEL 0x0d
+#define MAX9867_LEFTLINELVL 0x0e
+#define MAX9867_RIGTHLINELVL 0x0f
+#define MAX9867_LEFTVOL 0x10
+#define MAX9867_RIGHTVOL 0x11
+#define MAX9867_LEFTMICGAIN 0x12
+#define MAX9867_RIGHTMICGAIN 0x13
+#define MAX9867_INPUTCONFIG 0x14
+#define MAX9867_INPUT_SHIFT 0x6
+#define MAX9867_MICCONFIG 0x15
+#define MAX9867_MODECONFIG 0x16
+#define MAX9867_PWRMAN 0x17
+#define MAX9867_SHTDOWN_MASK (1<<7)
+#define MAX9867_REVISION 0xff
+
+#define MAX9867_CACHEREGNUM 10
+
+/* codec private data */
+struct max9867_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ unsigned int sysclk;
+ unsigned int pclk;
+ unsigned int master;
+};
+#endif
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
new file mode 100644
index 000000000000..8d14adae5cc5
--- /dev/null
+++ b/sound/soc/codecs/max98926.c
@@ -0,0 +1,606 @@
+/*
+ * max98926.c -- ALSA SoC MAX98926 driver
+ * Copyright 2013-15 Maxim Integrated Products
+ * 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/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98926.h"
+
+static const char * const max98926_boost_voltage_txt[] = {
+ "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
+ "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
+};
+
+static const char * const max98926_boost_current_txt[] = {
+ "0.6", "0.8", "1.0", "1.2", "1.4", "1.6", "1.8", "2.0",
+ "2.2", "2.4", "2.6", "2.8", "3.2", "3.6", "4.0", "4.4"
+};
+
+static const char *const max98926_dai_txt[] = {
+ "Left", "Right", "LeftRight", "LeftRightDiv2",
+};
+
+static const char *const max98926_pdm_ch_text[] = {
+ "Current", "Voltage",
+};
+
+static const char *const max98926_hpf_cutoff_txt[] = {
+ "Disable", "DC Block", "100Hz",
+ "200Hz", "400Hz", "800Hz",
+};
+
+static const struct reg_default max98926_reg[] = {
+ { 0x0B, 0x00 }, /* IRQ Enable0 */
+ { 0x0C, 0x00 }, /* IRQ Enable1 */
+ { 0x0D, 0x00 }, /* IRQ Enable2 */
+ { 0x0E, 0x00 }, /* IRQ Clear0 */
+ { 0x0F, 0x00 }, /* IRQ Clear1 */
+ { 0x10, 0x00 }, /* IRQ Clear2 */
+ { 0x11, 0xC0 }, /* Map0 */
+ { 0x12, 0x00 }, /* Map1 */
+ { 0x13, 0x00 }, /* Map2 */
+ { 0x14, 0xF0 }, /* Map3 */
+ { 0x15, 0x00 }, /* Map4 */
+ { 0x16, 0xAB }, /* Map5 */
+ { 0x17, 0x89 }, /* Map6 */
+ { 0x18, 0x00 }, /* Map7 */
+ { 0x19, 0x00 }, /* Map8 */
+ { 0x1A, 0x04 }, /* DAI Clock Mode 1 */
+ { 0x1B, 0x00 }, /* DAI Clock Mode 2 */
+ { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */
+ { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */
+ { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */
+ { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
+ { 0x20, 0x50 }, /* Format */
+ { 0x21, 0x00 }, /* TDM Slot Select */
+ { 0x22, 0x00 }, /* DOUT Configuration VMON */
+ { 0x23, 0x00 }, /* DOUT Configuration IMON */
+ { 0x24, 0x00 }, /* DOUT Configuration VBAT */
+ { 0x25, 0x00 }, /* DOUT Configuration VBST */
+ { 0x26, 0x00 }, /* DOUT Configuration FLAG */
+ { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
+ { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
+ { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
+ { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
+ { 0x2B, 0x02 }, /* DOUT Drive Strength */
+ { 0x2C, 0x90 }, /* Filters */
+ { 0x2D, 0x00 }, /* Gain */
+ { 0x2E, 0x02 }, /* Gain Ramping */
+ { 0x2F, 0x00 }, /* Speaker Amplifier */
+ { 0x30, 0x0A }, /* Threshold */
+ { 0x31, 0x00 }, /* ALC Attack */
+ { 0x32, 0x80 }, /* ALC Atten and Release */
+ { 0x33, 0x00 }, /* ALC Infinite Hold Release */
+ { 0x34, 0x92 }, /* ALC Configuration */
+ { 0x35, 0x01 }, /* Boost Converter */
+ { 0x36, 0x00 }, /* Block Enable */
+ { 0x37, 0x00 }, /* Configuration */
+ { 0x38, 0x00 }, /* Global Enable */
+ { 0x3A, 0x00 }, /* Boost Limiter */
+};
+
+static const struct soc_enum max98926_voltage_enum[] = {
+ SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0,
+ ARRAY_SIZE(max98926_pdm_ch_text),
+ max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_voltage_control =
+ SOC_DAPM_ENUM("Route", max98926_voltage_enum);
+
+static const struct soc_enum max98926_current_enum[] = {
+ SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_SOURCE_1_SHIFT,
+ ARRAY_SIZE(max98926_pdm_ch_text),
+ max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_current_control =
+ SOC_DAPM_ENUM("Route", max98926_current_enum);
+
+static const struct snd_kcontrol_new max98926_mixer_controls[] = {
+ SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 0, 0),
+ SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new max98926_dai_controls[] = {
+ SOC_DAPM_SINGLE("Left", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 0, 0),
+ SOC_DAPM_SINGLE("Right", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 1, 0),
+ SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 2, 0),
+ SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN,
+ MAX98926_DAC_IN_SEL_SHIFT, 3, 0),
+};
+
+static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE,
+ MAX98926_SPK_EN_SHIFT, 0),
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE,
+ MAX98926_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE,
+ MAX98926_ADC_IMON_EN_WIDTH |
+ MAX98926_ADC_VMON_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE,
+ MAX98926_BST_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+ SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP,
+ MAX98926_INSELECT_MODE_SHIFT, 0,
+ &max98926_mixer_controls[0],
+ ARRAY_SIZE(max98926_mixer_controls)),
+ SND_SOC_DAPM_MIXER("DAI Sel",
+ MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0,
+ &max98926_dai_controls[0],
+ ARRAY_SIZE(max98926_dai_controls)),
+ SND_SOC_DAPM_MUX("PDM CH1 Source",
+ MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CURRENT_SHIFT,
+ 0, &max98926_current_control),
+ SND_SOC_DAPM_MUX("PDM CH0 Source",
+ MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_VOLTAGE_SHIFT,
+ 0, &max98926_voltage_control),
+};
+
+static const struct snd_soc_dapm_route max98926_audio_map[] = {
+ {"VI Enable", NULL, "DAI_OUT"},
+ {"DAI Sel", "Left", "VI Enable"},
+ {"DAI Sel", "Right", "VI Enable"},
+ {"DAI Sel", "LeftRight", "VI Enable"},
+ {"DAI Sel", "LeftRightDiv2", "VI Enable"},
+ {"PCM Sel", "PCM", "DAI Sel"},
+
+ {"PDM CH1 Source", "Current", "DAI_OUT"},
+ {"PDM CH1 Source", "Voltage", "DAI_OUT"},
+ {"PDM CH0 Source", "Current", "DAI_OUT"},
+ {"PDM CH0 Source", "Voltage", "DAI_OUT"},
+ {"PCM Sel", "Analog", "PDM CH1 Source"},
+ {"PCM Sel", "Analog", "PDM CH0 Source"},
+ {"Amp Enable", NULL, "PCM Sel"},
+
+ {"BST Enable", NULL, "Amp Enable"},
+ {"BE_OUT", NULL, "BST Enable"},
+};
+
+static bool max98926_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98926_VBAT_DATA:
+ case MAX98926_VBST_DATA:
+ case MAX98926_LIVE_STATUS0:
+ case MAX98926_LIVE_STATUS1:
+ case MAX98926_LIVE_STATUS2:
+ case MAX98926_STATE0:
+ case MAX98926_STATE1:
+ case MAX98926_STATE2:
+ case MAX98926_FLAG0:
+ case MAX98926_FLAG1:
+ case MAX98926_FLAG2:
+ case MAX98926_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98926_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98926_IRQ_CLEAR0:
+ case MAX98926_IRQ_CLEAR1:
+ case MAX98926_IRQ_CLEAR2:
+ case MAX98926_ALC_HOLD_RLS:
+ return false;
+ default:
+ return true;
+ }
+};
+
+DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0);
+DECLARE_TLV_DB_RANGE(max98926_current_tlv,
+ 0, 11, TLV_DB_SCALE_ITEM(20, 20, 0),
+ 12, 15, TLV_DB_SCALE_ITEM(320, 40, 0),
+);
+
+static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff,
+ MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT,
+ max98926_hpf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage,
+ MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT,
+ max98926_boost_voltage_txt);
+
+static const struct snd_kcontrol_new max98926_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN,
+ MAX98926_SPK_GAIN_SHIFT,
+ (1<<MAX98926_SPK_GAIN_WIDTH)-1, 0,
+ max98926_spk_tlv),
+ SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_RMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_ZCD_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD,
+ MAX98926_ALC_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD,
+ MAX98926_ALC_TH_SHIFT,
+ (1<<MAX98926_ALC_TH_WIDTH)-1, 0),
+ SOC_ENUM("Boost Output Voltage", max98926_boost_voltage),
+ SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER,
+ MAX98926_BST_ILIM_SHIFT,
+ (1<<MAX98926_BST_ILIM_SHIFT)-1, 0,
+ max98926_current_tlv),
+ SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff),
+ SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_1_SHIFT,
+ MAX98926_PDM_CHANNEL_1_HIZ, 1, 0),
+ SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_0_SHIFT,
+ MAX98926_PDM_CHANNEL_0_HIZ, 1, 0),
+};
+
+static const struct {
+ int rate;
+ int sr;
+} rate_table[] = {
+ {
+ .rate = 8000,
+ .sr = 0,
+ },
+ {
+ .rate = 11025,
+ .sr = 1,
+ },
+ {
+ .rate = 12000,
+ .sr = 2,
+ },
+ {
+ .rate = 16000,
+ .sr = 3,
+ },
+ {
+ .rate = 22050,
+ .sr = 4,
+ },
+ {
+ .rate = 24000,
+ .sr = 5,
+ },
+ {
+ .rate = 32000,
+ .sr = 6,
+ },
+ {
+ .rate = 44100,
+ .sr = 7,
+ },
+ {
+ .rate = 48000,
+ .sr = 8,
+ },
+};
+
+static void max98926_set_sense_data(struct max98926_priv *max98926)
+{
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VMON,
+ MAX98926_DAI_VMON_EN_MASK,
+ MAX98926_DAI_VMON_EN_MASK);
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_IMON,
+ MAX98926_DAI_IMON_EN_MASK,
+ MAX98926_DAI_IMON_EN_MASK);
+
+ if (!max98926->interleave_mode) {
+ /* set VMON slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VMON,
+ MAX98926_DAI_VMON_SLOT_MASK,
+ max98926->v_slot);
+ /* set IMON slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_IMON,
+ MAX98926_DAI_IMON_SLOT_MASK,
+ max98926->i_slot);
+ } else {
+ /* enable interleave mode */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_INTERLEAVE_MASK,
+ MAX98926_DAI_INTERLEAVE_MASK);
+ /* set interleave slots */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DOUT_CFG_VBAT,
+ MAX98926_DAI_INTERLEAVE_SLOT_MASK,
+ max98926->v_slot);
+ }
+}
+
+static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+ unsigned int invert = 0;
+
+ dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ max98926_set_sense_data(max98926);
+ break;
+ default:
+ dev_err(codec->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ invert = MAX98926_DAI_WCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98926_DAI_BCI_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ regmap_write(max98926->regmap,
+ MAX98926_FORMAT, MAX98926_DAI_DLY_MASK);
+ regmap_update_bits(max98926->regmap, MAX98926_FORMAT,
+ MAX98926_DAI_BCI_MASK, invert);
+ return 0;
+}
+
+static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int dai_sr = -EINVAL;
+ int rate = params_rate(params), i;
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+ int blr_clk_ratio;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_16);
+ max98926->ch_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_24);
+ max98926->ch_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_FORMAT,
+ MAX98926_DAI_CHANSZ_MASK,
+ MAX98926_DAI_CHANSZ_32);
+ max98926->ch_size = 32;
+ break;
+ default:
+ dev_dbg(codec->dev, "format unsupported %d",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ /* BCLK/LRCLK ratio calculation */
+ blr_clk_ratio = params_channels(params) * max98926->ch_size;
+
+ switch (blr_clk_ratio) {
+ case 32:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_32);
+ break;
+ case 48:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_48);
+ break;
+ case 64:
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_BSEL_MASK,
+ MAX98926_DAI_BSEL_64);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* find the closest rate */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= rate) {
+ dai_sr = rate_table[i].sr;
+ break;
+ }
+ }
+ if (dai_sr < 0)
+ return -EINVAL;
+
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98926->regmap,
+ MAX98926_DAI_CLK_MODE2,
+ MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT);
+ return 0;
+}
+
+#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops max98926_dai_ops = {
+ .set_fmt = max98926_dai_set_fmt,
+ .hw_params = max98926_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98926_dai[] = {
+{
+ .name = "max98926-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98926_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = MAX98926_FORMATS,
+ },
+ .ops = &max98926_dai_ops,
+}
+};
+
+static int max98926_probe(struct snd_soc_codec *codec)
+{
+ struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+
+ max98926->codec = codec;
+ codec->control_data = max98926->regmap;
+ /* Hi-Z all the slots */
+ regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0);
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98926 = {
+ .probe = max98926_probe,
+ .controls = max98926_snd_controls,
+ .num_controls = ARRAY_SIZE(max98926_snd_controls),
+ .dapm_routes = max98926_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98926_audio_map),
+ .dapm_widgets = max98926_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets),
+};
+
+static const struct regmap_config max98926_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX98926_VERSION,
+ .reg_defaults = max98926_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98926_reg),
+ .volatile_reg = max98926_volatile_register,
+ .readable_reg = max98926_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int max98926_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ int ret, reg;
+ u32 value;
+ struct max98926_priv *max98926;
+
+ max98926 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98926), GFP_KERNEL);
+ if (!max98926)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, max98926);
+ max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap);
+ if (IS_ERR(max98926->regmap)) {
+ ret = PTR_ERR(max98926->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ goto err_out;
+ }
+ if (of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
+ max98926->interleave_mode = true;
+
+ if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
+ if (value > MAX98926_DAI_VMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "vmon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98926->v_slot = value;
+ }
+ if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) {
+ if (value > MAX98926_DAI_IMON_SLOT_1E_1F) {
+ dev_err(&i2c->dev, "imon slot number is wrong:\n");
+ return -EINVAL;
+ }
+ max98926->i_slot = value;
+ }
+ ret = regmap_read(max98926->regmap,
+ MAX98926_VERSION, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to read: %x\n", reg);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98926,
+ max98926_dai, ARRAY_SIZE(max98926_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev,
+ "Failed to register codec: %d\n", ret);
+ dev_info(&i2c->dev, "device version: %x\n", reg);
+err_out:
+ return ret;
+}
+
+static int max98926_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max98926_i2c_id[] = {
+ { "max98926", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max98926_i2c_id);
+
+static const struct of_device_id max98926_of_match[] = {
+ { .compatible = "maxim,max98926", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98926_of_match);
+
+static struct i2c_driver max98926_i2c_driver = {
+ .driver = {
+ .name = "max98926",
+ .of_match_table = of_match_ptr(max98926_of_match),
+ .pm = NULL,
+ },
+ .probe = max98926_i2c_probe,
+ .remove = max98926_i2c_remove,
+ .id_table = max98926_i2c_id,
+};
+
+module_i2c_driver(max98926_i2c_driver)
+MODULE_DESCRIPTION("ALSA SoC MAX98926 driver");
+MODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h
new file mode 100644
index 000000000000..9d7ab6df79ca
--- /dev/null
+++ b/sound/soc/codecs/max98926.h
@@ -0,0 +1,848 @@
+/*
+ * max98926.h -- MAX98926 ALSA SoC Audio driver
+ * Copyright 2013-2015 Maxim Integrated Products
+ * 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 _MAX98926_H
+#define _MAX98926_H
+
+#define MAX98926_CHIP_VERSION 0x40
+#define MAX98926_CHIP_VERSION1 0x50
+
+#define MAX98926_VBAT_DATA 0x00
+#define MAX98926_VBST_DATA 0x01
+#define MAX98926_LIVE_STATUS0 0x02
+#define MAX98926_LIVE_STATUS1 0x03
+#define MAX98926_LIVE_STATUS2 0x04
+#define MAX98926_STATE0 0x05
+#define MAX98926_STATE1 0x06
+#define MAX98926_STATE2 0x07
+#define MAX98926_FLAG0 0x08
+#define MAX98926_FLAG1 0x09
+#define MAX98926_FLAG2 0x0A
+#define MAX98926_IRQ_ENABLE0 0x0B
+#define MAX98926_IRQ_ENABLE1 0x0C
+#define MAX98926_IRQ_ENABLE2 0x0D
+#define MAX98926_IRQ_CLEAR0 0x0E
+#define MAX98926_IRQ_CLEAR1 0x0F
+#define MAX98926_IRQ_CLEAR2 0x10
+#define MAX98926_MAP0 0x11
+#define MAX98926_MAP1 0x12
+#define MAX98926_MAP2 0x13
+#define MAX98926_MAP3 0x14
+#define MAX98926_MAP4 0x15
+#define MAX98926_MAP5 0x16
+#define MAX98926_MAP6 0x17
+#define MAX98926_MAP7 0x18
+#define MAX98926_MAP8 0x19
+#define MAX98926_DAI_CLK_MODE1 0x1A
+#define MAX98926_DAI_CLK_MODE2 0x1B
+#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C
+#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D
+#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E
+#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F
+#define MAX98926_FORMAT 0x20
+#define MAX98926_TDM_SLOT_SELECT 0x21
+#define MAX98926_DOUT_CFG_VMON 0x22
+#define MAX98926_DOUT_CFG_IMON 0x23
+#define MAX98926_DOUT_CFG_VBAT 0x24
+#define MAX98926_DOUT_CFG_VBST 0x25
+#define MAX98926_DOUT_CFG_FLAG 0x26
+#define MAX98926_DOUT_HIZ_CFG1 0x27
+#define MAX98926_DOUT_HIZ_CFG2 0x28
+#define MAX98926_DOUT_HIZ_CFG3 0x29
+#define MAX98926_DOUT_HIZ_CFG4 0x2A
+#define MAX98926_DOUT_DRV_STRENGTH 0x2B
+#define MAX98926_FILTERS 0x2C
+#define MAX98926_GAIN 0x2D
+#define MAX98926_GAIN_RAMPING 0x2E
+#define MAX98926_SPK_AMP 0x2F
+#define MAX98926_THRESHOLD 0x30
+#define MAX98926_ALC_ATTACK 0x31
+#define MAX98926_ALC_ATTEN_RLS 0x32
+#define MAX98926_ALC_HOLD_RLS 0x33
+#define MAX98926_ALC_CONFIGURATION 0x34
+#define MAX98926_BOOST_CONVERTER 0x35
+#define MAX98926_BLOCK_ENABLE 0x36
+#define MAX98926_CONFIGURATION 0x37
+#define MAX98926_GLOBAL_ENABLE 0x38
+#define MAX98926_BOOST_LIMITER 0x3A
+#define MAX98926_VERSION 0xFF
+
+#define MAX98926_REG_CNT (MAX98926_R03A_BOOST_LIMITER+1)
+
+#define MAX98926_PDM_CURRENT_MASK (1<<7)
+#define MAX98926_PDM_CURRENT_SHIFT 7
+#define MAX98926_PDM_VOLTAGE_MASK (1<<3)
+#define MAX98926_PDM_VOLTAGE_SHIFT 3
+#define MAX98926_PDM_CHANNEL_0_MASK (1<<2)
+#define MAX98926_PDM_CHANNEL_0_SHIFT 2
+#define MAX98926_PDM_CHANNEL_1_MASK (1<<6)
+#define MAX98926_PDM_CHANNEL_1_SHIFT 6
+#define MAX98926_PDM_CHANNEL_1_HIZ 5
+#define MAX98926_PDM_CHANNEL_0_HIZ 1
+#define MAX98926_PDM_SOURCE_0_SHIFT 0
+#define MAX98926_PDM_SOURCE_0_MASK (1<<0)
+#define MAX98926_PDM_SOURCE_1_MASK (1<<4)
+#define MAX98926_PDM_SOURCE_1_SHIFT 4
+
+/* MAX98926 Register Bit Fields */
+
+/* MAX98926_R002_LIVE_STATUS0 */
+#define MAX98926_THERMWARN_STATUS_MASK (1<<3)
+#define MAX98926_THERMWARN_STATUS_SHIFT 3
+#define MAX98926_THERMWARN_STATUS_WIDTH 1
+#define MAX98926_THERMSHDN_STATUS_MASK (1<<1)
+#define MAX98926_THERMSHDN_STATUS_SHIFT 1
+#define MAX98926_THERMSHDN_STATUS_WIDTH 1
+
+/* MAX98926_R003_LIVE_STATUS1 */
+#define MAX98926_SPKCURNT_STATUS_MASK (1<<5)
+#define MAX98926_SPKCURNT_STATUS_SHIFT 5
+#define MAX98926_SPKCURNT_STATUS_WIDTH 1
+#define MAX98926_WATCHFAIL_STATUS_MASK (1<<4)
+#define MAX98926_WATCHFAIL_STATUS_SHIFT 4
+#define MAX98926_WATCHFAIL_STATUS_WIDTH 1
+#define MAX98926_ALCINFH_STATUS_MASK (1<<3)
+#define MAX98926_ALCINFH_STATUS_SHIFT 3
+#define MAX98926_ALCINFH_STATUS_WIDTH 1
+#define MAX98926_ALCACT_STATUS_MASK (1<<2)
+#define MAX98926_ALCACT_STATUS_SHIFT 2
+#define MAX98926_ALCACT_STATUS_WIDTH 1
+#define MAX98926_ALCMUT_STATUS_MASK (1<<1)
+#define MAX98926_ALCMUT_STATUS_SHIFT 1
+#define MAX98926_ALCMUT_STATUS_WIDTH 1
+#define MAX98926_ACLP_STATUS_MASK (1<<0)
+#define MAX98926_ACLP_STATUS_SHIFT 0
+#define MAX98926_ACLP_STATUS_WIDTH 1
+
+/* MAX98926_R004_LIVE_STATUS2 */
+#define MAX98926_SLOTOVRN_STATUS_MASK (1<<6)
+#define MAX98926_SLOTOVRN_STATUS_SHIFT 6
+#define MAX98926_SLOTOVRN_STATUS_WIDTH 1
+#define MAX98926_INVALSLOT_STATUS_MASK (1<<5)
+#define MAX98926_INVALSLOT_STATUS_SHIFT 5
+#define MAX98926_INVALSLOT_STATUS_WIDTH 1
+#define MAX98926_SLOTCNFLT_STATUS_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_STATUS_SHIFT 4
+#define MAX98926_SLOTCNFLT_STATUS_WIDTH 1
+#define MAX98926_VBSTOVFL_STATUS_MASK (1<<3)
+#define MAX98926_VBSTOVFL_STATUS_SHIFT 3
+#define MAX98926_VBSTOVFL_STATUS_WIDTH 1
+#define MAX98926_VBATOVFL_STATUS_MASK (1<<2)
+#define MAX98926_VBATOVFL_STATUS_SHIFT 2
+#define MAX98926_VBATOVFL_STATUS_WIDTH 1
+#define MAX98926_IMONOVFL_STATUS_MASK (1<<1)
+#define MAX98926_IMONOVFL_STATUS_SHIFT 1
+#define MAX98926_IMONOVFL_STATUS_WIDTH 1
+#define MAX98926_VMONOVFL_STATUS_MASK (1<<0)
+#define MAX98926_VMONOVFL_STATUS_SHIFT 0
+#define MAX98926_VMONOVFL_STATUS_WIDTH 1
+
+/* MAX98926_R005_STATE0 */
+#define MAX98926_THERMWARN_END_STATE_MASK (1<<3)
+#define MAX98926_THERMWARN_END_STATE_SHIFT 3
+#define MAX98926_THERMWARN_END_STATE_WIDTH 1
+#define MAX98926_THERMWARN_BGN_STATE_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_STATE_SHIFT 1
+#define MAX98926_THERMWARN_BGN_STATE_WIDTH 1
+#define MAX98926_THERMSHDN_END_STATE_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_STATE_SHIFT 1
+#define MAX98926_THERMSHDN_END_STATE_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_STATE_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_STATE_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_STATE_WIDTH 1
+
+/* MAX98926_R006_STATE1 */
+#define MAX98926_SPRCURNT_STATE_MASK (1<<5)
+#define MAX98926_SPRCURNT_STATE_SHIFT 5
+#define MAX98926_SPRCURNT_STATE_WIDTH 1
+#define MAX98926_WATCHFAIL_STATE_MASK (1<<4)
+#define MAX98926_WATCHFAIL_STATE_SHIFT 4
+#define MAX98926_WATCHFAIL_STATE_WIDTH 1
+#define MAX98926_ALCINFH_STATE_MASK (1<<3)
+#define MAX98926_ALCINFH_STATE_SHIFT 3
+#define MAX98926_ALCINFH_STATE_WIDTH 1
+#define MAX98926_ALCACT_STATE_MASK (1<<2)
+#define MAX98926_ALCACT_STATE_SHIFT 2
+#define MAX98926_ALCACT_STATE_WIDTH 1
+#define MAX98926_ALCMUT_STATE_MASK (1<<1)
+#define MAX98926_ALCMUT_STATE_SHIFT 1
+#define MAX98926_ALCMUT_STATE_WIDTH 1
+#define MAX98926_ALCP_STATE_MASK (1<<0)
+#define MAX98926_ALCP_STATE_SHIFT 0
+#define MAX98926_ALCP_STATE_WIDTH 1
+
+/* MAX98926_R007_STATE2 */
+#define MAX98926_SLOTOVRN_STATE_MASK (1<<6)
+#define MAX98926_SLOTOVRN_STATE_SHIFT 6
+#define MAX98926_SLOTOVRN_STATE_WIDTH 1
+#define MAX98926_INVALSLOT_STATE_MASK (1<<5)
+#define MAX98926_INVALSLOT_STATE_SHIFT 5
+#define MAX98926_INVALSLOT_STATE_WIDTH 1
+#define MAX98926_SLOTCNFLT_STATE_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_STATE_SHIFT 4
+#define MAX98926_SLOTCNFLT_STATE_WIDTH 1
+#define MAX98926_VBSTOVFL_STATE_MASK (1<<3)
+#define MAX98926_VBSTOVFL_STATE_SHIFT 3
+#define MAX98926_VBSTOVFL_STATE_WIDTH 1
+#define MAX98926_VBATOVFL_STATE_MASK (1<<2)
+#define MAX98926_VBATOVFL_STATE_SHIFT 2
+#define MAX98926_VBATOVFL_STATE_WIDTH 1
+#define MAX98926_IMONOVFL_STATE_MASK (1<<1)
+#define MAX98926_IMONOVFL_STATE_SHIFT 1
+#define MAX98926_IMONOVFL_STATE_WIDTH 1
+#define MAX98926_VMONOVFL_STATE_MASK (1<<0)
+#define MAX98926_VMONOVFL_STATE_SHIFT 0
+#define MAX98926_VMONOVFL_STATE_WIDTH 1
+
+/* MAX98926_R008_FLAG0 */
+#define MAX98926_THERMWARN_END_FLAG_MASK (1<<3)
+#define MAX98926_THERMWARN_END_FLAG_SHIFT 3
+#define MAX98926_THERMWARN_END_FLAG_WIDTH 1
+#define MAX98926_THERMWARN_BGN_FLAG_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_FLAG_SHIFT 2
+#define MAX98926_THERMWARN_BGN_FLAG_WIDTH 1
+#define MAX98926_THERMSHDN_END_FLAG_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_FLAG_SHIFT 1
+#define MAX98926_THERMSHDN_END_FLAG_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_FLAG_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH 1
+
+/* MAX98926_R009_FLAG1 */
+#define MAX98926_SPKCURNT_FLAG_MASK (1<<5)
+#define MAX98926_SPKCURNT_FLAG_SHIFT 5
+#define MAX98926_SPKCURNT_FLAG_WIDTH 1
+#define MAX98926_WATCHFAIL_FLAG_MASK (1<<4)
+#define MAX98926_WATCHFAIL_FLAG_SHIFT 4
+#define MAX98926_WATCHFAIL_FLAG_WIDTH 1
+#define MAX98926_ALCINFH_FLAG_MASK (1<<3)
+#define MAX98926_ALCINFH_FLAG_SHIFT 3
+#define MAX98926_ALCINFH_FLAG_WIDTH 1
+#define MAX98926_ALCACT_FLAG_MASK (1<<2)
+#define MAX98926_ALCACT_FLAG_SHIFT 2
+#define MAX98926_ALCACT_FLAG_WIDTH 1
+#define MAX98926_ALCMUT_FLAG_MASK (1<<1)
+#define MAX98926_ALCMUT_FLAG_SHIFT 1
+#define MAX98926_ALCMUT_FLAG_WIDTH 1
+#define MAX98926_ALCP_FLAG_MASK (1<<0)
+#define MAX98926_ALCP_FLAG_SHIFT 0
+#define MAX98926_ALCP_FLAG_WIDTH 1
+
+/* MAX98926_R00A_FLAG2 */
+#define MAX98926_SLOTOVRN_FLAG_MASK (1<<6)
+#define MAX98926_SLOTOVRN_FLAG_SHIFT 6
+#define MAX98926_SLOTOVRN_FLAG_WIDTH 1
+#define MAX98926_INVALSLOT_FLAG_MASK (1<<5)
+#define MAX98926_INVALSLOT_FLAG_SHIFT 5
+#define MAX98926_INVALSLOT_FLAG_WIDTH 1
+#define MAX98926_SLOTCNFLT_FLAG_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_FLAG_SHIFT 4
+#define MAX98926_SLOTCNFLT_FLAG_WIDTH 1
+#define MAX98926_VBSTOVFL_FLAG_MASK (1<<3)
+#define MAX98926_VBSTOVFL_FLAG_SHIFT 3
+#define MAX98926_VBSTOVFL_FLAG_WIDTH 1
+#define MAX98926_VBATOVFL_FLAG_MASK (1<<2)
+#define MAX98926_VBATOVFL_FLAG_SHIFT 2
+#define MAX98926_VBATOVFL_FLAG_WIDTH 1
+#define MAX98926_IMONOVFL_FLAG_MASK (1<<1)
+#define MAX98926_IMONOVFL_FLAG_SHIFT 1
+#define MAX98926_IMONOVFL_FLAG_WIDTH 1
+#define MAX98926_VMONOVFL_FLAG_MASK (1<<0)
+#define MAX98926_VMONOVFL_FLAG_SHIFT 0
+#define MAX98926_VMONOVFL_FLAG_WIDTH 1
+
+/* MAX98926_R00B_IRQ_ENABLE0 */
+#define MAX98926_THERMWARN_END_EN_MASK (1<<3)
+#define MAX98926_THERMWARN_END_EN_SHIFT 3
+#define MAX98926_THERMWARN_END_EN_WIDTH 1
+#define MAX98926_THERMWARN_BGN_EN_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_EN_SHIFT 2
+#define MAX98926_THERMWARN_BGN_EN_WIDTH 1
+#define MAX98926_THERMSHDN_END_EN_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_EN_SHIFT 1
+#define MAX98926_THERMSHDN_END_EN_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_EN_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_EN_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_EN_WIDTH 1
+
+/* MAX98926_R00C_IRQ_ENABLE1 */
+#define MAX98926_SPKCURNT_EN_MASK (1<<5)
+#define MAX98926_SPKCURNT_EN_SHIFT 5
+#define MAX98926_SPKCURNT_EN_WIDTH 1
+#define MAX98926_WATCHFAIL_EN_MASK (1<<4)
+#define MAX98926_WATCHFAIL_EN_SHIFT 4
+#define MAX98926_WATCHFAIL_EN_WIDTH 1
+#define MAX98926_ALCINFH_EN_MASK (1<<3)
+#define MAX98926_ALCINFH_EN_SHIFT 3
+#define MAX98926_ALCINFH_EN_WIDTH 1
+#define MAX98926_ALCACT_EN_MASK (1<<2)
+#define MAX98926_ALCACT_EN_SHIFT 2
+#define MAX98926_ALCACT_EN_WIDTH 1
+#define MAX98926_ALCMUT_EN_MASK (1<<1)
+#define MAX98926_ALCMUT_EN_SHIFT 1
+#define MAX98926_ALCMUT_EN_WIDTH 1
+#define MAX98926_ALCP_EN_MASK (1<<0)
+#define MAX98926_ALCP_EN_SHIFT 0
+#define MAX98926_ALCP_EN_WIDTH 1
+
+/* MAX98926_R00D_IRQ_ENABLE2 */
+#define MAX98926_SLOTOVRN_EN_MASK (1<<6)
+#define MAX98926_SLOTOVRN_EN_SHIFT 6
+#define MAX98926_SLOTOVRN_EN_WIDTH 1
+#define MAX98926_INVALSLOT_EN_MASK (1<<5)
+#define MAX98926_INVALSLOT_EN_SHIFT 5
+#define MAX98926_INVALSLOT_EN_WIDTH 1
+#define MAX98926_SLOTCNFLT_EN_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_EN_SHIFT 4
+#define MAX98926_SLOTCNFLT_EN_WIDTH 1
+#define MAX98926_VBSTOVFL_EN_MASK (1<<3)
+#define MAX98926_VBSTOVFL_EN_SHIFT 3
+#define MAX98926_VBSTOVFL_EN_WIDTH 1
+#define MAX98926_VBATOVFL_EN_MASK (1<<2)
+#define MAX98926_VBATOVFL_EN_SHIFT 2
+#define MAX98926_VBATOVFL_EN_WIDTH 1
+#define MAX98926_IMONOVFL_EN_MASK (1<<1)
+#define MAX98926_IMONOVFL_EN_SHIFT 1
+#define MAX98926_IMONOVFL_EN_WIDTH 1
+#define MAX98926_VMONOVFL_EN_MASK (1<<0)
+#define MAX98926_VMONOVFL_EN_SHIFT 0
+#define MAX98926_VMONOVFL_EN_WIDTH 1
+
+/* MAX98926_R00E_IRQ_CLEAR0 */
+#define MAX98926_THERMWARN_END_CLR_MASK (1<<3)
+#define MAX98926_THERMWARN_END_CLR_SHIFT 3
+#define MAX98926_THERMWARN_END_CLR_WIDTH 1
+#define MAX98926_THERMWARN_BGN_CLR_MASK (1<<2)
+#define MAX98926_THERMWARN_BGN_CLR_SHIFT 2
+#define MAX98926_THERMWARN_BGN_CLR_WIDTH 1
+#define MAX98926_THERMSHDN_END_CLR_MASK (1<<1)
+#define MAX98926_THERMSHDN_END_CLR_SHIFT 1
+#define MAX98926_THERMSHDN_END_CLR_WIDTH 1
+#define MAX98926_THERMSHDN_BGN_CLR_MASK (1<<0)
+#define MAX98926_THERMSHDN_BGN_CLR_SHIFT 0
+#define MAX98926_THERMSHDN_BGN_CLR_WIDTH 1
+
+/* MAX98926_R00F_IRQ_CLEAR1 */
+#define MAX98926_SPKCURNT_CLR_MASK (1<<5)
+#define MAX98926_SPKCURNT_CLR_SHIFT 5
+#define MAX98926_SPKCURNT_CLR_WIDTH 1
+#define MAX98926_WATCHFAIL_CLR_MASK (1<<4)
+#define MAX98926_WATCHFAIL_CLR_SHIFT 4
+#define MAX98926_WATCHFAIL_CLR_WIDTH 1
+#define MAX98926_ALCINFH_CLR_MASK (1<<3)
+#define MAX98926_ALCINFH_CLR_SHIFT 3
+#define MAX98926_ALCINFH_CLR_WIDTH 1
+#define MAX98926_ALCACT_CLR_MASK (1<<2)
+#define MAX98926_ALCACT_CLR_SHIFT 2
+#define MAX98926_ALCACT_CLR_WIDTH 1
+#define MAX98926_ALCMUT_CLR_MASK (1<<1)
+#define MAX98926_ALCMUT_CLR_SHIFT 1
+#define MAX98926_ALCMUT_CLR_WIDTH 1
+#define MAX98926_ALCP_CLR_MASK (1<<0)
+#define MAX98926_ALCP_CLR_SHIFT 0
+#define MAX98926_ALCP_CLR_WIDTH 1
+
+/* MAX98926_R010_IRQ_CLEAR2 */
+#define MAX98926_SLOTOVRN_CLR_MASK (1<<6)
+#define MAX98926_SLOTOVRN_CLR_SHIFT 6
+#define MAX98926_SLOTOVRN_CLR_WIDTH 1
+#define MAX98926_INVALSLOT_CLR_MASK (1<<5)
+#define MAX98926_INVALSLOT_CLR_SHIFT 5
+#define MAX98926_INVALSLOT_CLR_WIDTH 1
+#define MAX98926_SLOTCNFLT_CLR_MASK (1<<4)
+#define MAX98926_SLOTCNFLT_CLR_SHIFT 4
+#define MAX98926_SLOTCNFLT_CLR_WIDTH 1
+#define MAX98926_VBSTOVFL_CLR_MASK (1<<3)
+#define MAX98926_VBSTOVFL_CLR_SHIFT 3
+#define MAX98926_VBSTOVFL_CLR_WIDTH 1
+#define MAX98926_VBATOVFL_CLR_MASK (1<<2)
+#define MAX98926_VBATOVFL_CLR_SHIFT 2
+#define MAX98926_VBATOVFL_CLR_WIDTH 1
+#define MAX98926_IMONOVFL_CLR_MASK (1<<1)
+#define MAX98926_IMONOVFL_CLR_SHIFT 1
+#define MAX98926_IMONOVFL_CLR_WIDTH 1
+#define MAX98926_VMONOVFL_CLR_MASK (1<<0)
+#define MAX98926_VMONOVFL_CLR_SHIFT 0
+#define MAX98926_VMONOVFL_CLR_WIDTH 1
+
+/* MAX98926_R011_MAP0 */
+#define MAX98926_ER_THERMWARN_EN_MASK (1<<7)
+#define MAX98926_ER_THERMWARN_EN_SHIFT 7
+#define MAX98926_ER_THERMWARN_EN_WIDTH 1
+#define MAX98926_ER_THERMWARN_MAP_MASK (0x07<<4)
+#define MAX98926_ER_THERMWARN_MAP_SHIFT 4
+#define MAX98926_ER_THERMWARN_MAP_WIDTH 3
+
+/* MAX98926_R012_MAP1 */
+#define MAX98926_ER_ALCMUT_EN_MASK (1<<7)
+#define MAX98926_ER_ALCMUT_EN_SHIFT 7
+#define MAX98926_ER_ALCMUT_EN_WIDTH 1
+#define MAX98926_ER_ALCMUT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_ALCMUT_MAP_SHIFT 4
+#define MAX98926_ER_ALCMUT_MAP_WIDTH 3
+#define MAX98926_ER_ALCP_EN_MASK (1<<3)
+#define MAX98926_ER_ALCP_EN_SHIFT 3
+#define MAX98926_ER_ALCP_EN_WIDTH 1
+#define MAX98926_ER_ALCP_MAP_MASK (0x07<<0)
+#define MAX98926_ER_ALCP_MAP_SHIFT 0
+#define MAX98926_ER_ALCP_MAP_WIDTH 3
+
+/* MAX98926_R013_MAP2 */
+#define MAX98926_ER_ALCINFH_EN_MASK (1<<7)
+#define MAX98926_ER_ALCINFH_EN_SHIFT 7
+#define MAX98926_ER_ALCINFH_EN_WIDTH 1
+#define MAX98926_ER_ALCINFH_MAP_MASK (0x07<<4)
+#define MAX98926_ER_ALCINFH_MAP_SHIFT 4
+#define MAX98926_ER_ALCINFH_MAP_WIDTH 3
+#define MAX98926_ER_ALCACT_EN_MASK (1<<3)
+#define MAX98926_ER_ALCACT_EN_SHIFT 3
+#define MAX98926_ER_ALCACT_EN_WIDTH 1
+#define MAX98926_ER_ALCACT_MAP_MASK (0x07<<0)
+#define MAX98926_ER_ALCACT_MAP_SHIFT 0
+#define MAX98926_ER_ALCACT_MAP_WIDTH 3
+
+/* MAX98926_R014_MAP3 */
+#define MAX98926_ER_SPKCURNT_EN_MASK (1<<7)
+#define MAX98926_ER_SPKCURNT_EN_SHIFT 7
+#define MAX98926_ER_SPKCURNT_EN_WIDTH 1
+#define MAX98926_ER_SPKCURNT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_SPKCURNT_MAP_SHIFT 4
+#define MAX98926_ER_SPKCURNT_MAP_WIDTH 3
+
+/* MAX98926_R015_MAP4 */
+/* RESERVED */
+
+/* MAX98926_R016_MAP5 */
+#define MAX98926_ER_IMONOVFL_EN_MASK (1<<7)
+#define MAX98926_ER_IMONOVFL_EN_SHIFT 7
+#define MAX98926_ER_IMONOVFL_EN_WIDTH 1
+#define MAX98926_ER_IMONOVFL_MAP_MASK (0x07<<4)
+#define MAX98926_ER_IMONOVFL_MAP_SHIFT 4
+#define MAX98926_ER_IMONOVFL_MAP_WIDTH 3
+#define MAX98926_ER_VMONOVFL_EN_MASK (1<<3)
+#define MAX98926_ER_VMONOVFL_EN_SHIFT 3
+#define MAX98926_ER_VMONOVFL_EN_WIDTH 1
+#define MAX98926_ER_VMONOVFL_MAP_MASK (0x07<<0)
+#define MAX98926_ER_VMONOVFL_MAP_SHIFT 0
+#define MAX98926_ER_VMONOVFL_MAP_WIDTH 3
+
+/* MAX98926_R017_MAP6 */
+#define MAX98926_ER_VBSTOVFL_EN_MASK (1<<7)
+#define MAX98926_ER_VBSTOVFL_EN_SHIFT 7
+#define MAX98926_ER_VBSTOVFL_EN_WIDTH 1
+#define MAX98926_ER_VBSTOVFL_MAP_MASK (0x07<<4)
+#define MAX98926_ER_VBSTOVFL_MAP_SHIFT 4
+#define MAX98926_ER_VBSTOVFL_MAP_WIDTH 3
+#define MAX98926_ER_VBATOVFL_EN_MASK (1<<3)
+#define MAX98926_ER_VBATOVFL_EN_SHIFT 3
+#define MAX98926_ER_VBATOVFL_EN_WIDTH 1
+#define MAX98926_ER_VBATOVFL_MAP_MASK (0x07<<0)
+#define MAX98926_ER_VBATOVFL_MAP_SHIFT 0
+#define MAX98926_ER_VBATOVFL_MAP_WIDTH 3
+
+/* MAX98926_R018_MAP7 */
+#define MAX98926_ER_INVALSLOT_EN_MASK (1<<7)
+#define MAX98926_ER_INVALSLOT_EN_SHIFT 7
+#define MAX98926_ER_INVALSLOT_EN_WIDTH 1
+#define MAX98926_ER_INVALSLOT_MAP_MASK (0x07<<4)
+#define MAX98926_ER_INVALSLOT_MAP_SHIFT 4
+#define MAX98926_ER_INVALSLOT_MAP_WIDTH 3
+#define MAX98926_ER_SLOTCNFLT_EN_MASK (1<<3)
+#define MAX98926_ER_SLOTCNFLT_EN_SHIFT 3
+#define MAX98926_ER_SLOTCNFLT_EN_WIDTH 1
+#define MAX98926_ER_SLOTCNFLT_MAP_MASK (0x07<<0)
+#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT 0
+#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH 3
+
+/* MAX98926_R019_MAP8 */
+#define MAX98926_ER_SLOTOVRN_EN_MASK (1<<3)
+#define MAX98926_ER_SLOTOVRN_EN_SHIFT 3
+#define MAX98926_ER_SLOTOVRN_EN_WIDTH 1
+#define MAX98926_ER_SLOTOVRN_MAP_MASK (0x07<<0)
+#define MAX98926_ER_SLOTOVRN_MAP_SHIFT 0
+#define MAX98926_ER_SLOTOVRN_MAP_WIDTH 3
+
+/* MAX98926_R01A_DAI_CLK_MODE1 */
+#define MAX98926_DAI_CLK_SOURCE_MASK (1<<6)
+#define MAX98926_DAI_CLK_SOURCE_SHIFT 6
+#define MAX98926_DAI_CLK_SOURCE_WIDTH 1
+#define MAX98926_MDLL_MULT_MASK (0x0F<<0)
+#define MAX98926_MDLL_MULT_SHIFT 0
+#define MAX98926_MDLL_MULT_WIDTH 4
+
+#define MAX98926_MDLL_MULT_MCLKx8 6
+#define MAX98926_MDLL_MULT_MCLKx16 8
+
+/* MAX98926_R01B_DAI_CLK_MODE2 */
+#define MAX98926_DAI_SR_MASK (0x0F<<4)
+#define MAX98926_DAI_SR_SHIFT 4
+#define MAX98926_DAI_SR_WIDTH 4
+#define MAX98926_DAI_MAS_MASK (1<<3)
+#define MAX98926_DAI_MAS_SHIFT 3
+#define MAX98926_DAI_MAS_WIDTH 1
+#define MAX98926_DAI_BSEL_MASK (0x07<<0)
+#define MAX98926_DAI_BSEL_SHIFT 0
+#define MAX98926_DAI_BSEL_WIDTH 3
+
+#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT)
+
+/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */
+#define MAX98926_DAI_M_MSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_M_MSBS_SHIFT 0
+#define MAX98926_DAI_M_MSBS_WIDTH 8
+
+/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */
+#define MAX98926_DAI_M_LSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_M_LSBS_SHIFT 0
+#define MAX98926_DAI_M_LSBS_WIDTH 8
+
+/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */
+#define MAX98926_DAI_N_MSBS_MASK (0x7F<<0)
+#define MAX98926_DAI_N_MSBS_SHIFT 0
+#define MAX98926_DAI_N_MSBS_WIDTH 7
+
+/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */
+#define MAX98926_DAI_N_LSBS_MASK (0xFF<<0)
+#define MAX98926_DAI_N_LSBS_SHIFT 0
+#define MAX98926_DAI_N_LSBS_WIDTH 8
+
+/* MAX98926_R020_FORMAT */
+#define MAX98926_DAI_CHANSZ_MASK (0x03<<6)
+#define MAX98926_DAI_CHANSZ_SHIFT 6
+#define MAX98926_DAI_CHANSZ_WIDTH 2
+#define MAX98926_DAI_INTERLEAVE_MASK (1<<5)
+#define MAX98926_DAI_INTERLEAVE_SHIFT 5
+#define MAX98926_DAI_INTERLEAVE_WIDTH 1
+#define MAX98926_DAI_EXTBCLK_HIZ_MASK (1<<4)
+#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT 4
+#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH 1
+#define MAX98926_DAI_WCI_MASK (1<<3)
+#define MAX98926_DAI_WCI_SHIFT 3
+#define MAX98926_DAI_WCI_WIDTH 1
+#define MAX98926_DAI_BCI_MASK (1<<2)
+#define MAX98926_DAI_BCI_SHIFT 2
+#define MAX98926_DAI_BCI_WIDTH 1
+#define MAX98926_DAI_DLY_MASK (1<<1)
+#define MAX98926_DAI_DLY_SHIFT 1
+#define MAX98926_DAI_DLY_WIDTH 1
+#define MAX98926_DAI_TDM_MASK (1<<0)
+#define MAX98926_DAI_TDM_SHIFT 0
+#define MAX98926_DAI_TDM_WIDTH 1
+
+#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT)
+
+/* MAX98926_R021_TDM_SLOT_SELECT */
+#define MAX98926_DAI_DO_EN_MASK (1<<7)
+#define MAX98926_DAI_DO_EN_SHIFT 7
+#define MAX98926_DAI_DO_EN_WIDTH 1
+#define MAX98926_DAI_DIN_EN_MASK (1<<6)
+#define MAX98926_DAI_DIN_EN_SHIFT 6
+#define MAX98926_DAI_DIN_EN_WIDTH 1
+#define MAX98926_DAI_INR_SOURCE_MASK (0x07<<3)
+#define MAX98926_DAI_INR_SOURCE_SHIFT 3
+#define MAX98926_DAI_INR_SOURCE_WIDTH 3
+#define MAX98926_DAI_INL_SOURCE_MASK (0x07<<0)
+#define MAX98926_DAI_INL_SOURCE_SHIFT 0
+#define MAX98926_DAI_INL_SOURCE_WIDTH 3
+
+/* MAX98926_R022_DOUT_CFG_VMON */
+#define MAX98926_DAI_VMON_EN_MASK (1<<5)
+#define MAX98926_DAI_VMON_EN_SHIFT 5
+#define MAX98926_DAI_VMON_EN_WIDTH 1
+#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_VMON_SLOT_SHIFT 0
+#define MAX98926_DAI_VMON_SLOT_WIDTH 5
+
+#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT)
+
+/* MAX98926_R023_DOUT_CFG_IMON */
+#define MAX98926_DAI_IMON_EN_MASK (1<<5)
+#define MAX98926_DAI_IMON_EN_SHIFT 5
+#define MAX98926_DAI_IMON_EN_WIDTH 1
+#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_IMON_SLOT_SHIFT 0
+#define MAX98926_DAI_IMON_SLOT_WIDTH 5
+
+#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT)
+
+/* MAX98926_R024_DOUT_CFG_VBAT */
+#define MAX98926_DAI_INTERLEAVE_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT 0
+#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH 5
+
+/* MAX98926_R025_DOUT_CFG_VBST */
+#define MAX98926_DAI_VBST_EN_MASK (1<<5)
+#define MAX98926_DAI_VBST_EN_SHIFT 5
+#define MAX98926_DAI_VBST_EN_WIDTH 1
+#define MAX98926_DAI_VBST_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_VBST_SLOT_SHIFT 0
+#define MAX98926_DAI_VBST_SLOT_WIDTH 5
+
+/* MAX98926_R026_DOUT_CFG_FLAG */
+#define MAX98926_DAI_FLAG_EN_MASK (1<<5)
+#define MAX98926_DAI_FLAG_EN_SHIFT 5
+#define MAX98926_DAI_FLAG_EN_WIDTH 1
+#define MAX98926_DAI_FLAG_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_FLAG_SLOT_SHIFT 0
+#define MAX98926_DAI_FLAG_SLOT_WIDTH 5
+
+/* MAX98926_R027_DOUT_HIZ_CFG1 */
+#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH 8
+
+/* MAX98926_R028_DOUT_HIZ_CFG2 */
+#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH 8
+
+/* MAX98926_R029_DOUT_HIZ_CFG3 */
+#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH 8
+
+/* MAX98926_R02A_DOUT_HIZ_CFG4 */
+#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT 0
+#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH 8
+
+/* MAX98926_R02B_DOUT_DRV_STRENGTH */
+#define MAX98926_DAI_OUT_DRIVE_MASK (0x03<<0)
+#define MAX98926_DAI_OUT_DRIVE_SHIFT 0
+#define MAX98926_DAI_OUT_DRIVE_WIDTH 2
+
+/* MAX98926_R02C_FILTERS */
+#define MAX98926_ADC_DITHER_EN_MASK (1<<7)
+#define MAX98926_ADC_DITHER_EN_SHIFT 7
+#define MAX98926_ADC_DITHER_EN_WIDTH 1
+#define MAX98926_IV_DCB_EN_MASK (1<<6)
+#define MAX98926_IV_DCB_EN_SHIFT 6
+#define MAX98926_IV_DCB_EN_WIDTH 1
+#define MAX98926_DAC_DITHER_EN_MASK (1<<4)
+#define MAX98926_DAC_DITHER_EN_SHIFT 4
+#define MAX98926_DAC_DITHER_EN_WIDTH 1
+#define MAX98926_DAC_FILTER_MODE_MASK (1<<3)
+#define MAX98926_DAC_FILTER_MODE_SHIFT 3
+#define MAX98926_DAC_FILTER_MODE_WIDTH 1
+#define MAX98926_DAC_HPF_MASK (0x07<<0)
+#define MAX98926_DAC_HPF_SHIFT 0
+#define MAX98926_DAC_HPF_WIDTH 3
+#define MAX98926_DAC_HPF_DISABLE (0 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_DC_BLOCK (1 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_100 (2 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_200 (3 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_400 (4 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_800 (5 << MAX98926_DAC_HPF_SHIFT)
+
+/* MAX98926_R02D_GAIN */
+#define MAX98926_DAC_IN_SEL_MASK (0x03<<5)
+#define MAX98926_DAC_IN_SEL_SHIFT 5
+#define MAX98926_DAC_IN_SEL_WIDTH 2
+#define MAX98926_SPK_GAIN_MASK (0x1F<<0)
+#define MAX98926_SPK_GAIN_SHIFT 0
+#define MAX98926_SPK_GAIN_WIDTH 5
+
+#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT)
+
+/* MAX98926_R02E_GAIN_RAMPING */
+#define MAX98926_SPK_RMP_EN_MASK (1<<1)
+#define MAX98926_SPK_RMP_EN_SHIFT 1
+#define MAX98926_SPK_RMP_EN_WIDTH 1
+#define MAX98926_SPK_ZCD_EN_MASK (1<<0)
+#define MAX98926_SPK_ZCD_EN_SHIFT 0
+#define MAX98926_SPK_ZCD_EN_WIDTH 1
+
+/* MAX98926_R02F_SPK_AMP */
+#define MAX98926_SPK_MODE_MASK (1<<0)
+#define MAX98926_SPK_MODE_SHIFT 0
+#define MAX98926_SPK_MODE_WIDTH 1
+#define MAX98926_INSELECT_MODE_MASK (1<<1)
+#define MAX98926_INSELECT_MODE_SHIFT 1
+#define MAX98926_INSELECT_MODE_WIDTH 1
+
+/* MAX98926_R030_THRESHOLD */
+#define MAX98926_ALC_EN_MASK (1<<5)
+#define MAX98926_ALC_EN_SHIFT 5
+#define MAX98926_ALC_EN_WIDTH 1
+#define MAX98926_ALC_TH_MASK (0x1F<<0)
+#define MAX98926_ALC_TH_SHIFT 0
+#define MAX98926_ALC_TH_WIDTH 5
+
+/* MAX98926_R031_ALC_ATTACK */
+#define MAX98926_ALC_ATK_STEP_MASK (0x0F<<4)
+#define MAX98926_ALC_ATK_STEP_SHIFT 4
+#define MAX98926_ALC_ATK_STEP_WIDTH 4
+#define MAX98926_ALC_ATK_RATE_MASK (0x7<<0)
+#define MAX98926_ALC_ATK_RATE_SHIFT 0
+#define MAX98926_ALC_ATK_RATE_WIDTH 3
+
+/* MAX98926_R032_ALC_ATTEN_RLS */
+#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4)
+#define MAX98926_ALC_MAX_ATTEN_SHIFT 4
+#define MAX98926_ALC_MAX_ATTEN_WIDTH 4
+#define MAX98926_ALC_RLS_RATE_MASK (0x7<<0)
+#define MAX98926_ALC_RLS_RATE_SHIFT 0
+#define MAX98926_ALC_RLS_RATE_WIDTH 3
+
+/* MAX98926_R033_ALC_HOLD_RLS */
+#define MAX98926_ALC_RLS_TGR_MASK (1<<0)
+#define MAX98926_ALC_RLS_TGR_SHIFT 0
+#define MAX98926_ALC_RLS_TGR_WIDTH 1
+
+/* MAX98926_R034_ALC_CONFIGURATION */
+#define MAX98926_ALC_MUTE_EN_MASK (1<<7)
+#define MAX98926_ALC_MUTE_EN_SHIFT 7
+#define MAX98926_ALC_MUTE_EN_WIDTH 1
+#define MAX98926_ALC_MUTE_DLY_MASK (0x07<<4)
+#define MAX98926_ALC_MUTE_DLY_SHIFT 4
+#define MAX98926_ALC_MUTE_DLY_WIDTH 3
+#define MAX98926_ALC_RLS_DBT_MASK (0x07<<0)
+#define MAX98926_ALC_RLS_DBT_SHIFT 0
+#define MAX98926_ALC_RLS_DBT_WIDTH 3
+
+/* MAX98926_R035_BOOST_CONVERTER */
+#define MAX98926_BST_SYNC_MASK (1<<7)
+#define MAX98926_BST_SYNC_SHIFT 7
+#define MAX98926_BST_SYNC_WIDTH 1
+#define MAX98926_BST_PHASE_MASK (0x03<<4)
+#define MAX98926_BST_PHASE_SHIFT 4
+#define MAX98926_BST_PHASE_WIDTH 2
+#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0)
+#define MAX98926_BST_SKIP_MODE_SHIFT 0
+#define MAX98926_BST_SKIP_MODE_WIDTH 2
+
+/* MAX98926_R036_BLOCK_ENABLE */
+#define MAX98926_BST_EN_MASK (1<<7)
+#define MAX98926_BST_EN_SHIFT 7
+#define MAX98926_BST_EN_WIDTH 1
+#define MAX98926_WATCH_EN_MASK (1<<6)
+#define MAX98926_WATCH_EN_SHIFT 6
+#define MAX98926_WATCH_EN_WIDTH 1
+#define MAX98926_CLKMON_EN_MASK (1<<5)
+#define MAX98926_CLKMON_EN_SHIFT 5
+#define MAX98926_CLKMON_EN_WIDTH 1
+#define MAX98926_SPK_EN_MASK (1<<4)
+#define MAX98926_SPK_EN_SHIFT 4
+#define MAX98926_SPK_EN_WIDTH 1
+#define MAX98926_ADC_VBST_EN_MASK (1<<3)
+#define MAX98926_ADC_VBST_EN_SHIFT 3
+#define MAX98926_ADC_VBST_EN_WIDTH 1
+#define MAX98926_ADC_VBAT_EN_MASK (1<<2)
+#define MAX98926_ADC_VBAT_EN_SHIFT 2
+#define MAX98926_ADC_VBAT_EN_WIDTH 1
+#define MAX98926_ADC_IMON_EN_MASK (1<<1)
+#define MAX98926_ADC_IMON_EN_SHIFT 1
+#define MAX98926_ADC_IMON_EN_WIDTH 1
+#define MAX98926_ADC_VMON_EN_MASK (1<<0)
+#define MAX98926_ADC_VMON_EN_SHIFT 0
+#define MAX98926_ADC_VMON_EN_WIDTH 1
+
+/* MAX98926_R037_CONFIGURATION */
+#define MAX98926_BST_VOUT_MASK (0x0F<<4)
+#define MAX98926_BST_VOUT_SHIFT 4
+#define MAX98926_BST_VOUT_WIDTH 4
+#define MAX98926_THERMWARN_LEVEL_MASK (0x03<<2)
+#define MAX98926_THERMWARN_LEVEL_SHIFT 2
+#define MAX98926_THERMWARN_LEVEL_WIDTH 2
+#define MAX98926_WATCH_TIME_MASK (0x03<<0)
+#define MAX98926_WATCH_TIME_SHIFT 0
+#define MAX98926_WATCH_TIME_WIDTH 2
+
+/* MAX98926_R038_GLOBAL_ENABLE */
+#define MAX98926_EN_MASK (1<<7)
+#define MAX98926_EN_SHIFT 7
+#define MAX98926_EN_WIDTH 1
+
+/* MAX98926_R03A_BOOST_LIMITER */
+#define MAX98926_BST_ILIM_MASK (0xF<<4)
+#define MAX98926_BST_ILIM_SHIFT 4
+#define MAX98926_BST_ILIM_WIDTH 4
+
+/* MAX98926_R0FF_VERSION */
+#define MAX98926_REV_ID_MASK (0xFF<<0)
+#define MAX98926_REV_ID_SHIFT 0
+#define MAX98926_REV_ID_WIDTH 8
+
+struct max98926_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ unsigned int sysclk;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int ch_size;
+ unsigned int interleave_mode;
+};
+#endif
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index c1b87c5800b1..1c8729984c2b 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -84,6 +84,7 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = {
static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_ENA_CTRL, 0x00ff },
+ { NAU8825_REG_IIC_ADDR_SET, 0x0 },
{ NAU8825_REG_CLK_DIVIDER, 0x0050 },
{ NAU8825_REG_FLL1, 0x0 },
{ NAU8825_REG_FLL2, 0x3126 },
@@ -158,8 +159,7 @@ static const struct reg_default nau8825_reg_defaults[] = {
static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case NAU8825_REG_ENA_CTRL:
- case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
@@ -184,8 +184,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
- case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+ case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
case NAU8825_REG_INTERRUPT_MASK:
case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
@@ -227,10 +226,42 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
static int nau8825_pump_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:
/* Prevent startup click by letting charge pump to ramp up */
msleep(10);
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8825_output_dac_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_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
break;
default:
return -EINVAL;
@@ -316,10 +347,10 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
NAU8825_SAR_ADC_EN_SFT, 0),
- SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
- SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
- SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0),
SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
NAU8825_ENABLE_DACR_SFT, 0),
@@ -330,29 +361,48 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
- SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
- 0),
+ SND_SOC_DAPM_PGA_S("HP amp L", 0,
+ NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HP amp R", 0,
+ NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
- nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0,
+ nau8825_pump_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_PGA("Output Driver R Stage 1",
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Output Driver L Stage 1",
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Output Driver R Stage 2",
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Output Driver L Stage 2",
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1,
+ SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1,
+ SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
- SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("Output DACL", 7,
+ NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 7,
+ NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
+ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
+ NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
+ NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0),
+
+ /* High current HPOL/R boost driver */
+ SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
+ NAU8825_REG_BOOST, 9, 1, NULL, 0),
+
+ /* Class G operation control*/
+ SND_SOC_DAPM_PGA_S("Class G", 10,
+ NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HPOL"),
SND_SOC_DAPM_OUTPUT("HPOR"),
@@ -375,24 +425,27 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"DACR Mux", "DACR", "DDACR"},
{"HP amp L", NULL, "DACL Mux"},
{"HP amp R", NULL, "DACR Mux"},
- {"HP amp L", NULL, "HP amp power"},
- {"HP amp R", NULL, "HP amp power"},
- {"ADACL", NULL, "HP amp L"},
- {"ADACR", NULL, "HP amp R"},
- {"ADACL", NULL, "ADACL Clock"},
- {"ADACR", NULL, "ADACR Clock"},
- {"Output Driver L Stage 1", NULL, "ADACL"},
- {"Output Driver R Stage 1", NULL, "ADACR"},
+ {"Charge Pump", NULL, "HP amp L"},
+ {"Charge Pump", NULL, "HP amp R"},
+ {"ADACL", NULL, "Charge Pump"},
+ {"ADACR", NULL, "Charge Pump"},
+ {"ADACL Clock", NULL, "ADACL"},
+ {"ADACR Clock", NULL, "ADACR"},
+ {"Output Driver L Stage 1", NULL, "ADACL Clock"},
+ {"Output Driver R Stage 1", NULL, "ADACR Clock"},
{"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
{"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
{"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
{"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
{"Output DACL", NULL, "Output Driver L Stage 3"},
{"Output DACR", NULL, "Output Driver R Stage 3"},
- {"HPOL", NULL, "Output DACL"},
- {"HPOR", NULL, "Output DACR"},
- {"HPOL", NULL, "Charge Pump"},
- {"HPOR", NULL, "Charge Pump"},
+ {"HPOL Pulldown", NULL, "Output DACL"},
+ {"HPOR Pulldown", NULL, "Output DACR"},
+ {"HP Boost Driver", NULL, "HPOL Pulldown"},
+ {"HP Boost Driver", NULL, "HPOR Pulldown"},
+ {"Class G", NULL, "HP Boost Driver"},
+ {"HPOL", NULL, "Class G"},
+ {"HPOR", NULL, "Class G"},
};
static int nau8825_hw_params(struct snd_pcm_substream *substream,
@@ -659,11 +712,10 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
break;
}
- if (type & SND_JACK_HEADPHONE) {
- /* Unground HPL/R */
- regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
- }
-
+ /* Leaving HPOL/R grounded after jack insert by default. They will be
+ * ungrounded as part of the widget power up sequence at the beginning
+ * of playback to reduce pop.
+ */
return type;
}
@@ -768,6 +820,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
{
struct regmap *regmap = nau8825->regmap;
+ /* Latch IIC LSB value */
+ regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001);
/* Enable Bias/Vmid */
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
@@ -780,10 +834,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
/* Disable Boost Driver, Automatic Short circuit protection enable */
regmap_update_bits(regmap, NAU8825_REG_BOOST,
- NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
- NAU8825_SHORT_SHUTDOWN_EN,
- NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
- NAU8825_SHORT_SHUTDOWN_EN);
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+ NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN,
+ NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+ NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN);
regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
NAU8825_JKDET_OUTPUT_EN,
@@ -822,6 +876,35 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+ /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+ * signal to avoid any glitches due to power up transients in both
+ * the analog and digital DAC circuit.
+ */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+ NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+ /* CICCLP off */
+ regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+ NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF);
+
+ /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
+ regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2,
+ NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB,
+ NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+ NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL,
+ NAU8825_CLASSG_TIMER_MASK,
+ 0x20 << NAU8825_CLASSG_TIMER_SFT);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8825_REG_RDAC,
+ NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK,
+ (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8825_RDAC_VREF_SFT));
}
static const struct regmap_config nau8825_regmap_config = {
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index dff8edb83bfd..8ceb5f385478 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -14,6 +14,7 @@
#define NAU8825_REG_RESET 0x00
#define NAU8825_REG_ENA_CTRL 0x01
+#define NAU8825_REG_IIC_ADDR_SET 0x02
#define NAU8825_REG_CLK_DIVIDER 0x03
#define NAU8825_REG_FLL1 0x04
#define NAU8825_REG_FLL2 0x05
@@ -129,7 +130,7 @@
/* HSD_CTRL (0xc) */
#define NAU8825_HSD_AUTO_MODE (1 << 6)
-/* 0 - short to GND, 1 - open */
+/* 0 - open, 1 - short to GND */
#define NAU8825_SPKR_DWN1R (1 << 1)
#define NAU8825_SPKR_DWN1L (1 << 0)
@@ -251,12 +252,18 @@
/* DACR_CTRL (0x34) */
#define NAU8825_DACR_CH_SEL_SFT 9
+/* CLASSG_CTRL (0x50) */
+#define NAU8825_CLASSG_TIMER_SFT 8
+#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_EN (1 << 0)
+
/* I2C_DEVICE_ID (0x58) */
#define NAU8825_GPIO2JD1 (1 << 7)
#define NAU8825_SOFTWARE_ID_MASK 0x3
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
/* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8)
#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)
@@ -274,6 +281,12 @@
#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
#define NAU8825_POWERUP_ADCL (1 << 6)
+/* RDAC (0x73) */
+#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
+#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT)
+
/* MIC_BIAS (0x74) */
#define NAU8825_MICBIAS_JKSLV (1 << 14)
#define NAU8825_MICBIAS_JKR2 (1 << 12)
@@ -284,6 +297,7 @@
/* BOOST (0x76) */
#define NAU8825_PRECHARGE_DIS (1 << 13)
#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_HP_BOOST_DIS (1 << 9)
#define NAU8825_HP_BOOST_G_DIS (1 << 8)
#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
new file mode 100644
index 000000000000..4118106abb8d
--- /dev/null
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -0,0 +1,73 @@
+/*
+ * PCM179X ASoC I2C driver
+ *
+ * Copyright (c) Teenage Engineering AB 2016
+ *
+ * Jacob Siverskog <jacob@teenage.engineering>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm179x_common_init(&client->dev, regmap);
+}
+
+static int pcm179x_i2c_remove(struct i2c_client *client)
+{
+ return pcm179x_common_exit(&client->dev);
+}
+
+static const struct of_device_id pcm179x_of_match[] = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+
+static const struct i2c_device_id pcm179x_i2c_ids[] = {
+ { "pcm179x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids);
+
+static struct i2c_driver pcm179x_i2c_driver = {
+ .driver = {
+ .name = "pcm179x",
+ .of_match_table = of_match_ptr(pcm179x_of_match),
+ },
+ .id_table = pcm179x_i2c_ids,
+ .probe = pcm179x_i2c_probe,
+ .remove = pcm179x_i2c_remove,
+};
+
+module_i2c_driver(pcm179x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X I2C driver");
+MODULE_AUTHOR("Jacob Siverskog <jacob@teenage.engineering>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
new file mode 100644
index 000000000000..da924d444083
--- /dev/null
+++ b/sound/soc/codecs/pcm179x-spi.c
@@ -0,0 +1,72 @@
+/*
+ * PCM179X ASoC SPI driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ * Michael Trimarchi <michael@amarulasolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_spi(spi, &pcm179x_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ return pcm179x_common_init(&spi->dev, regmap);
+}
+
+static int pcm179x_spi_remove(struct spi_device *spi)
+{
+ return pcm179x_common_exit(&spi->dev);
+}
+
+static const struct of_device_id pcm179x_of_match[] = {
+ { .compatible = "ti,pcm1792a", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+
+static const struct spi_device_id pcm179x_spi_ids[] = {
+ { "pcm179x", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
+
+static struct spi_driver pcm179x_spi_driver = {
+ .driver = {
+ .name = "pcm179x",
+ .of_match_table = of_match_ptr(pcm179x_of_match),
+ },
+ .id_table = pcm179x_spi_ids,
+ .probe = pcm179x_spi_probe,
+ .remove = pcm179x_spi_remove,
+};
+
+module_spi_driver(pcm179x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X SPI driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
index a56c7b767d90..06a66579ca6d 100644
--- a/sound/soc/codecs/pcm179x.c
+++ b/sound/soc/codecs/pcm179x.c
@@ -20,7 +20,6 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -29,7 +28,6 @@
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include "pcm179x.h"
@@ -189,18 +187,14 @@ static struct snd_soc_dai_driver pcm179x_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = PCM1792A_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 10000,
+ .rate_max = 200000,
.formats = PCM1792A_FORMATS, },
.ops = &pcm179x_dai_ops,
};
-static const struct of_device_id pcm179x_of_match[] = {
- { .compatible = "ti,pcm1792a", },
- { }
-};
-MODULE_DEVICE_TABLE(of, pcm179x_of_match);
-
-static const struct regmap_config pcm179x_regmap = {
+const struct regmap_config pcm179x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 23,
@@ -209,6 +203,7 @@ static const struct regmap_config pcm179x_regmap = {
.writeable_reg = pcm179x_writeable_reg,
.readable_reg = pcm179x_accessible_reg,
};
+EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
.controls = pcm179x_controls,
@@ -219,52 +214,29 @@ static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
.num_dapm_routes = ARRAY_SIZE(pcm179x_dapm_routes),
};
-static int pcm179x_spi_probe(struct spi_device *spi)
+int pcm179x_common_init(struct device *dev, struct regmap *regmap)
{
struct pcm179x_private *pcm179x;
- int ret;
- pcm179x = devm_kzalloc(&spi->dev, sizeof(struct pcm179x_private),
+ pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private),
GFP_KERNEL);
if (!pcm179x)
return -ENOMEM;
- spi_set_drvdata(spi, pcm179x);
-
- pcm179x->regmap = devm_regmap_init_spi(spi, &pcm179x_regmap);
- if (IS_ERR(pcm179x->regmap)) {
- ret = PTR_ERR(pcm179x->regmap);
- dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
- return ret;
- }
+ pcm179x->regmap = regmap;
+ dev_set_drvdata(dev, pcm179x);
- return snd_soc_register_codec(&spi->dev,
+ return snd_soc_register_codec(dev,
&soc_codec_dev_pcm179x, &pcm179x_dai, 1);
}
+EXPORT_SYMBOL_GPL(pcm179x_common_init);
-static int pcm179x_spi_remove(struct spi_device *spi)
+int pcm179x_common_exit(struct device *dev)
{
- snd_soc_unregister_codec(&spi->dev);
+ snd_soc_unregister_codec(dev);
return 0;
}
-
-static const struct spi_device_id pcm179x_spi_ids[] = {
- { "pcm179x", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
-
-static struct spi_driver pcm179x_codec_driver = {
- .driver = {
- .name = "pcm179x",
- .of_match_table = of_match_ptr(pcm179x_of_match),
- },
- .id_table = pcm179x_spi_ids,
- .probe = pcm179x_spi_probe,
- .remove = pcm179x_spi_remove,
-};
-
-module_spi_driver(pcm179x_codec_driver);
+EXPORT_SYMBOL_GPL(pcm179x_common_exit);
MODULE_DESCRIPTION("ASoC PCM179X driver");
MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h
index c6fdc062a497..11e331268aae 100644
--- a/sound/soc/codecs/pcm179x.h
+++ b/sound/soc/codecs/pcm179x.h
@@ -17,11 +17,12 @@
#ifndef __PCM179X_H__
#define __PCM179X_H__
-#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
- SNDRV_PCM_RATE_192000)
-
#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE)
+extern const struct regmap_config pcm179x_regmap_config;
+
+int pcm179x_common_init(struct device *dev, struct regmap *regmap);
+int pcm179x_common_exit(struct device *dev);
+
#endif
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 44b268aa4dd8..992a77edcd5d 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -299,10 +299,15 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
+ int ret;
if (freq > PCM1368A_MAX_SYSCLK)
return -EINVAL;
+ ret = clk_set_rate(pcm3168a->scki, freq);
+ if (ret)
+ return ret;
+
pcm3168a->sysclk = freq;
return 0;
@@ -395,13 +400,12 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
bool tx, master_mode;
u32 val, mask, shift, reg;
- unsigned int rate, channels, fmt, ratio, max_ratio;
+ unsigned int rate, fmt, ratio, max_ratio;
int i, min_frame_size;
snd_pcm_format_t format;
rate = params_rate(params);
format = params_format(params);
- channels = params_channels(params);
ratio = pcm3168a->sysclk / rate;
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 30c6de62ae6c..f0e6c06e89ac 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1224,7 +1224,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000);
regmap_update_bits(rt298->regmap,
RT298_WIND_FILTER_CTRL, 0x0082, 0x0082);
- regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
+
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81);
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82);
+ regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84);
+ regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2);
+
rt298->is_hp_in = -1;
if (rt298->i2c->irq) {
diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h
index 31da16265f2b..d66f8847b676 100644
--- a/sound/soc/codecs/rt298.h
+++ b/sound/soc/codecs/rt298.h
@@ -34,6 +34,7 @@
#define RT298_HP_OUT 0x21
#define RT298_MIXER_IN1 0x22
#define RT298_MIXER_IN2 0x23
+#define RT298_INLINE_CMD 0x55
#define RT298_SET_PIN_SFT 6
#define RT298_SET_PIN_ENABLE 0x40
@@ -124,6 +125,12 @@
VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0)
#define RT298_PROC_COEF\
VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0)
+#define RT298_UNSOLICITED_INLINE_CMD\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0)
+#define RT298_UNSOLICITED_HP_OUT\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0)
+#define RT298_UNSOLICITED_MIC1\
+ VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0)
/* Index registers */
#define RT298_A_BIAS_CTRL1 0x01
@@ -148,6 +155,7 @@
#define RT298_DEPOP_CTRL2 0x67
#define RT298_DEPOP_CTRL3 0x68
#define RT298_DEPOP_CTRL4 0x69
+#define RT298_IRQ_FLAG_CTRL 0x7c
/* SPDIF (0x06) */
#define RT298_SPDIF_SEL_SFT 0
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
new file mode 100644
index 000000000000..879bf60f4965
--- /dev/null
+++ b/sound/soc/codecs/rt5514.c
@@ -0,0 +1,982 @@
+/*
+ * rt5514.c -- RT5514 ALSA SoC audio codec 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/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.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 "rl6231.h"
+#include "rt5514.h"
+
+static const struct reg_sequence rt5514_i2c_patch[] = {
+ {0x1800101c, 0x00000000},
+ {0x18001100, 0x0000031f},
+ {0x18001104, 0x00000007},
+ {0x18001108, 0x00000000},
+ {0x1800110c, 0x00000000},
+ {0x18001110, 0x00000000},
+ {0x18001114, 0x00000001},
+ {0x18001118, 0x00000000},
+ {0x18002f08, 0x00000006},
+ {0x18002f00, 0x00055149},
+ {0x18002f00, 0x0005514b},
+ {0x18002f00, 0x00055149},
+ {0xfafafafa, 0x00000001},
+ {0x18002f10, 0x00000001},
+ {0x18002f10, 0x00000000},
+ {0x18002f10, 0x00000001},
+ {0xfafafafa, 0x00000001},
+ {0x18002000, 0x000010ec},
+ {0xfafafafa, 0x00000000},
+};
+
+static const struct reg_sequence rt5514_patch[] = {
+ {RT5514_DIG_IO_CTRL, 0x00000040},
+ {RT5514_CLK_CTRL1, 0x38020041},
+ {RT5514_SRC_CTRL, 0x44000eee},
+ {RT5514_ANA_CTRL_LDO10, 0x00028604},
+ {RT5514_ANA_CTRL_ADCFED, 0x00000800},
+};
+
+static const struct reg_default rt5514_reg[] = {
+ {RT5514_RESET, 0x00000000},
+ {RT5514_PWR_ANA1, 0x00808880},
+ {RT5514_PWR_ANA2, 0x00220000},
+ {RT5514_I2S_CTRL1, 0x00000330},
+ {RT5514_I2S_CTRL2, 0x20000000},
+ {RT5514_VAD_CTRL6, 0xc00007d2},
+ {RT5514_EXT_VAD_CTRL, 0x80000080},
+ {RT5514_DIG_IO_CTRL, 0x00000040},
+ {RT5514_PAD_CTRL1, 0x00804000},
+ {RT5514_DMIC_DATA_CTRL, 0x00000005},
+ {RT5514_DIG_SOURCE_CTRL, 0x00000002},
+ {RT5514_SRC_CTRL, 0x44000eee},
+ {RT5514_DOWNFILTER2_CTRL1, 0x0000882f},
+ {RT5514_PLL_SOURCE_CTRL, 0x00000004},
+ {RT5514_CLK_CTRL1, 0x38020041},
+ {RT5514_CLK_CTRL2, 0x00000000},
+ {RT5514_PLL3_CALIB_CTRL1, 0x00400200},
+ {RT5514_PLL3_CALIB_CTRL5, 0x40220012},
+ {RT5514_DELAY_BUF_CTRL1, 0x7fff006a},
+ {RT5514_DELAY_BUF_CTRL3, 0x00000000},
+ {RT5514_DOWNFILTER0_CTRL1, 0x00020c2f},
+ {RT5514_DOWNFILTER0_CTRL2, 0x00020c2f},
+ {RT5514_DOWNFILTER0_CTRL3, 0x00000362},
+ {RT5514_DOWNFILTER1_CTRL1, 0x00020c2f},
+ {RT5514_DOWNFILTER1_CTRL2, 0x00020c2f},
+ {RT5514_DOWNFILTER1_CTRL3, 0x00000362},
+ {RT5514_ANA_CTRL_LDO10, 0x00028604},
+ {RT5514_ANA_CTRL_LDO18_16, 0x02000345},
+ {RT5514_ANA_CTRL_ADC12, 0x0000a2a8},
+ {RT5514_ANA_CTRL_ADC21, 0x00001180},
+ {RT5514_ANA_CTRL_ADC22, 0x0000aaa8},
+ {RT5514_ANA_CTRL_ADC23, 0x00151427},
+ {RT5514_ANA_CTRL_MICBST, 0x00002000},
+ {RT5514_ANA_CTRL_ADCFED, 0x00000800},
+ {RT5514_ANA_CTRL_INBUF, 0x00000143},
+ {RT5514_ANA_CTRL_VREF, 0x00008d50},
+ {RT5514_ANA_CTRL_PLL3, 0x0000000e},
+ {RT5514_ANA_CTRL_PLL1_1, 0x00000000},
+ {RT5514_ANA_CTRL_PLL1_2, 0x00030220},
+ {RT5514_DMIC_LP_CTRL, 0x00000000},
+ {RT5514_MISC_CTRL_DSP, 0x00000000},
+ {RT5514_DSP_CTRL1, 0x00055149},
+ {RT5514_DSP_CTRL3, 0x00000006},
+ {RT5514_DSP_CTRL4, 0x00000001},
+ {RT5514_VENDOR_ID1, 0x00000001},
+ {RT5514_VENDOR_ID2, 0x10ec5514},
+};
+
+static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_VENDOR_ID1:
+ case RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt5514_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_RESET:
+ case RT5514_PWR_ANA1:
+ case RT5514_PWR_ANA2:
+ case RT5514_I2S_CTRL1:
+ case RT5514_I2S_CTRL2:
+ case RT5514_VAD_CTRL6:
+ case RT5514_EXT_VAD_CTRL:
+ case RT5514_DIG_IO_CTRL:
+ case RT5514_PAD_CTRL1:
+ case RT5514_DMIC_DATA_CTRL:
+ case RT5514_DIG_SOURCE_CTRL:
+ case RT5514_SRC_CTRL:
+ case RT5514_DOWNFILTER2_CTRL1:
+ case RT5514_PLL_SOURCE_CTRL:
+ case RT5514_CLK_CTRL1:
+ case RT5514_CLK_CTRL2:
+ case RT5514_PLL3_CALIB_CTRL1:
+ case RT5514_PLL3_CALIB_CTRL5:
+ case RT5514_DELAY_BUF_CTRL1:
+ case RT5514_DELAY_BUF_CTRL3:
+ case RT5514_DOWNFILTER0_CTRL1:
+ case RT5514_DOWNFILTER0_CTRL2:
+ case RT5514_DOWNFILTER0_CTRL3:
+ case RT5514_DOWNFILTER1_CTRL1:
+ case RT5514_DOWNFILTER1_CTRL2:
+ case RT5514_DOWNFILTER1_CTRL3:
+ case RT5514_ANA_CTRL_LDO10:
+ case RT5514_ANA_CTRL_LDO18_16:
+ case RT5514_ANA_CTRL_ADC12:
+ case RT5514_ANA_CTRL_ADC21:
+ case RT5514_ANA_CTRL_ADC22:
+ case RT5514_ANA_CTRL_ADC23:
+ case RT5514_ANA_CTRL_MICBST:
+ case RT5514_ANA_CTRL_ADCFED:
+ case RT5514_ANA_CTRL_INBUF:
+ case RT5514_ANA_CTRL_VREF:
+ case RT5514_ANA_CTRL_PLL3:
+ case RT5514_ANA_CTRL_PLL1_1:
+ case RT5514_ANA_CTRL_PLL1_2:
+ case RT5514_DMIC_LP_CTRL:
+ case RT5514_MISC_CTRL_DSP:
+ case RT5514_DSP_CTRL1:
+ case RT5514_DSP_CTRL3:
+ case RT5514_DSP_CTRL4:
+ case RT5514_VENDOR_ID1:
+ case RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt5514_i2c_readable_register(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case RT5514_DSP_MAPPING | RT5514_RESET:
+ case RT5514_DSP_MAPPING | RT5514_PWR_ANA1:
+ case RT5514_DSP_MAPPING | RT5514_PWR_ANA2:
+ case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6:
+ case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_SRC_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5:
+ case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2:
+ case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1:
+ case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2:
+ case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL:
+ case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3:
+ case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4:
+ case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1:
+ case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+ 0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
+ 4, 4, TLV_DB_SCALE_ITEM(750, 0, 0),
+ 5, 5, TLV_DB_SCALE_ITEM(950, 0, 0),
+ 6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0),
+ 7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0),
+ 8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 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),
+ SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
+ adc_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
+ adc_vol_tlv),
+};
+
+/* ADC Mixer*/
+static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_DMIC_MIX_BIT, 1, 1),
+ SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+/* DMIC Source */
+static const char * const rt5514_dmic_src[] = {
+ "DMIC1", "DMIC2"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+ RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
+ SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
+
+static const SOC_ENUM_SINGLE_DECL(
+ rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+ RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto2_dmic_mux =
+ SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum);
+
+/**
+ * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
+ *
+ * @rate: base clock rate.
+ *
+ * Choose divider parameter that gives the highest possible DMIC frequency in
+ * 1MHz - 3MHz range.
+ */
+static int rt5514_calc_dmic_clk(struct snd_soc_codec *codec, int rate)
+{
+ int div[] = {2, 3, 4, 8, 12, 16, 24, 32};
+ int i;
+
+ if (rate < 1000000 * div[0]) {
+ pr_warn("Base clock rate %d is too low\n", rate);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(div); i++) {
+ /* find divider that gives DMIC frequency below 3.072MHz */
+ if (3072000 * div[i] >= rate)
+ return i;
+ }
+
+ dev_warn(codec->dev, "Base clock rate %d is too high\n", rate);
+ return -EINVAL;
+}
+
+static int rt5514_set_dmic_clk(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);
+ int idx;
+
+ idx = rt5514_calc_dmic_clk(codec, rt5514->sysclk);
+ if (idx < 0)
+ dev_err(codec->dev, "Failed to set DMIC clock\n");
+ else
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
+ RT5514_CLK_DMIC_OUT_SEL_MASK,
+ idx << RT5514_CLK_DMIC_OUT_SEL_SFT);
+
+ return idx;
+}
+
+static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1)
+ return 1;
+ else
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1L"),
+ SND_SOC_DAPM_INPUT("DMIC1R"),
+ SND_SOC_DAPM_INPUT("DMIC2L"),
+ SND_SOC_DAPM_INPUT("DMIC2R"),
+
+ SND_SOC_DAPM_INPUT("AMICL"),
+ SND_SOC_DAPM_INPUT("AMICR"),
+
+ SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+ rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1,
+ RT5514_POW_LDO18_IN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1,
+ RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1,
+ RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1,
+ RT5514_POW_BG_LDO21_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2,
+ RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+
+ SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2,
+ RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2,
+ RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0,
+ NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5514_sto1_dmic_mux),
+ SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
+ &rt5514_sto2_dmic_mux),
+
+ /* ADC Mixer */
+ SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD0_EN_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1,
+ RT5514_CLK_AD1_EN_BIT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+ rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)),
+ SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)),
+
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1,
+ RT5514_AD_AD_MUTE_BIT, 1),
+ SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2,
+ RT5514_AD_AD_MUTE_BIT, 1),
+
+ /* ADC PGA */
+ SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
+ { "DMIC1", NULL, "DMIC1L" },
+ { "DMIC1", NULL, "DMIC1R" },
+ { "DMIC2", NULL, "DMIC2L" },
+ { "DMIC2", NULL, "DMIC2R" },
+
+ { "DMIC1L", NULL, "DMIC CLK" },
+ { "DMIC1R", NULL, "DMIC CLK" },
+ { "DMIC2L", NULL, "DMIC CLK" },
+ { "DMIC2R", NULL, "DMIC CLK" },
+
+ { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+
+ { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" },
+ { "Sto1 ADC MIXL", "ADC Switch", "AMICL" },
+ { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" },
+ { "Sto1 ADC MIXR", "ADC Switch", "AMICR" },
+
+ { "ADC Power", NULL, "LDO18 IN" },
+ { "ADC Power", NULL, "LDO18 ADC" },
+ { "ADC Power", NULL, "LDO21" },
+ { "ADC Power", NULL, "BG LDO18 IN" },
+ { "ADC Power", NULL, "BG LDO21" },
+ { "ADC Power", NULL, "BG MBIAS" },
+ { "ADC Power", NULL, "MBIAS" },
+ { "ADC Power", NULL, "VREF2" },
+ { "ADC Power", NULL, "VREF1" },
+
+ { "ADCL Power", NULL, "LDO16L" },
+ { "ADCL Power", NULL, "ADC1L" },
+ { "ADCL Power", NULL, "BSTL2" },
+ { "ADCL Power", NULL, "BSTL" },
+ { "ADCL Power", NULL, "ADCFEDL" },
+
+ { "ADCR Power", NULL, "LDO16R" },
+ { "ADCR Power", NULL, "ADC1R" },
+ { "ADCR Power", NULL, "BSTR2" },
+ { "ADCR Power", NULL, "BSTR" },
+ { "ADCR Power", NULL, "ADCFEDR" },
+
+ { "AMICL", NULL, "ADC CLK" },
+ { "AMICL", NULL, "ADC Power" },
+ { "AMICL", NULL, "ADCL Power" },
+ { "AMICR", NULL, "ADC CLK" },
+ { "AMICR", NULL, "ADC Power" },
+ { "AMICR", NULL, "ADCR Power" },
+
+ { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" },
+ { "PLL1", NULL, "PLL1 LDO" },
+
+ { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+ { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
+ { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
+ { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" },
+ { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+
+ { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
+ { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
+
+ { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" },
+ { "Sto2 ADC MIXL", "ADC Switch", "AMICL" },
+ { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" },
+ { "Sto2 ADC MIXR", "ADC Switch", "AMICR" },
+
+ { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" },
+ { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
+
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" },
+ { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
+ { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" },
+ { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+
+ { "AIF1TX", NULL, "Stereo1 ADC MIX"},
+ { "AIF1TX", NULL, "Stereo2 ADC MIX"},
+};
+
+static int rt5514_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 rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ int pre_div, bclk_ms, frame_size;
+ unsigned int val_len = 0;
+
+ rt5514->lrck = params_rate(params);
+ pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck);
+ if (pre_div < 0) {
+ dev_err(codec->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0) {
+ dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ return -EINVAL;
+ }
+
+ bclk_ms = frame_size > 32;
+ rt5514->bclk = rt5514->lrck * (32 << bclk_ms);
+
+ dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+ rt5514->bclk, rt5514->lrck);
+ dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+ bclk_ms, pre_div, dai->id);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ val_len = RT5514_I2S_DL_20;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val_len = RT5514_I2S_DL_24;
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ val_len = RT5514_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK,
+ val_len);
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK,
+ pre_div << RT5514_CLK_SYS_DIV_OUT_SFT |
+ pre_div << RT5514_SEL_ADC_OSR_SFT);
+
+ return 0;
+}
+
+static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ reg_val |= RT5514_I2S_LR_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5514_I2S_BP_INV;
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5514_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5514_I2S_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5514_I2S_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1,
+ RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK,
+ reg_val);
+
+ return 0;
+}
+
+static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT5514_SCLK_S_MCLK:
+ reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK;
+ break;
+
+ case RT5514_SCLK_S_PLL1:
+ reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL;
+ break;
+
+ default:
+ dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+ rt5514->sysclk = freq;
+ rt5514->sysclk_src = clk_id;
+
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+ return 0;
+}
+
+static int rt5514_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 rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ struct rl6231_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(codec->dev, "PLL disabled\n");
+
+ rt5514->pll_in = 0;
+ rt5514->pll_out = 0;
+ regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+ RT5514_CLK_SYS_PRE_SEL_MASK,
+ RT5514_CLK_SYS_PRE_SEL_MCLK);
+
+ return 0;
+ }
+
+ if (source == rt5514->pll_src && freq_in == rt5514->pll_in &&
+ freq_out == rt5514->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT5514_PLL1_S_MCLK:
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+ RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK);
+ break;
+
+ case RT5514_PLL1_S_BCLK:
+ regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+ RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK);
+ break;
+
+ default:
+ dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1,
+ pll_code.k_code << RT5514_PLL_K_SFT |
+ pll_code.n_code << RT5514_PLL_N_SFT |
+ (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT);
+ regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2,
+ RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT);
+
+ rt5514->pll_in = freq_in;
+ rt5514->pll_out = freq_out;
+ rt5514->pll_src = source;
+
+ return 0;
+}
+
+static int rt5514_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 rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0;
+
+ if (rx_mask || tx_mask)
+ val |= RT5514_TDM_MODE;
+
+ if (slots == 4)
+ val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH;
+
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20;
+ break;
+
+ case 24:
+ val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24;
+ break;
+
+ case 32:
+ val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32;
+ break;
+
+ case 16:
+ default:
+ break;
+ }
+
+ regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE |
+ RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK |
+ RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val);
+
+ return 0;
+}
+
+static int rt5514_probe(struct snd_soc_codec *codec)
+{
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+ rt5514->codec = codec;
+
+ return 0;
+}
+
+static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct i2c_client *client = context;
+ struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+ regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+ return 0;
+}
+
+static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct i2c_client *client = context;
+ struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+ regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+ return 0;
+}
+
+#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+struct snd_soc_dai_ops rt5514_aif_dai_ops = {
+ .hw_params = rt5514_hw_params,
+ .set_fmt = rt5514_set_dai_fmt,
+ .set_sysclk = rt5514_set_dai_sysclk,
+ .set_pll = rt5514_set_dai_pll,
+ .set_tdm_slot = rt5514_set_tdm_slot,
+};
+
+struct snd_soc_dai_driver rt5514_dai[] = {
+ {
+ .name = "rt5514-aif1",
+ .id = 0,
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT5514_STEREO_RATES,
+ .formats = RT5514_FORMATS,
+ },
+ .ops = &rt5514_aif_dai_ops,
+ }
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
+ .probe = rt5514_probe,
+ .idle_bias_off = true,
+ .controls = rt5514_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5514_snd_controls),
+ .dapm_widgets = rt5514_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets),
+ .dapm_routes = rt5514_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes),
+};
+
+static const struct regmap_config rt5514_i2c_regmap = {
+ .name = "i2c",
+ .reg_bits = 32,
+ .val_bits = 32,
+
+ .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
+ .readable_reg = rt5514_i2c_readable_register,
+
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config rt5514_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+
+ .max_register = RT5514_VENDOR_ID2,
+ .volatile_reg = rt5514_volatile_register,
+ .readable_reg = rt5514_readable_register,
+ .reg_read = rt5514_i2c_read,
+ .reg_write = rt5514_i2c_write,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5514_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5514_reg),
+ .use_single_rw = true,
+};
+
+static const struct i2c_device_id rt5514_i2c_id[] = {
+ { "rt5514", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5514_of_match[] = {
+ { .compatible = "realtek,rt5514", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+#endif
+
+static int rt5514_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5514_priv *rt5514;
+ int ret;
+ unsigned int val;
+
+ rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv),
+ GFP_KERNEL);
+ if (rt5514 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt5514);
+
+ rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap);
+ if (IS_ERR(rt5514->i2c_regmap)) {
+ ret = PTR_ERR(rt5514->i2c_regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap);
+ if (IS_ERR(rt5514->regmap)) {
+ ret = PTR_ERR(rt5514->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+ if (val != RT5514_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt5514\n", val);
+ return -ENODEV;
+ }
+
+ ret = regmap_register_patch(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",
+ ret);
+
+ ret = regmap_register_patch(rt5514->regmap, rt5514_patch,
+ ARRAY_SIZE(rt5514_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5514,
+ rt5514_dai, ARRAY_SIZE(rt5514_dai));
+}
+
+static int rt5514_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+struct i2c_driver rt5514_i2c_driver = {
+ .driver = {
+ .name = "rt5514",
+ .of_match_table = of_match_ptr(rt5514_of_match),
+ },
+ .probe = rt5514_i2c_probe,
+ .remove = rt5514_i2c_remove,
+ .id_table = rt5514_i2c_id,
+};
+module_i2c_driver(rt5514_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5514 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
new file mode 100644
index 000000000000..6ad8a612f659
--- /dev/null
+++ b/sound/soc/codecs/rt5514.h
@@ -0,0 +1,252 @@
+/*
+ * rt5514.h -- RT5514 ALSA SoC audio driver
+ *
+ * Copyright 2015 Realtek Microelectronics
+ * 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_H__
+#define __RT5514_H__
+
+#define RT5514_DEVICE_ID 0x10ec5514
+
+#define RT5514_RESET 0x2000
+#define RT5514_PWR_ANA1 0x2004
+#define RT5514_PWR_ANA2 0x2008
+#define RT5514_I2S_CTRL1 0x2010
+#define RT5514_I2S_CTRL2 0x2014
+#define RT5514_VAD_CTRL6 0x2030
+#define RT5514_EXT_VAD_CTRL 0x206c
+#define RT5514_DIG_IO_CTRL 0x2070
+#define RT5514_PAD_CTRL1 0x2080
+#define RT5514_DMIC_DATA_CTRL 0x20a0
+#define RT5514_DIG_SOURCE_CTRL 0x20a4
+#define RT5514_SRC_CTRL 0x20ac
+#define RT5514_DOWNFILTER2_CTRL1 0x20d0
+#define RT5514_PLL_SOURCE_CTRL 0x2100
+#define RT5514_CLK_CTRL1 0x2104
+#define RT5514_CLK_CTRL2 0x2108
+#define RT5514_PLL3_CALIB_CTRL1 0x2110
+#define RT5514_PLL3_CALIB_CTRL5 0x2124
+#define RT5514_DELAY_BUF_CTRL1 0x2140
+#define RT5514_DELAY_BUF_CTRL3 0x2148
+#define RT5514_DOWNFILTER0_CTRL1 0x2190
+#define RT5514_DOWNFILTER0_CTRL2 0x2194
+#define RT5514_DOWNFILTER0_CTRL3 0x2198
+#define RT5514_DOWNFILTER1_CTRL1 0x21a0
+#define RT5514_DOWNFILTER1_CTRL2 0x21a4
+#define RT5514_DOWNFILTER1_CTRL3 0x21a8
+#define RT5514_ANA_CTRL_LDO10 0x2200
+#define RT5514_ANA_CTRL_LDO18_16 0x2204
+#define RT5514_ANA_CTRL_ADC12 0x2210
+#define RT5514_ANA_CTRL_ADC21 0x2214
+#define RT5514_ANA_CTRL_ADC22 0x2218
+#define RT5514_ANA_CTRL_ADC23 0x221c
+#define RT5514_ANA_CTRL_MICBST 0x2220
+#define RT5514_ANA_CTRL_ADCFED 0x2224
+#define RT5514_ANA_CTRL_INBUF 0x2228
+#define RT5514_ANA_CTRL_VREF 0x222c
+#define RT5514_ANA_CTRL_PLL3 0x2240
+#define RT5514_ANA_CTRL_PLL1_1 0x2260
+#define RT5514_ANA_CTRL_PLL1_2 0x2264
+#define RT5514_DMIC_LP_CTRL 0x2e00
+#define RT5514_MISC_CTRL_DSP 0x2e04
+#define RT5514_DSP_CTRL1 0x2f00
+#define RT5514_DSP_CTRL3 0x2f08
+#define RT5514_DSP_CTRL4 0x2f10
+#define RT5514_VENDOR_ID1 0x2ff0
+#define RT5514_VENDOR_ID2 0x2ff4
+
+#define RT5514_DSP_MAPPING 0x18000000
+
+/* RT5514_PWR_ANA1 (0x2004) */
+#define RT5514_POW_LDO18_IN (0x1 << 5)
+#define RT5514_POW_LDO18_IN_BIT 5
+#define RT5514_POW_LDO18_ADC (0x1 << 4)
+#define RT5514_POW_LDO18_ADC_BIT 4
+#define RT5514_POW_LDO21 (0x1 << 3)
+#define RT5514_POW_LDO21_BIT 3
+#define RT5514_POW_BG_LDO18_IN (0x1 << 2)
+#define RT5514_POW_BG_LDO18_IN_BIT 2
+#define RT5514_POW_BG_LDO21 (0x1 << 1)
+#define RT5514_POW_BG_LDO21_BIT 1
+
+/* RT5514_PWR_ANA2 (0x2008) */
+#define RT5514_POW_PLL1 (0x1 << 18)
+#define RT5514_POW_PLL1_BIT 18
+#define RT5514_POW_PLL1_LDO (0x1 << 16)
+#define RT5514_POW_PLL1_LDO_BIT 16
+#define RT5514_POW_BG_MBIAS (0x1 << 15)
+#define RT5514_POW_BG_MBIAS_BIT 15
+#define RT5514_POW_MBIAS (0x1 << 14)
+#define RT5514_POW_MBIAS_BIT 14
+#define RT5514_POW_VREF2 (0x1 << 13)
+#define RT5514_POW_VREF2_BIT 13
+#define RT5514_POW_VREF1 (0x1 << 12)
+#define RT5514_POW_VREF1_BIT 12
+#define RT5514_POWR_LDO16 (0x1 << 11)
+#define RT5514_POWR_LDO16_BIT 11
+#define RT5514_POWL_LDO16 (0x1 << 10)
+#define RT5514_POWL_LDO16_BIT 10
+#define RT5514_POW_ADC2 (0x1 << 9)
+#define RT5514_POW_ADC2_BIT 9
+#define RT5514_POW_INPUT_BUF (0x1 << 8)
+#define RT5514_POW_INPUT_BUF_BIT 8
+#define RT5514_POW_ADC1_R (0x1 << 7)
+#define RT5514_POW_ADC1_R_BIT 7
+#define RT5514_POW_ADC1_L (0x1 << 6)
+#define RT5514_POW_ADC1_L_BIT 6
+#define RT5514_POW2_BSTR (0x1 << 5)
+#define RT5514_POW2_BSTR_BIT 5
+#define RT5514_POW2_BSTL (0x1 << 4)
+#define RT5514_POW2_BSTL_BIT 4
+#define RT5514_POW_BSTR (0x1 << 3)
+#define RT5514_POW_BSTR_BIT 3
+#define RT5514_POW_BSTL (0x1 << 2)
+#define RT5514_POW_BSTL_BIT 2
+#define RT5514_POW_ADCFEDR (0x1 << 1)
+#define RT5514_POW_ADCFEDR_BIT 1
+#define RT5514_POW_ADCFEDL (0x1 << 0)
+#define RT5514_POW_ADCFEDL_BIT 0
+
+/* RT5514_I2S_CTRL1 (0x2010) */
+#define RT5514_TDM_MODE (0x1 << 28)
+#define RT5514_TDM_MODE_SFT 28
+#define RT5514_I2S_LR_MASK (0x1 << 26)
+#define RT5514_I2S_LR_SFT 26
+#define RT5514_I2S_LR_NOR (0x0 << 26)
+#define RT5514_I2S_LR_INV (0x1 << 26)
+#define RT5514_I2S_BP_MASK (0x1 << 25)
+#define RT5514_I2S_BP_SFT 25
+#define RT5514_I2S_BP_NOR (0x0 << 25)
+#define RT5514_I2S_BP_INV (0x1 << 25)
+#define RT5514_I2S_DF_MASK (0x7 << 16)
+#define RT5514_I2S_DF_SFT 16
+#define RT5514_I2S_DF_I2S (0x0 << 16)
+#define RT5514_I2S_DF_LEFT (0x1 << 16)
+#define RT5514_I2S_DF_PCM_A (0x2 << 16)
+#define RT5514_I2S_DF_PCM_B (0x3 << 16)
+#define RT5514_TDMSLOT_SEL_RX_MASK (0x3 << 10)
+#define RT5514_TDMSLOT_SEL_RX_SFT 10
+#define RT5514_TDMSLOT_SEL_RX_4CH (0x1 << 10)
+#define RT5514_CH_LEN_RX_MASK (0x3 << 8)
+#define RT5514_CH_LEN_RX_SFT 8
+#define RT5514_CH_LEN_RX_16 (0x0 << 8)
+#define RT5514_CH_LEN_RX_20 (0x1 << 8)
+#define RT5514_CH_LEN_RX_24 (0x2 << 8)
+#define RT5514_CH_LEN_RX_32 (0x3 << 8)
+#define RT5514_TDMSLOT_SEL_TX_MASK (0x3 << 6)
+#define RT5514_TDMSLOT_SEL_TX_SFT 6
+#define RT5514_TDMSLOT_SEL_TX_4CH (0x1 << 6)
+#define RT5514_CH_LEN_TX_MASK (0x3 << 4)
+#define RT5514_CH_LEN_TX_SFT 4
+#define RT5514_CH_LEN_TX_16 (0x0 << 4)
+#define RT5514_CH_LEN_TX_20 (0x1 << 4)
+#define RT5514_CH_LEN_TX_24 (0x2 << 4)
+#define RT5514_CH_LEN_TX_32 (0x3 << 4)
+#define RT5514_I2S_DL_MASK (0x3 << 0)
+#define RT5514_I2S_DL_SFT 0
+#define RT5514_I2S_DL_16 (0x0 << 0)
+#define RT5514_I2S_DL_20 (0x1 << 0)
+#define RT5514_I2S_DL_24 (0x2 << 0)
+#define RT5514_I2S_DL_8 (0x3 << 0)
+
+/* RT5514_DIG_SOURCE_CTRL (0x20a4) */
+#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1)
+#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1
+#define RT5514_AD0_DMIC_INPUT_SEL (0x1 << 0)
+#define RT5514_AD0_DMIC_INPUT_SEL_SFT 0
+
+/* RT5514_PLL_SOURCE_CTRL (0x2100) */
+#define RT5514_PLL_1_SEL_MASK (0x7 << 12)
+#define RT5514_PLL_1_SEL_SFT 12
+#define RT5514_PLL_1_SEL_SCLK (0x3 << 12)
+#define RT5514_PLL_1_SEL_MCLK (0x4 << 12)
+
+/* RT5514_CLK_CTRL1 (0x2104) */
+#define RT5514_CLK_AD_ANA1_EN (0x1 << 31)
+#define RT5514_CLK_AD_ANA1_EN_BIT 31
+#define RT5514_CLK_AD1_EN (0x1 << 24)
+#define RT5514_CLK_AD1_EN_BIT 24
+#define RT5514_CLK_AD0_EN (0x1 << 23)
+#define RT5514_CLK_AD0_EN_BIT 23
+#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8)
+#define RT5514_CLK_DMIC_OUT_SEL_SFT 8
+
+/* RT5514_CLK_CTRL2 (0x2108) */
+#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8)
+#define RT5514_CLK_SYS_DIV_OUT_SFT 8
+#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4)
+#define RT5514_SEL_ADC_OSR_SFT 4
+#define RT5514_CLK_SYS_PRE_SEL_MASK (0x3 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_SFT 0
+#define RT5514_CLK_SYS_PRE_SEL_MCLK (0x2 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_PLL (0x3 << 0)
+
+/* RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */
+#define RT5514_AD_DMIC_MIX (0x1 << 11)
+#define RT5514_AD_DMIC_MIX_BIT 11
+#define RT5514_AD_AD_MIX (0x1 << 10)
+#define RT5514_AD_AD_MIX_BIT 10
+#define RT5514_AD_AD_MUTE (0x1 << 7)
+#define RT5514_AD_AD_MUTE_BIT 7
+#define RT5514_AD_GAIN_MASK (0x7f << 0)
+#define RT5514_AD_GAIN_SFT 0
+
+/* RT5514_ANA_CTRL_MICBST (0x2220) */
+#define RT5514_SEL_BSTL_MASK (0xf << 4)
+#define RT5514_SEL_BSTL_SFT 4
+#define RT5514_SEL_BSTR_MASK (0xf << 0)
+#define RT5514_SEL_BSTR_SFT 0
+
+/* RT5514_ANA_CTRL_PLL1_1 (0x2260) */
+#define RT5514_PLL_K_MAX 0x1f
+#define RT5514_PLL_K_MASK (RT5514_PLL_K_MAX << 16)
+#define RT5514_PLL_K_SFT 16
+#define RT5514_PLL_N_MAX 0x1ff
+#define RT5514_PLL_N_MASK (RT5514_PLL_N_MAX << 7)
+#define RT5514_PLL_N_SFT 4
+#define RT5514_PLL_M_MAX 0xf
+#define RT5514_PLL_M_MASK (RT5514_PLL_M_MAX << 0)
+#define RT5514_PLL_M_SFT 0
+
+/* RT5514_ANA_CTRL_PLL1_2 (0x2264) */
+#define RT5514_PLL_M_BP (0x1 << 2)
+#define RT5514_PLL_M_BP_SFT 2
+#define RT5514_PLL_K_BP (0x1 << 1)
+#define RT5514_PLL_K_BP_SFT 1
+#define RT5514_EN_LDO_PLL1 (0x1 << 0)
+#define RT5514_EN_LDO_PLL1_BIT 0
+
+#define RT5514_PLL_INP_MAX 40000000
+#define RT5514_PLL_INP_MIN 256000
+
+/* System Clock Source */
+enum {
+ RT5514_SCLK_S_MCLK,
+ RT5514_SCLK_S_PLL1,
+};
+
+/* PLL1 Source */
+enum {
+ RT5514_PLL1_S_MCLK,
+ RT5514_PLL1_S_BCLK,
+};
+
+struct rt5514_priv {
+ struct snd_soc_codec *codec;
+ struct regmap *i2c_regmap, *regmap;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+ int bclk;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#endif /* __RT5514_H__ */
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index 1c10d8ed39d2..f527b5b2817b 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
@@ -53,6 +54,7 @@ static const struct reg_sequence init_list[] = {
{RT5616_PR_BASE + 0x21, 0x4040},
{RT5616_PR_BASE + 0x23, 0x0004},
};
+
#define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5616_reg[] = {
@@ -143,6 +145,7 @@ struct rt5616_priv {
struct snd_soc_codec *codec;
struct delayed_work patch_work;
struct regmap *regmap;
+ struct clk *mclk;
int sysclk;
int sysclk_src;
@@ -162,9 +165,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg)
for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
if (reg >= rt5616_ranges[i].range_min &&
- reg <= rt5616_ranges[i].range_max) {
+ reg <= rt5616_ranges[i].range_max)
return true;
- }
}
switch (reg) {
@@ -190,9 +192,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg)
for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
if (reg >= rt5616_ranges[i].range_min &&
- reg <= rt5616_ranges[i].range_max) {
+ reg <= rt5616_ranges[i].range_max)
return true;
- }
}
switch (reg) {
@@ -307,45 +308,47 @@ static unsigned int bst_tlv[] = {
static const struct snd_kcontrol_new rt5616_snd_controls[] = {
/* Headphone Output Volume */
SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL,
- RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL,
+ RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
/* OUTPUT Control */
SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1,
- RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1,
- RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
+ RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
/* DAC Digital Volume */
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
- 175, 0, dac_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+ 175, 0, dac_vol_tlv),
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2,
- RT5616_BST_SFT1, 8, 0, bst_tlv),
+ RT5616_BST_SFT1, 8, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2,
- RT5616_BST_SFT2, 8, 0, bst_tlv),
+ RT5616_BST_SFT2, 8, 0, bst_tlv),
/* INL/INR Volume Control */
SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL,
- RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
- 31, 1, in_vol_tlv),
+ RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
+ 31, 1, in_vol_tlv),
/* ADC Digital Volume Control */
SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL,
- RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+ RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL,
- RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
- 127, 0, adc_vol_tlv),
+ RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+ 127, 0, adc_vol_tlv),
/* ADC Boost Volume Control */
SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL,
- RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
- 3, 0, adc_bst_tlv),
+ RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
+ 3, 0, adc_bst_tlv),
};
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink)
+ struct snd_soc_dapm_widget *sink)
{
unsigned int val;
@@ -462,20 +465,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = {
};
static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
break;
case SND_SOC_DAPM_POST_PMD:
snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE,
- RT5616_L_MUTE | RT5616_R_MUTE);
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
break;
default:
@@ -486,7 +489,7 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
@@ -494,54 +497,55 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
/* depop parameters */
snd_soc_update_bits(codec, RT5616_DEPOP_M2,
- RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
+ RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
- RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
- RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
+ RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+ RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
+ RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
snd_soc_write(codec, RT5616_PR_BASE +
- RT5616_HP_DCC_INT1, 0x9f00);
+ RT5616_HP_DCC_INT1, 0x9f00);
/* headphone amp power on */
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
+ RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
snd_soc_update_bits(codec, RT5616_PWR_VOL,
- RT5616_PWR_HV_L | RT5616_PWR_HV_R,
- RT5616_PWR_HV_L | RT5616_PWR_HV_R);
+ RT5616_PWR_HV_L | RT5616_PWR_HV_R,
+ RT5616_PWR_HV_L | RT5616_PWR_HV_R);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_HP_L | RT5616_PWR_HP_R |
- RT5616_PWR_HA, RT5616_PWR_HP_L |
- RT5616_PWR_HP_R | RT5616_PWR_HA);
+ RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+ RT5616_PWR_HA, RT5616_PWR_HP_L |
+ RT5616_PWR_HP_R | RT5616_PWR_HA);
msleep(50);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2,
- RT5616_PWR_FV1 | RT5616_PWR_FV2);
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
snd_soc_update_bits(codec, RT5616_CHARGE_PUMP,
- RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
+ RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
snd_soc_update_bits(codec, RT5616_PR_BASE +
- RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
+ RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
- RT5616_HP_CO_EN | RT5616_HP_SG_EN);
+ RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
+ RT5616_HP_CO_EN | RT5616_HP_SG_EN);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_PR_BASE +
- RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
+ RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
- RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+ RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+ RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
/* headphone amp power down */
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK |
- RT5616_HP_CO_MASK | RT5616_HP_CP_MASK |
- RT5616_HP_SG_MASK | RT5616_HP_CB_MASK,
- RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
- RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
- RT5616_HP_SG_EN | RT5616_HP_CB_PD);
+ RT5616_SMT_TRIG_MASK |
+ RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK |
+ RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+ RT5616_HP_CB_MASK,
+ RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
+ RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
+ RT5616_HP_SG_EN | RT5616_HP_CB_PD);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_HP_L | RT5616_PWR_HP_R |
- RT5616_PWR_HA, 0);
+ RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+ RT5616_PWR_HA, 0);
break;
default:
return 0;
@@ -551,7 +555,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
@@ -559,57 +563,57 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
/* headphone unmute sequence */
snd_soc_update_bits(codec, RT5616_DEPOP_M3,
- RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
- RT5616_CP_FQ3_MASK,
- (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) |
- (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
- (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT));
+ RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+ RT5616_CP_FQ3_MASK,
+ RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT |
+ RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+ RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT);
snd_soc_write(codec, RT5616_PR_BASE +
- RT5616_MAMP_INT_REG2, 0xfc00);
+ RT5616_MAMP_INT_REG2, 0xfc00);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
+ RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTN_MASK, RT5616_RSTN_EN);
+ RT5616_RSTN_MASK, RT5616_RSTN_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
- RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+ RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
+ RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
snd_soc_update_bits(codec, RT5616_HP_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
msleep(100);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
- RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+ RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+ RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
msleep(20);
snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
- RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
+ RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
break;
case SND_SOC_DAPM_PRE_PMD:
/* headphone mute sequence */
snd_soc_update_bits(codec, RT5616_DEPOP_M3,
- RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
- RT5616_CP_FQ3_MASK,
- (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) |
- (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
- (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT));
+ RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+ RT5616_CP_FQ3_MASK,
+ RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT |
+ RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+ RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT);
snd_soc_write(codec, RT5616_PR_BASE +
- RT5616_MAMP_INT_REG2, 0xfc00);
+ RT5616_MAMP_INT_REG2, 0xfc00);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
+ RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTP_MASK, RT5616_RSTP_EN);
+ RT5616_RSTP_MASK, RT5616_RSTP_EN);
snd_soc_update_bits(codec, RT5616_DEPOP_M1,
- RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
- RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
- RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+ RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
+ RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
+ RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
- RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
+ RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
msleep(90);
snd_soc_update_bits(codec, RT5616_HP_VOL,
- RT5616_L_MUTE | RT5616_R_MUTE,
- RT5616_L_MUTE | RT5616_R_MUTE);
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
msleep(30);
break;
@@ -621,24 +625,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_LM, RT5616_PWR_LM);
+ RT5616_PWR_LM, RT5616_PWR_LM);
snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
- RT5616_L_MUTE | RT5616_R_MUTE, 0);
+ RT5616_L_MUTE | RT5616_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
- RT5616_L_MUTE | RT5616_R_MUTE,
- RT5616_L_MUTE | RT5616_R_MUTE);
+ RT5616_L_MUTE | RT5616_R_MUTE,
+ RT5616_L_MUTE | RT5616_R_MUTE);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_LM, 0);
+ RT5616_PWR_LM, 0);
break;
default:
@@ -649,19 +653,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
+ RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST1_OP2, 0);
+ RT5616_PWR_BST1_OP2, 0);
break;
default:
@@ -672,19 +676,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
}
static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
+ RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
- RT5616_PWR_BST2_OP2, 0);
+ RT5616_PWR_BST2_OP2, 0);
break;
default:
@@ -696,13 +700,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2,
- RT5616_PWR_PLL_BIT, 0, NULL, 0),
+ RT5616_PWR_PLL_BIT, 0, NULL, 0),
/* Input Side */
/* micbias */
SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1,
- RT5616_PWR_LDO_BIT, 0, NULL, 0),
+ RT5616_PWR_LDO_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2,
- RT5616_PWR_MB1_BIT, 0, NULL, 0),
+ RT5616_PWR_MB1_BIT, 0, NULL, 0),
/* Input Lines */
SND_SOC_DAPM_INPUT("MIC1"),
@@ -714,45 +718,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
/* Boost */
SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2,
- RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2,
- RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
- SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
/* Input Volume */
SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL,
- RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0,
- rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
+ rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0,
- rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
+ rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
/* ADCs */
SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
- SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
- SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+ RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
/* ADC Mixer */
SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2,
- RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
+ RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
- rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
+ rt5616_sto1_adc_l_mix,
+ ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
- rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
+ rt5616_sto1_adc_r_mix,
+ ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
/* Digital Interface */
SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1,
- RT5616_PWR_I2S1_BIT, 0, NULL, 0),
+ RT5616_PWR_I2S1_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -770,68 +776,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
/* Output Side */
/* DAC mixer before sound effect */
SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
- rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
+ rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
- rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
+ rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2,
- RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
+ RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
/* DAC Mixer */
SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
- rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)),
+ rt5616_sto_dac_l_mix,
+ ARRAY_SIZE(rt5616_sto_dac_l_mix)),
SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
- rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)),
+ rt5616_sto_dac_r_mix,
+ ARRAY_SIZE(rt5616_sto_dac_r_mix)),
/* DACs */
SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_DAC_L1_BIT, 0),
+ RT5616_PWR_DAC_L1_BIT, 0),
SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1,
- RT5616_PWR_DAC_R1_BIT, 0),
+ RT5616_PWR_DAC_R1_BIT, 0),
/* OUT Mixer */
SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT,
- 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
+ 0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT,
- 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
+ 0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
/* Output Volume */
SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL,
- RT5616_PWR_OV_L_BIT, 0, NULL, 0),
+ RT5616_PWR_OV_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL,
- RT5616_PWR_OV_R_BIT, 0, NULL, 0),
+ RT5616_PWR_OV_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL,
- RT5616_PWR_HV_L_BIT, 0, NULL, 0),
+ RT5616_PWR_HV_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL,
- RT5616_PWR_HV_R_BIT, 0, NULL, 0),
+ RT5616_PWR_HV_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM,
- 0, 0, NULL, 0),
+ 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM,
- 0, 0, NULL, 0),
+ 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM,
- 0, 0, NULL, 0),
+ 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL,
- RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL,
- RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL,
- RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL,
- RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+ RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
/* HPO/LOUT/Mono Mixer */
SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0,
- rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
+ rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
- rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
+ rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0,
- rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
- SND_SOC_DAPM_POST_PMU),
+ rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0,
- rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
- SND_SOC_DAPM_POST_PMU),
+ rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0,
- rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
- SND_SOC_DAPM_PRE_PMD),
+ rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
/* Output Lines */
SND_SOC_DAPM_OUTPUT("HPOL"),
@@ -950,7 +958,8 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = {
};
static int rt5616_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
@@ -977,7 +986,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
rt5616->bclk[dai->id], rt5616->lrck[dai->id]);
dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
- bclk_ms, pre_div, dai->id);
+ bclk_ms, pre_div, dai->id);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -998,10 +1007,9 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
mask_clk = RT5616_I2S_PD1_MASK;
val_clk = pre_div << RT5616_I2S_PD1_SFT;
snd_soc_update_bits(codec, RT5616_I2S1_SDP,
- RT5616_I2S_DL_MASK, val_len);
+ RT5616_I2S_DL_MASK, val_len);
snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk);
-
return 0;
}
@@ -1050,15 +1058,14 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
snd_soc_update_bits(codec, RT5616_I2S1_SDP,
- RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
- RT5616_I2S_DF_MASK, reg_val);
-
+ RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
+ RT5616_I2S_DF_MASK, reg_val);
return 0;
}
static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
+ int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@@ -1078,8 +1085,9 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
+
snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_SCLK_SRC_MASK, reg_val);
+ RT5616_SCLK_SRC_MASK, reg_val);
rt5616->sysclk = freq;
rt5616->sysclk_src = clk_id;
@@ -1089,7 +1097,7 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
}
static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
- unsigned int freq_in, unsigned int freq_out)
+ unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@@ -1106,19 +1114,22 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
rt5616->pll_in = 0;
rt5616->pll_out = 0;
snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK);
+ RT5616_SCLK_SRC_MASK,
+ RT5616_SCLK_SRC_MCLK);
return 0;
}
switch (source) {
case RT5616_PLL1_S_MCLK:
snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK);
+ RT5616_PLL1_SRC_MASK,
+ RT5616_PLL1_SRC_MCLK);
break;
case RT5616_PLL1_S_BCLK1:
case RT5616_PLL1_S_BCLK2:
snd_soc_update_bits(codec, RT5616_GLB_CLK,
- RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1);
+ RT5616_PLL1_SRC_MASK,
+ RT5616_PLL1_SRC_BCLK1);
break;
default:
dev_err(codec->dev, "Unknown PLL source %d\n", source);
@@ -1136,10 +1147,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
pll_code.n_code, pll_code.k_code);
snd_soc_write(codec, RT5616_PLL_CTRL1,
- pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
+ pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
snd_soc_write(codec, RT5616_PLL_CTRL2,
- (pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT |
- pll_code.m_bp << RT5616_PLL_M_BP_SFT);
+ (pll_code.m_bp ? 0 : pll_code.m_code) <<
+ RT5616_PLL_M_SFT |
+ pll_code.m_bp << RT5616_PLL_M_BP_SFT);
rt5616->pll_in = freq_in;
rt5616->pll_out = freq_out;
@@ -1149,22 +1161,50 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
}
static int rt5616_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
+ enum snd_soc_bias_level level)
{
+ struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
switch (level) {
+
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (IS_ERR(rt5616->mclk))
+ break;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5616->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5616->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
case SND_SOC_BIAS_STANDBY:
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2);
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2);
mdelay(10);
snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2,
- RT5616_PWR_FV1 | RT5616_PWR_FV2);
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
snd_soc_update_bits(codec, RT5616_D_MISC,
- RT5616_D_GATE_EN, RT5616_D_GATE_EN);
+ RT5616_D_GATE_EN,
+ RT5616_D_GATE_EN);
}
break;
@@ -1189,6 +1229,11 @@ static int rt5616_probe(struct snd_soc_codec *codec)
{
struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+ /* Check if MCLK provided */
+ rt5616->mclk = devm_clk_get(codec->dev, "mclk");
+ if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
rt5616->codec = codec;
return 0;
@@ -1218,11 +1263,10 @@ static int rt5616_resume(struct snd_soc_codec *codec)
#define rt5616_resume NULL
#endif
-#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000
#define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-
struct snd_soc_dai_ops rt5616_aif_dai_ops = {
.hw_params = rt5616_hw_params,
.set_fmt = rt5616_set_dai_fmt,
@@ -1296,15 +1340,15 @@ MODULE_DEVICE_TABLE(of, rt5616_of_match);
#endif
static int rt5616_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct rt5616_priv *rt5616;
unsigned int val;
int ret;
rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv),
- GFP_KERNEL);
- if (rt5616 == NULL)
+ GFP_KERNEL);
+ if (!rt5616)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5616);
@@ -1326,14 +1370,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
}
regmap_write(rt5616->regmap, RT5616_RESET, 0);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2,
- RT5616_PWR_VREF1 | RT5616_PWR_MB |
- RT5616_PWR_BG | RT5616_PWR_VREF2);
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2,
+ RT5616_PWR_VREF1 | RT5616_PWR_MB |
+ RT5616_PWR_BG | RT5616_PWR_VREF2);
mdelay(10);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
- RT5616_PWR_FV1 | RT5616_PWR_FV2,
- RT5616_PWR_FV1 | RT5616_PWR_FV2);
+ RT5616_PWR_FV1 | RT5616_PWR_FV2,
+ RT5616_PWR_FV1 | RT5616_PWR_FV2);
ret = regmap_register_patch(rt5616->regmap, init_list,
ARRAY_SIZE(init_list));
@@ -1341,11 +1385,10 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
- RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
+ RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616,
- rt5616_dai, ARRAY_SIZE(rt5616_dai));
-
+ rt5616_dai, ARRAY_SIZE(rt5616_dai));
}
static int rt5616_i2c_remove(struct i2c_client *i2c)
@@ -1361,7 +1404,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client)
regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8);
regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8);
-
}
static struct i2c_driver rt5616_i2c_driver = {
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 11d032cdc658..e8b5ba04417a 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1217,11 +1217,14 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0,
rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)),
/* DACs */
- SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1,
- RT5640_PWR_DAC_L1_BIT, 0),
- SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1,
- RT5640_PWR_DAC_R1_BIT, 0),
-
+ SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L1_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R1_BIT, 0, NULL, 0),
/* SPK/OUT Mixer */
SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
@@ -1298,9 +1301,9 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
- SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_R2_BIT,
+ SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0,
0),
- SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_L2_BIT,
+ SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0,
0),
SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
@@ -1317,6 +1320,10 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
RT5640_PWR_MA_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1,
+ RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("MONOP"),
SND_SOC_DAPM_OUTPUT("MONON"),
@@ -1328,11 +1335,6 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)),
- SND_SOC_DAPM_SUPPLY("DAC L2 Filter", RT5640_PWR_DIG1,
- RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("DAC R2 Filter", RT5640_PWR_DIG1,
- RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
-
SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
0, rt5639_out_l_mix, ARRAY_SIZE(rt5639_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
@@ -1493,8 +1495,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"},
{"DAC MIXL", "INF1 Switch", "IF1 DAC L"},
+ {"DAC MIXL", NULL, "DAC L1 Power"},
{"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
{"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
+ {"DAC MIXR", NULL, "DAC R1 Power"},
{"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
{"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
@@ -1507,8 +1511,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"DAC L1", NULL, "Stereo DAC MIXL"},
{"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC L1", NULL, "DAC L1 Power"},
{"DAC R1", NULL, "Stereo DAC MIXR"},
{"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R1", NULL, "DAC R1 Power"},
{"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
{"SPK MIXL", "INL Switch", "INL VOL"},
@@ -1595,8 +1601,9 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
{"DAC L2 Mux", "IF2", "IF2 DAC L"},
{"DAC L2 Mux", "Base L/R", "Audio DSP"},
-
+ {"DAC L2 Mux", NULL, "DAC L2 Power"},
{"DAC R2 Mux", "IF2", "IF2 DAC R"},
+ {"DAC R2 Mux", NULL, "DAC R2 Power"},
{"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
{"Stereo DAC MIXL", "ANC Switch", "ANC"},
@@ -1614,8 +1621,10 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
{"DAC L2", NULL, "Mono DAC MIXL"},
{"DAC L2", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC L2", NULL, "DAC L2 Power"},
{"DAC R2", NULL, "Mono DAC MIXR"},
{"DAC R2", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R2", NULL, "DAC R2 Power"},
{"SPK MIXL", "DAC L2 Switch", "DAC L2"},
{"SPK MIXR", "DAC R2 Switch", "DAC R2"},
@@ -1656,8 +1665,8 @@ static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
{"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"},
{"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"},
- {"IF2 DAC L", NULL, "DAC L2 Filter"},
- {"IF2 DAC R", NULL, "DAC R2 Filter"},
+ {"IF2 DAC L", NULL, "DAC L2 Power"},
+ {"IF2 DAC R", NULL, "DAC R2 Power"},
};
static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
@@ -1880,7 +1889,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
struct snd_soc_codec *codec = dai->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
struct rl6231_pll_code pll_code;
- int ret, dai_sel;
+ int ret;
if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
freq_out == rt5640->pll_out)
@@ -1902,21 +1911,12 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK);
break;
case RT5640_PLL1_S_BCLK1:
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
+ break;
case RT5640_PLL1_S_BCLK2:
- dai_sel = get_sdp_info(codec, dai->id);
- if (dai_sel < 0) {
- dev_err(codec->dev,
- "Failed to get sdp info: %d\n", dai_sel);
- return -EINVAL;
- }
- if (dai_sel & RT5640_U_IF1) {
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
- RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
- }
- if (dai_sel & RT5640_U_IF2) {
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
- RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
- }
+ snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
break;
default:
dev_err(codec->dev, "Unknown PLL source %d\n", source);
@@ -1949,7 +1949,33 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
static int rt5640_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ /*
+ * SND_SOC_BIAS_PREPARE is called while preparing for a
+ * transition to ON or away from ON. If current bias_level
+ * is SND_SOC_BIAS_ON, then it is preparing for a transition
+ * away from ON. Disable the clock in that case, otherwise
+ * enable it.
+ */
+ if (IS_ERR(rt5640->mclk))
+ break;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5640->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5640->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
case SND_SOC_BIAS_STANDBY:
if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) {
snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
@@ -2088,6 +2114,11 @@ static int rt5640_probe(struct snd_soc_codec *codec)
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ /* Check if MCLK provided */
+ rt5640->mclk = devm_clk_get(codec->dev, "mclk");
+ if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
rt5640->codec = codec;
snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 83a7150ddc24..1761c3a98b76 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -12,6 +12,7 @@
#ifndef _RT5640_H
#define _RT5640_H
+#include <linux/clk.h>
#include <sound/rt5640.h>
/* Info */
@@ -2097,6 +2098,7 @@ struct rt5640_priv {
struct snd_soc_codec *codec;
struct rt5640_platform_data pdata;
struct regmap *regmap;
+ struct clk *mclk;
int sysclk;
int sysclk_src;
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 93e8c9017633..7af5e7380d61 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -1674,7 +1674,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
- msleep(70);
+ msleep(90);
rt5645->hp_on = true;
} else {
/* depop parameters */
@@ -3029,13 +3029,18 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
RT5645_PWR_BG | RT5645_PWR_VREF2,
RT5645_PWR_VREF1 | RT5645_PWR_MB |
RT5645_PWR_BG | RT5645_PWR_VREF2);
+ mdelay(10);
snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- if (rt5645->en_button_func &&
- snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
- queue_delayed_work(system_power_efficient_wq,
- &rt5645->jack_detect_work, msecs_to_jiffies(0));
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+ msleep(40);
+ if (rt5645->en_button_func)
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work,
+ msecs_to_jiffies(0));
+ }
break;
case SND_SOC_BIAS_OFF:
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index fb8ea05c0de1..1b30914c2d91 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -4176,7 +4176,7 @@ static int rt5659_i2c_remove(struct i2c_client *i2c)
return 0;
}
-void rt5659_i2c_shutdown(struct i2c_client *client)
+static void rt5659_i2c_shutdown(struct i2c_client *client)
{
struct rt5659_priv *rt5659 = i2c_get_clientdata(client);
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index e619d5651b09..080c78e88e10 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -352,6 +352,11 @@ static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
regcache_cache_only(ssm4567->regmap, !enable);
if (enable) {
+ ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET,
+ 0x00);
+ if (ret)
+ return ret;
+
ret = regmap_update_bits(ssm4567->regmap,
SSM4567_REG_POWER_CTRL,
SSM4567_POWER_SPWDN, 0x00);
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 64637d1cf4e5..a8b3e3f701f9 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -619,7 +619,7 @@ static int wm5102_adsp_power_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);
- unsigned int v;
+ unsigned int v = 0;
int ret;
switch (event) {
@@ -654,7 +654,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
break;
}
- return wm_adsp2_early_event(w, kcontrol, event);
+ return wm_adsp2_early_event(w, kcontrol, event, v);
}
static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
@@ -1408,7 +1408,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
-WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev),
+WM_ADSP2("DSP1", 0, wm5102_adsp_power_ev),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1599,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "Slim2 Capture", NULL, "SYSCLK" },
{ "Slim3 Capture", NULL, "SYSCLK" },
+ { "Audio Trace DSP", NULL, "DSP1" },
+ { "Audio Trace DSP", NULL, "SYSCLK" },
+
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1735,7 +1738,7 @@ static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define WM5102_RATES SNDRV_PCM_RATE_8000_192000
+#define WM5102_RATES SNDRV_PCM_RATE_KNOT
#define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -1864,14 +1867,67 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
},
.ops = &arizona_simple_dai_ops,
},
+ {
+ .name = "wm5102-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "wm5102-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = WM5102_RATES,
+ .formats = WM5102_FORMATS,
+ },
+ },
};
+static int wm5102_open(struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+
+ return wm_adsp_compr_open(&priv->core.adsp[0], stream);
+}
+
+static irqreturn_t wm5102_adsp2_irq(int irq, void *data)
+{
+ struct wm5102_priv *priv = data;
+ struct arizona *arizona = priv->core.arizona;
+ int ret;
+
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[0]);
+ if (ret == -ENODEV) {
+ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
static int wm5102_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->core.arizona;
int ret;
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5102_adsp2_irq,
+ priv);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
if (ret)
return ret;
@@ -1946,6 +2002,20 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
.num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes),
};
+static struct snd_compr_ops wm5102_compr_ops = {
+ .open = wm5102_open,
+ .free = wm_adsp_compr_free,
+ .set_params = wm_adsp_compr_set_params,
+ .get_caps = wm_adsp_compr_get_caps,
+ .trigger = wm_adsp_compr_trigger,
+ .pointer = wm_adsp_compr_pointer,
+ .copy = wm_adsp_compr_copy,
+};
+
+static struct snd_soc_platform_driver wm5102_compr_platform = {
+ .compr_ops = &wm5102_compr_ops,
+};
+
static int wm5102_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -2005,12 +2075,25 @@ static int wm5102_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
+ ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
wm5102_dai, ARRAY_SIZE(wm5102_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+ snd_soc_unregister_platform(&pdev->dev);
+ }
+
+ return ret;
}
static int wm5102_remove(struct platform_device *pdev)
{
+ snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 97c0f1e23886..83ba70fe16e6 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -191,6 +191,25 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
return 0;
}
+static int wm5110_adsp_power_ev(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 arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
+ return ret;
+ }
+
+ v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+ return wm_adsp2_early_event(w, kcontrol, event, v);
+}
+
static const struct reg_sequence wm5110_no_dre_left_enable[] = {
{ 0x3024, 0xE410 },
{ 0x3025, 0x0056 },
@@ -1179,10 +1198,10 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
NULL, 0),
-WM_ADSP2("DSP1", 0),
-WM_ADSP2("DSP2", 1),
-WM_ADSP2("DSP3", 2),
-WM_ADSP2("DSP4", 3),
+WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev),
+WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev),
+WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev),
+WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev),
SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@@ -1809,6 +1828,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "Voice Control DSP", NULL, "DSP3" },
{ "Voice Control DSP", NULL, "SYSCLK" },
+ { "Audio Trace DSP", NULL, "DSP1" },
+ { "Audio Trace DSP", NULL, "SYSCLK" },
+
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -2002,7 +2024,7 @@ static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define WM5110_RATES SNDRV_PCM_RATE_8000_192000
+#define WM5110_RATES SNDRV_PCM_RATE_KNOT
#define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -2152,6 +2174,27 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.formats = WM5110_FORMATS,
},
},
+ {
+ .name = "wm5110-cpu-trace",
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ .compress_new = snd_soc_new_compress,
+ },
+ {
+ .name = "wm5110-dsp-trace",
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 6,
+ .rates = WM5110_RATES,
+ .formats = WM5110_FORMATS,
+ },
+ },
};
static int wm5110_open(struct snd_compr_stream *stream)
@@ -2163,6 +2206,8 @@ static int wm5110_open(struct snd_compr_stream *stream)
if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
n_adsp = 2;
+ } else if (strcmp(rtd->codec_dai->name, "wm5110-dsp-trace") == 0) {
+ n_adsp = 0;
} else {
dev_err(arizona->dev,
"No suitable compressed stream for DAI '%s'\n",
@@ -2175,12 +2220,21 @@ static int wm5110_open(struct snd_compr_stream *stream)
static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
{
- struct wm5110_priv *florida = data;
- int ret;
+ struct wm5110_priv *priv = data;
+ struct arizona *arizona = priv->core.arizona;
+ int serviced = 0;
+ int i, ret;
- ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
- if (ret == -ENODEV)
+ for (i = 0; i < WM5110_NUM_ADSP; ++i) {
+ ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+ if (ret != -ENODEV)
+ serviced++;
+ }
+
+ if (!serviced) {
+ dev_err(arizona->dev, "Spurious compressed data IRQ\n");
return IRQ_NONE;
+ }
return IRQ_HANDLED;
}
@@ -2366,7 +2420,7 @@ static int wm5110_probe(struct platform_device *pdev)
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- goto error;
+ return ret;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
@@ -2376,7 +2430,6 @@ static int wm5110_probe(struct platform_device *pdev)
snd_soc_unregister_platform(&pdev->dev);
}
-error:
return ret;
}
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index c284c7b6db8b..dc8c3b1ebb6f 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -28,6 +28,11 @@
#include "wm8974.h"
+struct wm8974_priv {
+ unsigned int mclk;
+ unsigned int fs;
+};
+
static const struct reg_default wm8974_reg_defaults[] = {
{ 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 },
{ 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 },
@@ -379,6 +384,79 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return 0;
}
+static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out,
+ int *mclkdiv)
+{
+ unsigned int ratio = 2 * f_in / f_out;
+
+ if (ratio <= 2) {
+ *mclkdiv = WM8974_MCLKDIV_1;
+ ratio = 2;
+ } else if (ratio == 3) {
+ *mclkdiv = WM8974_MCLKDIV_1_5;
+ } else if (ratio == 4) {
+ *mclkdiv = WM8974_MCLKDIV_2;
+ } else if (ratio <= 6) {
+ *mclkdiv = WM8974_MCLKDIV_3;
+ ratio = 6;
+ } else if (ratio <= 8) {
+ *mclkdiv = WM8974_MCLKDIV_4;
+ ratio = 8;
+ } else if (ratio <= 12) {
+ *mclkdiv = WM8974_MCLKDIV_6;
+ ratio = 12;
+ } else if (ratio <= 16) {
+ *mclkdiv = WM8974_MCLKDIV_8;
+ ratio = 16;
+ } else {
+ *mclkdiv = WM8974_MCLKDIV_12;
+ ratio = 24;
+ }
+
+ return f_out * ratio / 2;
+}
+
+static int wm8974_update_clocks(struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int fs256;
+ unsigned int fpll = 0;
+ unsigned int f;
+ int mclkdiv;
+
+ if (!priv->mclk || !priv->fs)
+ return 0;
+
+ fs256 = 256 * priv->fs;
+
+ f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv);
+
+ if (f != priv->mclk) {
+ /* The PLL performs best around 90MHz */
+ fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv);
+ }
+
+ wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
+ wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv);
+
+ return 0;
+}
+
+static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+
+ priv->mclk = freq;
+
+ return wm8974_update_clocks(dai);
+}
+
static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
@@ -441,8 +519,15 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
+ struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
+ int err;
+
+ priv->fs = params_rate(params);
+ err = wm8974_update_clocks(dai);
+ if (err)
+ return err;
/* bit size */
switch (params_width(params)) {
@@ -547,6 +632,7 @@ static const struct snd_soc_dai_ops wm8974_ops = {
.set_fmt = wm8974_set_dai_fmt,
.set_clkdiv = wm8974_set_dai_clkdiv,
.set_pll = wm8974_set_dai_pll,
+ .set_sysclk = wm8974_set_dai_sysclk,
};
static struct snd_soc_dai_driver wm8974_dai = {
@@ -606,9 +692,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
static int wm8974_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct wm8974_priv *priv;
struct regmap *regmap;
int ret;
+ priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, priv);
+
regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index b4dba3a02aba..52d766efe14f 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -943,7 +943,7 @@ static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
}
}
-#define WM8997_RATES SNDRV_PCM_RATE_8000_192000
+#define WM8997_RATES SNDRV_PCM_RATE_KNOT
#define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 7719bc509e50..012396074a8a 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -1170,7 +1170,7 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
{ "DRC1 Signal Activity", NULL, "DRC1R" },
};
-#define WM8998_RATES SNDRV_PCM_RATE_8000_192000
+#define WM8998_RATES SNDRV_PCM_RATE_KNOT
#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index b9195b9c2b05..d3b1cb15e7f0 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -32,9 +32,6 @@
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <linux/mfd/arizona/registers.h>
-
-#include "arizona.h"
#include "wm_adsp.h"
#define adsp_crit(_dsp, fmt, ...) \
@@ -295,6 +292,8 @@ struct wm_adsp_compr {
u32 *raw_buf;
unsigned int copied_total;
+
+ unsigned int sample_rate;
};
#define WM_ADSP_DATA_WORD_SIZE 3
@@ -328,7 +327,7 @@ struct wm_adsp_buffer_region_def {
unsigned int size_offset;
};
-static struct wm_adsp_buffer_region_def ez2control_regions[] = {
+static const struct wm_adsp_buffer_region_def default_regions[] = {
{
.mem_type = WMFW_ADSP2_XM,
.base_offset = HOST_BUFFER_FIELD(X_buf_base),
@@ -350,10 +349,10 @@ struct wm_adsp_fw_caps {
u32 id;
struct snd_codec_desc desc;
int num_regions;
- struct wm_adsp_buffer_region_def *region_defs;
+ const struct wm_adsp_buffer_region_def *region_defs;
};
-static const struct wm_adsp_fw_caps ez2control_caps[] = {
+static const struct wm_adsp_fw_caps ctrl_caps[] = {
{
.id = SND_AUDIOCODEC_BESPOKE,
.desc = {
@@ -362,8 +361,26 @@ static const struct wm_adsp_fw_caps ez2control_caps[] = {
.num_sample_rates = 1,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .num_regions = ARRAY_SIZE(ez2control_regions),
- .region_defs = ez2control_regions,
+ .num_regions = ARRAY_SIZE(default_regions),
+ .region_defs = default_regions,
+ },
+};
+
+static const struct wm_adsp_fw_caps trace_caps[] = {
+ {
+ .id = SND_AUDIOCODEC_BESPOKE,
+ .desc = {
+ .max_ch = 8,
+ .sample_rates = {
+ 4000, 8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000, 64000, 88200,
+ 96000, 176400, 192000
+ },
+ .num_sample_rates = 15,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .num_regions = ARRAY_SIZE(default_regions),
+ .region_defs = default_regions,
},
};
@@ -382,11 +399,16 @@ static const struct {
[WM_ADSP_FW_CTRL] = {
.file = "ctrl",
.compr_direction = SND_COMPRESS_CAPTURE,
- .num_caps = ARRAY_SIZE(ez2control_caps),
- .caps = ez2control_caps,
+ .num_caps = ARRAY_SIZE(ctrl_caps),
+ .caps = ctrl_caps,
},
[WM_ADSP_FW_ASR] = { .file = "asr" },
- [WM_ADSP_FW_TRACE] = { .file = "trace" },
+ [WM_ADSP_FW_TRACE] = {
+ .file = "trace",
+ .compr_direction = SND_COMPRESS_CAPTURE,
+ .num_caps = ARRAY_SIZE(trace_caps),
+ .caps = trace_caps,
+ },
[WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
[WM_ADSP_FW_MISC] = { .file = "misc" },
};
@@ -719,19 +741,19 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg);
- scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
+ scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
ret = regmap_raw_write(dsp->regmap, reg, scratch,
- ctl->len);
+ len);
if (ret) {
adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
- ctl->len, reg, ret);
+ len, reg, ret);
kfree(scratch);
return ret;
}
- adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
+ adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
kfree(scratch);
@@ -778,20 +800,20 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg);
- scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
+ scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!scratch)
return -ENOMEM;
- ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
+ ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
if (ret) {
adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
- ctl->len, reg, ret);
+ len, reg, ret);
kfree(scratch);
return ret;
}
- adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
+ adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
- memcpy(buf, scratch, ctl->len);
+ memcpy(buf, scratch, len);
kfree(scratch);
return 0;
@@ -855,17 +877,18 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+ } else {
+ kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
}
- ret = snd_soc_add_card_controls(dsp->card,
- kcontrol, 1);
+ ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
if (ret < 0)
goto err_kcontrol;
kfree(kcontrol);
- ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
- ctl->name);
+ ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
return 0;
@@ -885,9 +908,7 @@ static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
continue;
- ret = wm_coeff_read_control(ctl,
- ctl->cache,
- ctl->len);
+ ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
if (ret < 0)
return ret;
}
@@ -904,9 +925,7 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp)
if (!ctl->enabled)
continue;
if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
- ret = wm_coeff_write_control(ctl,
- ctl->cache,
- ctl->len);
+ ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len);
if (ret < 0)
return ret;
}
@@ -1502,8 +1521,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
if (ret != 0) {
- adsp_err(dsp, "Failed to read algorithm list: %d\n",
- ret);
+ adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
kfree(alg);
return ERR_PTR(ret);
}
@@ -2002,8 +2020,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
goto err_mutex;
}
- val = (val & dsp->sysclk_mask)
- >> dsp->sysclk_shift;
+ val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP1_CONTROL_31,
@@ -2096,8 +2113,7 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
/* Wait for the RAM to start, should be near instantaneous */
for (count = 0; count < 10; ++count) {
- ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
- &val);
+ ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
if (ret != 0)
return ret;
@@ -2123,30 +2139,9 @@ static void wm_adsp2_boot_work(struct work_struct *work)
struct wm_adsp,
boot_work);
int ret;
- unsigned int val;
mutex_lock(&dsp->pwr_lock);
- /*
- * For simplicity set the DSP clock rate to be the
- * SYSCLK rate rather than making it configurable.
- */
- ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
- if (ret != 0) {
- adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
- goto err_mutex;
- }
- val = (val & ARIZONA_SYSCLK_FREQ_MASK)
- >> ARIZONA_SYSCLK_FREQ_SHIFT;
-
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CLOCKING,
- ADSP2_CLK_SEL_MASK, val);
- if (ret != 0) {
- adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
- goto err_mutex;
- }
-
ret = wm_adsp2_ena(dsp);
if (ret != 0)
goto err_mutex;
@@ -2186,8 +2181,21 @@ err_mutex:
mutex_unlock(&dsp->pwr_lock);
}
+static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
+{
+ int ret;
+
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING,
+ ADSP2_CLK_SEL_MASK,
+ freq << ADSP2_CLK_SEL_SHIFT);
+ if (ret != 0)
+ adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+}
+
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+ struct snd_kcontrol *kcontrol, int event,
+ unsigned int freq)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
@@ -2197,6 +2205,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
+ wm_adsp2_set_dspclk(dsp, freq);
queue_work(system_unbound_wq, &dsp->boot_work);
break;
default:
@@ -2471,6 +2480,8 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
if (!compr->raw_buf)
return -ENOMEM;
+ compr->sample_rate = params->codec.sample_rate;
+
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
@@ -2810,7 +2821,6 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
mutex_lock(&dsp->pwr_lock);
if (!buf) {
- adsp_err(dsp, "Spurious buffer IRQ\n");
ret = -ENODEV;
goto out;
}
@@ -2841,7 +2851,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
goto out;
}
- if (compr->stream)
+ if (compr && compr->stream)
snd_compr_fragment_elapsed(compr->stream);
out:
@@ -2911,6 +2921,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
tstamp->copied_total = compr->copied_total;
tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
+ tstamp->sampling_rate = compr->sample_rate;
out:
mutex_unlock(&dsp->pwr_lock);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 1a928ec54741..b61cb57e600f 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -80,7 +80,7 @@ struct wm_adsp {
SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
-#define WM_ADSP2_E(wname, num, event_fn) \
+#define WM_ADSP2(wname, num, event_fn) \
{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
.reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \
@@ -88,9 +88,6 @@ struct wm_adsp {
.reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
-#define WM_ADSP2(wname, num) \
- WM_ADSP2_E(wname, num, wm_adsp2_early_event)
-
extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
int wm_adsp1_init(struct wm_adsp *dsp);
@@ -100,7 +97,8 @@ int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event);
+ struct snd_kcontrol *kcontrol, int event,
+ unsigned int freq);
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 3736d9aabc56..50ca291cc225 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -5,7 +5,7 @@ config SND_DAVINCI_SOC
config SND_EDMA_SOC
tristate "SoC Audio for Texas Instruments chips using eDMA"
- depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI
+ depends on TI_EDMA
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M here if you want audio support for TI SoC which uses eDMA.
@@ -13,6 +13,7 @@ config SND_EDMA_SOC
- daVinci devices
- AM335x
- AM437x/AM438x
+ - DRA7xx family
config SND_DAVINCI_SOC_I2S
tristate
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 2ccb8bccc9d4..e1324989bd6b 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -77,6 +77,7 @@ struct davinci_mcasp {
u32 fifo_base;
struct device *dev;
struct snd_pcm_substream *substreams[2];
+ unsigned int dai_fmt;
/* McASP specific data */
int tdm_slots;
@@ -398,6 +399,9 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
bool fs_pol_rising;
bool inv_fs = false;
+ if (!fmt)
+ return 0;
+
pm_runtime_get_sync(mcasp->dev);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
@@ -529,6 +533,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
}
+
+ mcasp->dai_fmt = fmt;
out:
pm_runtime_put(mcasp->dev);
return ret;
@@ -1026,6 +1032,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
int period_size = params_period_size(params);
int ret;
+ ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt);
+ if (ret)
+ return ret;
+
/*
* If mcasp is BCLK master, and a BCLK divider was not provided by
* the machine driver, we need to calculate the ratio.
@@ -1517,6 +1527,8 @@ static int mcasp_reparent_fck(struct platform_device *pdev)
if (!parent_name)
return 0;
+ dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n");
+
gfclk = clk_get(&pdev->dev, "fck");
if (IS_ERR(gfclk)) {
dev_err(&pdev->dev, "failed to get fck\n");
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 14dfdee05fd5..35aabf9dc503 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -292,8 +292,8 @@ config SND_SOC_FSL_ASOC_CARD
select SND_SOC_FSL_SSI
help
ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
- ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
- and SGTL5000.
+ ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888,
+ CS4271, CS4272 and SGTL5000.
Say Y if you want to add support for Freescale Generic ASoC Sound Card.
endif # SND_IMX_SOC
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 562b3bd22d9a..dffd549a0e2a 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -28,6 +28,8 @@
#include "../codecs/wm8962.h"
#include "../codecs/wm8960.h"
+#define CS427x_SYSCLK_MCLK 0
+
#define RX 0
#define TX 1
@@ -99,19 +101,26 @@ struct fsl_asoc_card_priv {
/**
* This dapm route map exsits for DPCM link only.
* The other routes shall go through Device Tree.
+ *
+ * Note: keep all ASRC routes in the second half
+ * to drop them easily for non-ASRC cases.
*/
static const struct snd_soc_dapm_route audio_map[] = {
- {"CPU-Playback", NULL, "ASRC-Playback"},
+ /* 1st half -- Normal DAPM routes */
{"Playback", NULL, "CPU-Playback"},
- {"ASRC-Capture", NULL, "CPU-Capture"},
{"CPU-Capture", NULL, "Capture"},
+ /* 2nd half -- ASRC DAPM routes */
+ {"CPU-Playback", NULL, "ASRC-Playback"},
+ {"ASRC-Capture", NULL, "CPU-Capture"},
};
static const struct snd_soc_dapm_route audio_map_ac97[] = {
- {"AC97 Playback", NULL, "ASRC-Playback"},
+ /* 1st half -- Normal DAPM routes */
{"Playback", NULL, "AC97 Playback"},
- {"ASRC-Capture", NULL, "AC97 Capture"},
{"AC97 Capture", NULL, "Capture"},
+ /* 2nd half -- ASRC DAPM routes */
+ {"AC97 Playback", NULL, "ASRC-Playback"},
+ {"ASRC-Capture", NULL, "AC97 Capture"},
};
/* Add all possible widgets into here without being redundant */
@@ -528,6 +537,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.slot_width = 32;
priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
+ codec_dai_name = "cs4271-hifi";
+ priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK;
+ priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
codec_dai_name = "sgtl5000";
priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
@@ -593,6 +606,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
+ /* Drop the second half of DAPM routes -- ASRC */
+ if (!asrc_pdev)
+ priv->card.num_dapm_routes /= 2;
+
memcpy(priv->dai_link, fsl_asoc_card_dai,
sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
@@ -681,6 +698,7 @@ fail:
static const struct of_device_id fsl_asoc_card_dt_ids[] = {
{ .compatible = "fsl,imx-audio-ac97", },
{ .compatible = "fsl,imx-audio-cs42888", },
+ { .compatible = "fsl,imx-audio-cs427x", },
{ .compatible = "fsl,imx-audio-sgtl5000", },
{ .compatible = "fsl,imx-audio-wm8962", },
{ .compatible = "fsl,imx-audio-wm8960", },
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index fef264d27fd3..0754df771e3b 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -17,6 +17,7 @@
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <linux/time.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
@@ -919,7 +920,7 @@ static int fsl_sai_resume(struct device *dev)
regcache_cache_only(sai->regmap, false);
regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
- msleep(1);
+ usleep_range(1000, 2000);
regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
return regcache_sync(sai->regmap);
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 40dfd8a36484..ed8de1035cda 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -112,20 +112,6 @@ struct fsl_ssi_rxtx_reg_val {
struct fsl_ssi_reg_val tx;
};
-static const struct reg_default fsl_ssi_reg_defaults[] = {
- {CCSR_SSI_SCR, 0x00000000},
- {CCSR_SSI_SIER, 0x00003003},
- {CCSR_SSI_STCR, 0x00000200},
- {CCSR_SSI_SRCR, 0x00000200},
- {CCSR_SSI_STCCR, 0x00040000},
- {CCSR_SSI_SRCCR, 0x00040000},
- {CCSR_SSI_SACNT, 0x00000000},
- {CCSR_SSI_STMSK, 0x00000000},
- {CCSR_SSI_SRMSK, 0x00000000},
- {CCSR_SSI_SACCEN, 0x00000000},
- {CCSR_SSI_SACCDIS, 0x00000000},
-};
-
static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -190,8 +176,7 @@ static const struct regmap_config fsl_ssi_regconfig = {
.val_bits = 32,
.reg_stride = 4,
.val_format_endian = REGMAP_ENDIAN_NATIVE,
- .reg_defaults = fsl_ssi_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+ .num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1,
.readable_reg = fsl_ssi_readable_reg,
.volatile_reg = fsl_ssi_volatile_reg,
.precious_reg = fsl_ssi_precious_reg,
@@ -201,6 +186,7 @@ static const struct regmap_config fsl_ssi_regconfig = {
struct fsl_ssi_soc_data {
bool imx;
+ bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */
bool offline_config;
u32 sisr_write_mask;
};
@@ -303,6 +289,7 @@ static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
.imx = true,
+ .imx21regs = true,
.offline_config = true,
.sisr_write_mask = 0,
};
@@ -586,8 +573,12 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
*/
regmap_write(regs, CCSR_SSI_SACNT,
CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV);
- regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
- regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
+
+ /* no SACC{ST,EN,DIS} regs on imx21-class SSI */
+ if (!ssi_private->soc->imx21regs) {
+ regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
+ regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
+ }
/*
* Enable SSI, Transmit and Receive. AC97 has to communicate with the
@@ -1397,6 +1388,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *iomem;
char name[64];
+ struct regmap_config regconfig = fsl_ssi_regconfig;
of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
if (!of_id || !of_id->data)
@@ -1444,15 +1436,25 @@ static int fsl_ssi_probe(struct platform_device *pdev)
return PTR_ERR(iomem);
ssi_private->ssi_phys = res->start;
+ if (ssi_private->soc->imx21regs) {
+ /*
+ * According to datasheet imx21-class SSI
+ * don't have SACC{ST,EN,DIS} regs.
+ */
+ regconfig.max_register = CCSR_SSI_SRMSK;
+ regconfig.num_reg_defaults_raw =
+ CCSR_SSI_SRMSK / sizeof(uint32_t) + 1;
+ }
+
ret = of_property_match_string(np, "clock-names", "ipg");
if (ret < 0) {
ssi_private->has_ipg_clk_name = false;
ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
- &fsl_ssi_regconfig);
+ &regconfig);
} else {
ssi_private->has_ipg_clk_name = true;
ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev,
- "ipg", iomem, &fsl_ssi_regconfig);
+ "ipg", iomem, &regconfig);
}
if (IS_ERR(ssi_private->regs)) {
dev_err(&pdev->dev, "Failed to init register map\n");
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 0bab76051fd8..243700cc29e6 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -13,6 +13,7 @@
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/delay.h>
+#include <linux/time.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -127,7 +128,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
mutex_unlock(&psc_dma->mutex);
- msleep(1);
+ usleep_range(1000, 2000);
psc_ac97_warm_reset(ac97);
}
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 7d7c872c280d..b3e6c2300457 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -163,6 +163,7 @@ config SND_SOC_INTEL_SKYLAKE
tristate
select SND_HDA_EXT_CORE
select SND_SOC_TOPOLOGY
+ select SND_HDA_I915
select SND_SOC_INTEL_SST
config SND_SOC_INTEL_SKL_RT286_MACH
@@ -172,6 +173,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH
select SND_SOC_INTEL_SKYLAKE
select SND_SOC_RT286
select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
help
This adds support for ASoC machine driver for Skylake platforms
with RT286 I2S audio codec.
@@ -186,6 +188,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
select SND_SOC_NAU8825
select SND_SOC_SSM4567
select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + SSM4567.
@@ -200,6 +203,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
select SND_SOC_NAU8825
select SND_SOC_MAX98357A
select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + MAX98357A.
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 4fce03fc1870..3bc4b63b2f9d 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -342,6 +342,10 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
&chv_platform_data },
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
+ /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
+ {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
+ &chv_platform_data },
+
{},
};
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
index 3dc7358828b3..8afa6fe7b0b0 100644
--- a/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -318,7 +318,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
union ipc_header_high msg_high;
u32 msg_low;
struct ipc_dsp_hdr *dsp_hdr;
- unsigned int cmd_id;
msg_high = msg->mrfld_header.p.header_high;
msg_low = msg->mrfld_header.p.header_low_payload;
@@ -357,7 +356,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
return;
/* Copy command id so that we can use to put sst to reset */
dsp_hdr = (struct ipc_dsp_hdr *)data;
- cmd_id = dsp_hdr->cmd_id;
dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
msg_high.part.drv_id,
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 9a1752df45a9..032a2e753f0b 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -32,6 +32,18 @@
#include "../atom/sst-atom-controls.h"
#include "../common/sst-acpi.h"
+enum {
+ BYT_RT5640_DMIC1_MAP,
+ BYT_RT5640_DMIC2_MAP,
+ BYT_RT5640_IN1_MAP,
+};
+
+#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
+#define BYT_RT5640_DMIC_EN BIT(16)
+
+static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_DMIC_EN;
+
static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -70,18 +82,6 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
{"IN1P", NULL, "Internal Mic"},
};
-enum {
- BYT_RT5640_DMIC1_MAP,
- BYT_RT5640_DMIC2_MAP,
- BYT_RT5640_IN1_MAP,
-};
-
-#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
-#define BYT_RT5640_DMIC_EN BIT(16)
-
-static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
- BYT_RT5640_DMIC_EN;
-
static const struct snd_kcontrol_new byt_rt5640_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -174,7 +174,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
return ret;
}
- dmi_check_system(byt_rt5640_quirk_table);
switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
case BYT_RT5640_IN1_MAP:
custom_map = byt_rt5640_intmic_in1_map;
@@ -341,15 +340,34 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
struct sst_acpi_mach *mach;
+ const char *i2c_name = NULL;
+ int i;
+ int dai_index;
/* register the soc card */
byt_rt5640_card.dev = &pdev->dev;
mach = byt_rt5640_card.dev->platform_data;
+ /* fix index of codec dai */
+ dai_index = MERR_DPCM_COMPR + 1;
+ for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) {
+ if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) {
+ dai_index = i;
+ break;
+ }
+ }
+
/* fixup codec name based on HID */
- snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
- "%s%s%s", "i2c-", mach->id, ":00");
- byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name;
+ i2c_name = sst_acpi_find_name_from_hid(mach->id);
+ if (i2c_name != NULL) {
+ snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
+ "%s%s", "i2c-", i2c_name);
+
+ byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name;
+ }
+
+ /* check quirks before creating card */
+ dmi_check_system(byt_rt5640_quirk_table);
ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
index 90588d6e64fc..e609f089593a 100644
--- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c
+++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c
@@ -287,33 +287,20 @@ static struct snd_soc_card snd_soc_card_cht = {
.num_controls = ARRAY_SIZE(cht_mc_controls),
};
-static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- *(bool *)context = true;
- return AE_OK;
-}
-
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
- bool found = false;
struct cht_mc_private *drv;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv)
return -ENOMEM;
- if (ACPI_SUCCESS(acpi_get_devices(
- "104C227E",
- snd_acpi_codec_match,
- &found, NULL)) && found) {
- drv->ts3a227e_present = true;
- } else {
+ drv->ts3a227e_present = acpi_dev_present("104C227E");
+ if (!drv->ts3a227e_present) {
/* no need probe TI jack detection chip */
snd_soc_card_cht.aux_dev = NULL;
snd_soc_card_cht.num_aux_devs = 0;
- drv->ts3a227e_present = false;
}
/* register the soc card */
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index a7b96a9a4e0e..2a6f80843bc9 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -147,6 +147,17 @@ static const struct snd_kcontrol_new cht_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
+static struct snd_soc_jack_pin cht_bsw_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -202,9 +213,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
else
jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
- ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
+ ret = snd_soc_card_jack_new(runtime->card, "Headset",
jack_type, &ctx->jack,
- NULL, 0);
+ cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
if (ret) {
dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
return ret;
@@ -333,20 +344,12 @@ static struct cht_acpi_card snd_soc_cards[] = {
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
};
-static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- *(bool *)context = true;
- return AE_OK;
-}
-
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
int i;
struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
- bool found = false;
char codec_name[16];
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
@@ -354,10 +357,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
- if (ACPI_SUCCESS(acpi_get_devices(
- snd_soc_cards[i].codec_id,
- snd_acpi_codec_match,
- &found, NULL)) && found) {
+ if (acpi_dev_present(snd_soc_cards[i].codec_id)) {
dev_dbg(&pdev->dev,
"found codec %s\n", snd_soc_cards[i].codec_id);
card = snd_soc_cards[i].soc_card;
diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
index ab7da9c304b2..72176b79a18d 100644
--- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c
+++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
@@ -22,6 +22,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "../../codecs/nau8825.h"
+#include "../../codecs/hdac_hdmi.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_MAXIM_CODEC_DAI "HiFi"
@@ -29,6 +30,16 @@
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
+enum {
+ SKL_DPCM_AUDIO_PB = 0,
+ SKL_DPCM_AUDIO_CP,
+ SKL_DPCM_AUDIO_REF_CP,
+ SKL_DPCM_AUDIO_DMIC_CP,
+ SKL_DPCM_AUDIO_HDMI1_PB,
+ SKL_DPCM_AUDIO_HDMI2_PB,
+ SKL_DPCM_AUDIO_HDMI3_PB,
+};
+
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
@@ -87,7 +98,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_SPK("Spk", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
- SND_SOC_DAPM_SINK("WoV Sink"),
SND_SOC_DAPM_SPK("DP", NULL),
SND_SOC_DAPM_SPK("HDMI", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
@@ -107,7 +117,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
{ "MIC", NULL, "Headset Mic" },
{ "DMic", NULL, "SoC DMIC" },
- {"WoV Sink", NULL, "hwd_in sink"},
{"HDMI", NULL, "hif5 Output"},
{"DP", NULL, "hif6 Output"},
@@ -124,8 +133,14 @@ static const struct snd_soc_dapm_route skylake_map[] = {
/* DMIC */
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
{ "DMIC01 Rx", NULL, "DMIC AIF" },
- { "hifi1", NULL, "iDisp Tx"},
- { "iDisp Tx", NULL, "iDisp_out"},
+
+ { "hifi3", NULL, "iDisp3 Tx"},
+ { "iDisp3 Tx", NULL, "iDisp3_out"},
+ { "hifi2", NULL, "iDisp2 Tx"},
+ { "iDisp2 Tx", NULL, "iDisp2_out"},
+ { "hifi1", NULL, "iDisp1 Tx"},
+ { "iDisp1 Tx", NULL, "iDisp1_out"},
+
{ "Headphone Jack", NULL, "Platform Clock" },
{ "Headset Mic", NULL, "Platform Clock" },
};
@@ -171,11 +186,31 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
nau8825_enable_jack_detect(codec, &skylake_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
return ret;
}
+static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+}
+
+static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+}
+
+static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+}
+
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dapm_context *dapm;
@@ -318,7 +353,7 @@ static struct snd_soc_ops skylaye_refcap_ops = {
/* skylake digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link skylake_dais[] = {
/* Front End DAI links */
- {
+ [SKL_DPCM_AUDIO_PB] = {
.name = "Skl Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
@@ -333,7 +368,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_playback = 1,
.ops = &skylake_nau8825_fe_ops,
},
- {
+ [SKL_DPCM_AUDIO_CP] = {
.name = "Skl Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
@@ -347,7 +382,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_capture = 1,
.ops = &skylake_nau8825_fe_ops,
},
- {
+ [SKL_DPCM_AUDIO_REF_CP] = {
.name = "Skl Audio Reference cap",
.stream_name = "Wake on Voice",
.cpu_dai_name = "Reference Pin",
@@ -361,7 +396,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylaye_refcap_ops,
},
- {
+ [SKL_DPCM_AUDIO_DMIC_CP] = {
.name = "Skl Audio DMIC cap",
.stream_name = "dmiccap",
.cpu_dai_name = "DMIC Pin",
@@ -374,15 +409,45 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylake_dmic_ops,
},
- {
- .name = "Skl HDMI Port",
- .stream_name = "Hdmi",
- .cpu_dai_name = "HDMI Pin",
+ [SKL_DPCM_AUDIO_HDMI1_PB] = {
+ .name = "Skl HDMI Port1",
+ .stream_name = "Hdmi1",
+ .cpu_dai_name = "HDMI1 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [SKL_DPCM_AUDIO_HDMI2_PB] = {
+ .name = "Skl HDMI Port2",
+ .stream_name = "Hdmi2",
+ .cpu_dai_name = "HDMI2 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
.init = NULL,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [SKL_DPCM_AUDIO_HDMI3_PB] = {
+ .name = "Skl HDMI Port3",
+ .stream_name = "Hdmi3",
+ .cpu_dai_name = "HDMI3 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ .init = NULL,
.nonatomic = 1,
.dynamic = 1,
},
@@ -407,7 +472,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
{
/* SSP1 - Codec */
.name = "SSP1-Codec",
- .be_id = 0,
+ .be_id = 1,
.cpu_dai_name = "SSP1 Pin",
.platform_name = "0000:00:1f.3",
.no_pcm = 1,
@@ -424,7 +489,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
},
{
.name = "dmic01",
- .be_id = 1,
+ .be_id = 2,
.cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
@@ -435,13 +500,36 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
},
{
- .name = "iDisp",
+ .name = "iDisp1",
.be_id = 3,
- .cpu_dai_name = "iDisp Pin",
+ .cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
+ .init = skylake_hdmi1_init,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp2",
+ .be_id = 4,
+ .cpu_dai_name = "iDisp2 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi2",
+ .platform_name = "0000:00:1f.3",
+ .init = skylake_hdmi2_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp3",
+ .be_id = 5,
+ .cpu_dai_name = "iDisp3 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi3",
+ .platform_name = "0000:00:1f.3",
+ .init = skylake_hdmi3_init,
+ .dpcm_playback = 1,
.no_pcm = 1,
},
};
diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
index c071812f31e5..5f1ca99ae9b0 100644
--- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
+++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
@@ -26,6 +26,7 @@
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include "../../codecs/nau8825.h"
+#include "../../codecs/hdac_hdmi.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
@@ -33,6 +34,16 @@
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
+enum {
+ SKL_DPCM_AUDIO_PB = 0,
+ SKL_DPCM_AUDIO_CP,
+ SKL_DPCM_AUDIO_REF_CP,
+ SKL_DPCM_AUDIO_DMIC_CP,
+ SKL_DPCM_AUDIO_HDMI1_PB,
+ SKL_DPCM_AUDIO_HDMI2_PB,
+ SKL_DPCM_AUDIO_HDMI3_PB,
+};
+
static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
@@ -92,7 +103,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
SND_SOC_DAPM_SPK("Left Speaker", NULL),
SND_SOC_DAPM_SPK("Right Speaker", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
- SND_SOC_DAPM_SINK("WoV Sink"),
SND_SOC_DAPM_SPK("DP", NULL),
SND_SOC_DAPM_SPK("HDMI", NULL),
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
@@ -113,8 +123,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
{"MIC", NULL, "Headset Mic"},
{"DMic", NULL, "SoC DMIC"},
- {"WoV Sink", NULL, "hwd_in sink"},
-
{"HDMI", NULL, "hif5 Output"},
{"DP", NULL, "hif6 Output"},
/* CODEC BE connections */
@@ -122,6 +130,11 @@ static const struct snd_soc_dapm_route skylake_map[] = {
{ "Right Playback", NULL, "ssp0 Tx"},
{ "ssp0 Tx", NULL, "codec0_out"},
+ /* IV feedback path */
+ { "codec0_lp_in", NULL, "ssp0 Rx"},
+ { "ssp0 Rx", NULL, "Left Capture Sense" },
+ { "ssp0 Rx", NULL, "Right Capture Sense" },
+
{ "Playback", NULL, "ssp1 Tx"},
{ "ssp1 Tx", NULL, "codec1_out"},
@@ -131,8 +144,14 @@ static const struct snd_soc_dapm_route skylake_map[] = {
/* DMIC */
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
{ "DMIC01 Rx", NULL, "DMIC AIF" },
- { "hifi1", NULL, "iDisp Tx"},
- { "iDisp Tx", NULL, "iDisp_out"},
+
+ { "hifi3", NULL, "iDisp3 Tx"},
+ { "iDisp3 Tx", NULL, "iDisp3_out"},
+ { "hifi2", NULL, "iDisp2 Tx"},
+ { "iDisp2 Tx", NULL, "iDisp2_out"},
+ { "hifi1", NULL, "iDisp1 Tx"},
+ { "iDisp1 Tx", NULL, "iDisp1_out"},
+
{ "Headphone Jack", NULL, "Platform Clock" },
{ "Headset Mic", NULL, "Platform Clock" },
};
@@ -197,11 +216,32 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
nau8825_enable_jack_detect(codec, &skylake_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
return ret;
}
+static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+}
+
+static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+}
+
+
+static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+}
+
static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dapm_context *dapm;
@@ -362,7 +402,7 @@ static struct snd_soc_ops skylaye_refcap_ops = {
/* skylake digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link skylake_dais[] = {
/* Front End DAI links */
- {
+ [SKL_DPCM_AUDIO_PB] = {
.name = "Skl Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
@@ -377,7 +417,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_playback = 1,
.ops = &skylake_nau8825_fe_ops,
},
- {
+ [SKL_DPCM_AUDIO_CP] = {
.name = "Skl Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
@@ -391,7 +431,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dpcm_capture = 1,
.ops = &skylake_nau8825_fe_ops,
},
- {
+ [SKL_DPCM_AUDIO_REF_CP] = {
.name = "Skl Audio Reference cap",
.stream_name = "Wake on Voice",
.cpu_dai_name = "Reference Pin",
@@ -405,7 +445,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylaye_refcap_ops,
},
- {
+ [SKL_DPCM_AUDIO_DMIC_CP] = {
.name = "Skl Audio DMIC cap",
.stream_name = "dmiccap",
.cpu_dai_name = "DMIC Pin",
@@ -418,13 +458,43 @@ static struct snd_soc_dai_link skylake_dais[] = {
.dynamic = 1,
.ops = &skylake_dmic_ops,
},
- {
- .name = "Skl HDMI Port",
- .stream_name = "Hdmi",
- .cpu_dai_name = "HDMI Pin",
+ [SKL_DPCM_AUDIO_HDMI1_PB] = {
+ .name = "Skl HDMI Port1",
+ .stream_name = "Hdmi1",
+ .cpu_dai_name = "HDMI1 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [SKL_DPCM_AUDIO_HDMI2_PB] = {
+ .name = "Skl HDMI Port2",
+ .stream_name = "Hdmi2",
+ .cpu_dai_name = "HDMI2 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [SKL_DPCM_AUDIO_HDMI3_PB] = {
+ .name = "Skl HDMI Port3",
+ .stream_name = "Hdmi3",
+ .cpu_dai_name = "HDMI3 Pin",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.platform_name = "0000:00:1f.3",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
.init = NULL,
.nonatomic = 1,
@@ -448,11 +518,12 @@ static struct snd_soc_dai_link skylake_dais[] = {
.ignore_pmdown_time = 1,
.be_hw_params_fixup = skylake_ssp_fixup,
.dpcm_playback = 1,
+ .dpcm_capture = 1,
},
{
/* SSP1 - Codec */
.name = "SSP1-Codec",
- .be_id = 0,
+ .be_id = 1,
.cpu_dai_name = "SSP1 Pin",
.platform_name = "0000:00:1f.3",
.no_pcm = 1,
@@ -469,7 +540,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
},
{
.name = "dmic01",
- .be_id = 1,
+ .be_id = 2,
.cpu_dai_name = "DMIC01 Pin",
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
@@ -480,13 +551,36 @@ static struct snd_soc_dai_link skylake_dais[] = {
.no_pcm = 1,
},
{
- .name = "iDisp",
+ .name = "iDisp1",
.be_id = 3,
- .cpu_dai_name = "iDisp Pin",
+ .cpu_dai_name = "iDisp1 Pin",
.codec_name = "ehdaudio0D2",
.codec_dai_name = "intel-hdmi-hifi1",
.platform_name = "0000:00:1f.3",
.dpcm_playback = 1,
+ .init = skylake_hdmi1_init,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp2",
+ .be_id = 4,
+ .cpu_dai_name = "iDisp2 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi2",
+ .platform_name = "0000:00:1f.3",
+ .init = skylake_hdmi2_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp3",
+ .be_id = 5,
+ .cpu_dai_name = "iDisp3 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi3",
+ .platform_name = "0000:00:1f.3",
+ .init = skylake_hdmi3_init,
+ .dpcm_playback = 1,
.no_pcm = 1,
},
};
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
index 2cbcbe412661..2016397a8e75 100644
--- a/sound/soc/intel/boards/skl_rt286.c
+++ b/sound/soc/intel/boards/skl_rt286.c
@@ -26,8 +26,20 @@
#include <sound/jack.h>
#include <sound/pcm_params.h>
#include "../../codecs/rt286.h"
+#include "../../codecs/hdac_hdmi.h"
static struct snd_soc_jack skylake_headset;
+
+enum {
+ SKL_DPCM_AUDIO_PB = 0,
+ SKL_DPCM_AUDIO_CP,
+ SKL_DPCM_AUDIO_REF_CP,
+ SKL_DPCM_AUDIO_DMIC_CP,
+ SKL_DPCM_AUDIO_HDMI1_PB,
+ SKL_DPCM_AUDIO_HDMI2_PB,
+ SKL_DPCM_AUDIO_HDMI3_PB,
+};
+
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin skylake_headset_pins[] = {
{
@@ -52,7 +64,9 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_MIC("DMIC2", NULL),
SND_SOC_DAPM_MIC("SoC DMIC", NULL),
- SND_SOC_DAPM_SINK("WoV Sink"),
+ SND_SOC_DAPM_SPK("HDMI1", NULL),
+ SND_SOC_DAPM_SPK("HDMI2", NULL),
+ SND_SOC_DAPM_SPK("HDMI3", NULL),
};
static const struct snd_soc_dapm_route skylake_rt286_map[] = {
@@ -70,7 +84,9 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
{"DMIC1 Pin", NULL, "DMIC2"},
{"DMic", NULL, "SoC DMIC"},
- {"WoV Sink", NULL, "hwd_in sink"},
+ {"HDMI1", NULL, "hif5 Output"},
+ {"HDMI2", NULL, "hif6 Output"},
+ {"HDMI3", NULL, "hif7 Output"},
/* CODEC BE connections */
{ "AIF1 Playback", NULL, "ssp0 Tx"},
@@ -84,8 +100,12 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
{ "dmic01_hifi", NULL, "DMIC01 Rx" },
{ "DMIC01 Rx", NULL, "DMIC AIF" },
- { "hif1", NULL, "iDisp Tx"},
- { "iDisp Tx", NULL, "iDisp_out"},
+ { "hifi3", NULL, "iDisp3 Tx"},
+ { "iDisp3 Tx", NULL, "iDisp3_out"},
+ { "hifi2", NULL, "iDisp2 Tx"},
+ { "iDisp2 Tx", NULL, "iDisp2_out"},
+ { "hifi1", NULL, "iDisp1 Tx"},
+ { "iDisp1 Tx", NULL, "iDisp1_out"},
};
@@ -116,11 +136,17 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
rt286_mic_detect(codec, &skylake_headset);
snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
- snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
return 0;
}
+static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id);
+}
+
static unsigned int rates[] = {
48000,
};
@@ -249,7 +275,7 @@ static struct snd_soc_ops skylake_dmic_ops = {
/* skylake digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link skylake_rt286_dais[] = {
/* Front End DAI links */
- {
+ [SKL_DPCM_AUDIO_PB] = {
.name = "Skl Audio Port",
.stream_name = "Audio",
.cpu_dai_name = "System Pin",
@@ -266,7 +292,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dpcm_playback = 1,
.ops = &skylake_rt286_fe_ops,
},
- {
+ [SKL_DPCM_AUDIO_CP] = {
.name = "Skl Audio Capture Port",
.stream_name = "Audio Record",
.cpu_dai_name = "System Pin",
@@ -282,7 +308,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dpcm_capture = 1,
.ops = &skylake_rt286_fe_ops,
},
- {
+ [SKL_DPCM_AUDIO_REF_CP] = {
.name = "Skl Audio Reference cap",
.stream_name = "refcap",
.cpu_dai_name = "Reference Pin",
@@ -295,7 +321,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.nonatomic = 1,
.dynamic = 1,
},
- {
+ [SKL_DPCM_AUDIO_DMIC_CP] = {
.name = "Skl Audio DMIC cap",
.stream_name = "dmiccap",
.cpu_dai_name = "DMIC Pin",
@@ -308,6 +334,42 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dynamic = 1,
.ops = &skylake_dmic_ops,
},
+ [SKL_DPCM_AUDIO_HDMI1_PB] = {
+ .name = "Skl HDMI Port1",
+ .stream_name = "Hdmi1",
+ .cpu_dai_name = "HDMI1 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [SKL_DPCM_AUDIO_HDMI2_PB] = {
+ .name = "Skl HDMI Port2",
+ .stream_name = "Hdmi2",
+ .cpu_dai_name = "HDMI2 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [SKL_DPCM_AUDIO_HDMI3_PB] = {
+ .name = "Skl HDMI Port3",
+ .stream_name = "Hdmi3",
+ .cpu_dai_name = "HDMI3 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:1f.3",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
/* Back End DAI links */
{
@@ -341,6 +403,39 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
.dpcm_capture = 1,
.no_pcm = 1,
},
+ {
+ .name = "iDisp1",
+ .be_id = 2,
+ .cpu_dai_name = "iDisp1 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi1",
+ .platform_name = "0000:00:1f.3",
+ .init = skylake_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp2",
+ .be_id = 3,
+ .cpu_dai_name = "iDisp2 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi2",
+ .platform_name = "0000:00:1f.3",
+ .init = skylake_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp3",
+ .be_id = 4,
+ .cpu_dai_name = "iDisp3 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi3",
+ .platform_name = "0000:00:1f.3",
+ .init = skylake_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
};
/* skylake audio machine driver for SPT + RT286S */
diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h
index 3ee3b7ab5d03..4dcfb7e5ed70 100644
--- a/sound/soc/intel/common/sst-acpi.h
+++ b/sound/soc/intel/common/sst-acpi.h
@@ -14,6 +14,9 @@
#include <linux/acpi.h>
+/* translation fron HID to I2C name, needed for DAI codec_name */
+const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
+
/* acpi match */
struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h
index 81aa1ed64201..97dc1ae05e69 100644
--- a/sound/soc/intel/common/sst-dsp-priv.h
+++ b/sound/soc/intel/common/sst-dsp-priv.h
@@ -317,6 +317,7 @@ struct sst_dsp {
struct skl_cl_dev cl_dev;
u32 intr_status;
const struct firmware *fw;
+ struct snd_dma_buffer dmab;
};
/* Size optimised DRAM/IRAM memcpy */
diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c
index 3b4539d21492..789843307a49 100644
--- a/sound/soc/intel/common/sst-match-acpi.c
+++ b/sound/soc/intel/common/sst-match-acpi.c
@@ -13,17 +13,53 @@
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
#include "sst-acpi.h"
+static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ struct acpi_device *adev;
+ const char *name = NULL;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;
+
+ if (adev->status.present && adev->status.functional) {
+ name = acpi_dev_name(adev);
+ *(const char **)ret = name;
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+{
+ const char *name = NULL;
+ acpi_status status;
+
+ status = acpi_get_devices(hid, sst_acpi_find_name, NULL,
+ (void **)&name);
+
+ if (ACPI_FAILURE(status) || name[0] == '\0')
+ return NULL;
+
+ return name;
+}
+EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid);
+
static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
void *context, void **ret)
{
+ unsigned long long sta;
+ acpi_status status;
+
*(bool *)context = true;
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
+ *(bool *)context = false;
+
return AE_OK;
}
@@ -37,7 +73,6 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
sst_acpi_mach_match,
&found, NULL)) && found)
return mach;
-
return NULL;
}
EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 4629372d7c8e..79c5089b85d6 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -72,17 +72,47 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
}
+static struct skl_dsp_loader_ops skl_get_loader_ops(void)
+{
+ struct skl_dsp_loader_ops loader_ops;
+
+ memset(&loader_ops, 0, sizeof(struct skl_dsp_loader_ops));
+
+ loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
+ loader_ops.free_dma_buf = skl_free_dma_buf;
+
+ return loader_ops;
+};
+
+static const struct skl_dsp_ops dsp_ops[] = {
+ {
+ .id = 0x9d70,
+ .loader_ops = skl_get_loader_ops,
+ .init = skl_sst_dsp_init,
+ .cleanup = skl_sst_dsp_cleanup
+ },
+};
+
+static int skl_get_dsp_ops(int pci_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) {
+ if (dsp_ops[i].id == pci_id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
int skl_init_dsp(struct skl *skl)
{
void __iomem *mmio_base;
struct hdac_ext_bus *ebus = &skl->ebus;
struct hdac_bus *bus = ebus_to_hbus(ebus);
- int irq = bus->irq;
struct skl_dsp_loader_ops loader_ops;
- int ret;
-
- loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
- loader_ops.free_dma_buf = skl_free_dma_buf;
+ int irq = bus->irq;
+ int ret, index;
/* enable ppcap interrupt */
snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
@@ -95,8 +125,14 @@ int skl_init_dsp(struct skl *skl)
return -ENXIO;
}
- ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
+ index = skl_get_dsp_ops(skl->pci->device);
+ if (index < 0)
+ return -EINVAL;
+
+ loader_ops = dsp_ops[index].loader_ops();
+ ret = dsp_ops[index].init(bus->dev, mmio_base, irq,
skl->fw_name, loader_ops, &skl->skl_sst);
+
if (ret < 0)
return ret;
@@ -106,18 +142,26 @@ int skl_init_dsp(struct skl *skl)
return ret;
}
-void skl_free_dsp(struct skl *skl)
+int skl_free_dsp(struct skl *skl)
{
struct hdac_ext_bus *ebus = &skl->ebus;
struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct skl_sst *ctx = skl->skl_sst;
+ struct skl_sst *ctx = skl->skl_sst;
+ int index;
/* disable ppcap interrupt */
snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
- skl_sst_dsp_cleanup(bus->dev, ctx);
+ index = skl_get_dsp_ops(skl->pci->device);
+ if (index < 0)
+ return -EIO;
+
+ dsp_ops[index].cleanup(bus->dev, ctx);
+
if (ctx->dsp->addr.lpe)
iounmap(ctx->dsp->addr.lpe);
+
+ return 0;
}
int skl_suspend_dsp(struct skl *skl)
@@ -238,9 +282,8 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
* Calculate the gatewat settings required for copier module, type of
* gateway and index of gateway to use
*/
-static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
- struct skl_module_cfg *mconfig,
- struct skl_cpr_cfg *cpr_mconfig)
+static u32 skl_get_node_id(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig)
{
union skl_connector_node_id node_id = {0};
union skl_ssp_dma_node ssp_node = {0};
@@ -289,13 +332,24 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
break;
default:
- cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
+ node_id.val = 0xFFFFFFFF;
+ break;
+ }
+
+ return node_id.val;
+}
+
+static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig,
+ struct skl_cpr_cfg *cpr_mconfig)
+{
+ cpr_mconfig->gtw_cfg.node_id = skl_get_node_id(ctx, mconfig);
+
+ if (cpr_mconfig->gtw_cfg.node_id == SKL_NON_GATEWAY_CPR_NODE_ID) {
cpr_mconfig->cpr_feature_mask = 0;
return;
}
- cpr_mconfig->gtw_cfg.node_id = node_id.val;
-
if (SKL_CONN_SOURCE == mconfig->hw_conn_type)
cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs;
else
@@ -307,6 +361,46 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
skl_copy_copier_caps(mconfig, cpr_mconfig);
}
+#define DMA_CONTROL_ID 5
+
+int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+{
+ struct skl_dma_control *dma_ctrl;
+ struct skl_i2s_config_blob config_blob;
+ struct skl_ipc_large_config_msg msg = {0};
+ int err = 0;
+
+
+ /*
+ * if blob size is same as capablity size, then no dma control
+ * present so return
+ */
+ if (mconfig->formats_config.caps_size == sizeof(config_blob))
+ return 0;
+
+ msg.large_param_id = DMA_CONTROL_ID;
+ msg.param_data_size = sizeof(struct skl_dma_control) +
+ mconfig->formats_config.caps_size;
+
+ dma_ctrl = kzalloc(msg.param_data_size, GFP_KERNEL);
+ if (dma_ctrl == NULL)
+ return -ENOMEM;
+
+ dma_ctrl->node_id = skl_get_node_id(ctx, mconfig);
+
+ /* size in dwords */
+ dma_ctrl->config_length = sizeof(config_blob) / 4;
+
+ memcpy(dma_ctrl->config_data, mconfig->formats_config.caps,
+ mconfig->formats_config.caps_size);
+
+ err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl);
+
+ kfree(dma_ctrl);
+
+ return err;
+}
+
static void skl_setup_out_format(struct skl_sst *ctx,
struct skl_module_cfg *mconfig,
struct skl_audio_data_format *out_fmt)
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 6e4b21cdb1bd..14d1916ea9f8 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -145,3 +145,37 @@ struct nhlt_specific_cfg
return NULL;
}
+
+static void skl_nhlt_trim_space(struct skl *skl)
+{
+ char *s = skl->tplg_name;
+ int cnt;
+ int i;
+
+ cnt = 0;
+ for (i = 0; s[i]; i++) {
+ if (!isspace(s[i]))
+ s[cnt++] = s[i];
+ }
+
+ s[cnt] = '\0';
+}
+
+int skl_nhlt_update_topology_bin(struct skl *skl)
+{
+ struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+ struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
+ struct device *dev = bus->dev;
+
+ dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n",
+ nhlt->header.oem_id, nhlt->header.oem_table_id,
+ nhlt->header.oem_revision);
+
+ snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s",
+ skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,
+ nhlt->header.oem_revision, "-tplg.bin");
+
+ skl_nhlt_trim_space(skl);
+
+ return 0;
+}
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index b6e6b61d10ec..dab0900eef26 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -206,6 +206,23 @@ static int skl_get_format(struct snd_pcm_substream *substream,
return format_val;
}
+static int skl_be_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_sst *ctx = skl->skl_sst;
+ struct skl_module_cfg *mconfig;
+
+ if ((dai->playback_active > 1) || (dai->capture_active > 1))
+ return 0;
+
+ mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
+ if (mconfig == NULL)
+ return -EINVAL;
+
+ return skl_dsp_set_dma_control(ctx, mconfig);
+}
+
static int skl_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -458,7 +475,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
struct hdac_ext_stream *link_dev;
struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
- struct skl_dma_params *dma_params;
+ struct hdac_ext_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct skl_pipe_params p_params = {0};
@@ -470,11 +487,9 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
/* set the stream tag in the codec dai dma params */
- dma_params = (struct skl_dma_params *)
- snd_soc_dai_get_dma_data(codec_dai, substream);
+ dma_params = snd_soc_dai_get_dma_data(codec_dai, substream);
if (dma_params)
dma_params->stream_tag = hdac_stream(link_dev)->stream_tag;
- snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
p_params.s_fmt = snd_pcm_format_width(params_format(params));
p_params.ch = params_channels(params);
@@ -588,6 +603,7 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = {
static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
.hw_params = skl_be_hw_params,
+ .prepare = skl_be_prepare,
};
static struct snd_soc_dai_ops skl_link_dai_ops = {
@@ -660,6 +676,51 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
},
+{
+ .name = "HDMI1 Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "HDMI1 Playback",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+},
+{
+ .name = "HDMI2 Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "HDMI2 Playback",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+},
+{
+ .name = "HDMI3 Pin",
+ .ops = &skl_pcm_dai_ops,
+ .playback = {
+ .stream_name = "HDMI3 Playback",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+},
/* BE CPU Dais */
{
@@ -699,14 +760,41 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
},
},
{
- .name = "iDisp Pin",
+ .name = "iDisp1 Pin",
.ops = &skl_link_dai_ops,
.playback = {
- .stream_name = "iDisp Tx",
+ .stream_name = "iDisp1 Tx",
.channels_min = HDA_STEREO,
.channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "iDisp2 Pin",
+ .ops = &skl_link_dai_ops,
+ .playback = {
+ .stream_name = "iDisp2 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ },
+},
+{
+ .name = "iDisp3 Pin",
+ .ops = &skl_link_dai_ops,
+ .playback = {
+ .stream_name = "iDisp3 Tx",
+ .channels_min = HDA_STEREO,
+ .channels_max = HDA_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
+ SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
@@ -863,7 +951,9 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
else
delay += hstream->bufsize;
}
- delay = (hstream->bufsize == delay) ? 0 : delay;
+
+ if (hstream->bufsize == delay)
+ delay = 0;
if (delay >= hstream->period_bytes) {
dev_info(bus->dev,
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index 1bfb7f63b572..a5267e8a96e0 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -34,7 +34,7 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
mutex_unlock(&ctx->mutex);
}
-static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
+static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
{
int ret;
@@ -60,7 +60,7 @@ static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
return ret;
}
-static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
+static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
{
int ret;
@@ -87,7 +87,7 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
return ret;
}
-static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
+static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
{
int val;
bool is_enable;
@@ -140,7 +140,7 @@ static int skl_dsp_start_core(struct sst_dsp *ctx)
return ret;
}
-static int skl_dsp_core_power_up(struct sst_dsp *ctx)
+static int skl_dsp_core_power_up(struct sst_dsp *ctx)
{
int ret;
@@ -166,7 +166,7 @@ static int skl_dsp_core_power_up(struct sst_dsp *ctx)
return ret;
}
-static int skl_dsp_core_power_down(struct sst_dsp *ctx)
+static int skl_dsp_core_power_down(struct sst_dsp *ctx)
{
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
@@ -181,7 +181,7 @@ static int skl_dsp_core_power_down(struct sst_dsp *ctx)
"Power down");
}
-static int skl_dsp_enable_core(struct sst_dsp *ctx)
+int skl_dsp_enable_core(struct sst_dsp *ctx)
{
int ret;
@@ -195,7 +195,7 @@ static int skl_dsp_enable_core(struct sst_dsp *ctx)
return skl_dsp_start_core(ctx);
}
-int skl_dsp_disable_core(struct sst_dsp *ctx)
+int skl_dsp_disable_core(struct sst_dsp *ctx)
{
int ret;
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
index cbb40751c37e..b6e310d49dd6 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ b/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -53,6 +53,10 @@ struct sst_dsp_device;
/* HIPCT */
#define SKL_ADSP_REG_HIPCT_BUSY BIT(31)
+/* FW base IDs */
+#define SKL_INSTANCE_ID 0
+#define SKL_BASE_FW_MODULE_ID 0
+
/* Intel HD Audio SRAM Window 1 */
#define SKL_ADSP_SRAM1_BASE 0xA000
@@ -144,7 +148,8 @@ int skl_cldma_prepare(struct sst_dsp *ctx);
void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
struct sst_dsp_device *sst_dev, int irq);
-int skl_dsp_disable_core(struct sst_dsp *ctx);
+int skl_dsp_enable_core(struct sst_dsp *ctx);
+int skl_dsp_disable_core(struct sst_dsp *ctx);
bool is_skl_dsp_running(struct sst_dsp *ctx);
irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
int skl_dsp_wake(struct sst_dsp *ctx);
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index e26f4746afb7..348a734f8e24 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -35,9 +35,6 @@
#define SKL_ADSP_FW_STATUS SKL_ADSP_SRAM0_BASE
#define SKL_ADSP_ERROR_CODE (SKL_ADSP_FW_STATUS + 0x4)
-#define SKL_INSTANCE_ID 0
-#define SKL_BASE_FW_MODULE_ID 0
-
#define SKL_NUM_MODULES 1
static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index 5a4837dcfce3..545b4e77b8aa 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -260,6 +260,65 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
multiplier;
}
+static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
+ struct skl_sst *ctx)
+{
+ struct skl_module_cfg *m_cfg = w->priv;
+ int link_type, dir;
+ u32 ch, s_freq, s_fmt;
+ struct nhlt_specific_cfg *cfg;
+ struct skl *skl = get_skl_ctx(ctx->dev);
+
+ /* check if we already have blob */
+ if (m_cfg->formats_config.caps_size > 0)
+ return 0;
+
+ dev_dbg(ctx->dev, "Applying default cfg blob\n");
+ switch (m_cfg->dev_type) {
+ case SKL_DEVICE_DMIC:
+ link_type = NHLT_LINK_DMIC;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ s_freq = m_cfg->in_fmt[0].s_freq;
+ s_fmt = m_cfg->in_fmt[0].bit_depth;
+ ch = m_cfg->in_fmt[0].channels;
+ break;
+
+ case SKL_DEVICE_I2S:
+ link_type = NHLT_LINK_SSP;
+ if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) {
+ dir = SNDRV_PCM_STREAM_PLAYBACK;
+ s_freq = m_cfg->out_fmt[0].s_freq;
+ s_fmt = m_cfg->out_fmt[0].bit_depth;
+ ch = m_cfg->out_fmt[0].channels;
+ } else {
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ s_freq = m_cfg->in_fmt[0].s_freq;
+ s_fmt = m_cfg->in_fmt[0].bit_depth;
+ ch = m_cfg->in_fmt[0].channels;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* update the blob based on virtual bus_id and default params */
+ cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type,
+ s_fmt, ch, s_freq, dir);
+ if (cfg) {
+ m_cfg->formats_config.caps_size = cfg->size;
+ m_cfg->formats_config.caps = (u32 *) &cfg->caps;
+ } else {
+ dev_err(ctx->dev, "Blob NULL for id %x type %d dirn %d\n",
+ m_cfg->vbus_id, link_type, dir);
+ dev_err(ctx->dev, "PCM: ch %d, freq %d, fmt %d\n",
+ ch, s_freq, s_fmt);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
struct skl_sst *ctx)
{
@@ -433,6 +492,9 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
return ret;
}
+ /* update blob if blob is null for be with default value */
+ skl_tplg_update_be_blob(w, ctx);
+
/*
* apply fix/conversion to module params based on
* FE/BE params
@@ -545,6 +607,66 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
return 0;
}
+/*
+ * Some modules require params to be set after the module is bound to
+ * all pins connected.
+ *
+ * The module provider initializes set_param flag for such modules and we
+ * send params after binding
+ */
+static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
+ struct skl_module_cfg *mcfg, struct skl_sst *ctx)
+{
+ int i, ret;
+ struct skl_module_cfg *mconfig = w->priv;
+ const struct snd_kcontrol_new *k;
+ struct soc_bytes_ext *sb;
+ struct skl_algo_data *bc;
+ struct skl_specific_cfg *sp_cfg;
+
+ /*
+ * check all out/in pins are in bind state.
+ * if so set the module param
+ */
+ for (i = 0; i < mcfg->max_out_queue; i++) {
+ if (mcfg->m_out_pin[i].pin_state != SKL_PIN_BIND_DONE)
+ return 0;
+ }
+
+ for (i = 0; i < mcfg->max_in_queue; i++) {
+ if (mcfg->m_in_pin[i].pin_state != SKL_PIN_BIND_DONE)
+ return 0;
+ }
+
+ if (mconfig->formats_config.caps_size > 0 &&
+ mconfig->formats_config.set_params == SKL_PARAM_BIND) {
+ sp_cfg = &mconfig->formats_config;
+ ret = skl_set_module_params(ctx, sp_cfg->caps,
+ sp_cfg->caps_size,
+ sp_cfg->param_id, mconfig);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ k = &w->kcontrol_news[i];
+ if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+ sb = (void *) k->private_value;
+ bc = (struct skl_algo_data *)sb->dobj.private;
+
+ if (bc->set_params == SKL_PARAM_BIND) {
+ ret = skl_set_module_params(ctx,
+ (u32 *)bc->params, bc->max,
+ bc->param_id, mconfig);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
struct skl *skl,
struct snd_soc_dapm_widget *src_w,
@@ -579,11 +701,19 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
sink = p->sink;
sink_mconfig = sink->priv;
+ if (src_mconfig->m_state == SKL_MODULE_UNINIT ||
+ sink_mconfig->m_state == SKL_MODULE_UNINIT)
+ continue;
+
/* Bind source to sink, mixin is always source */
ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
if (ret)
return ret;
+ /* set module params after bind */
+ skl_tplg_set_module_bind_params(src_w, src_mconfig, ctx);
+ skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx);
+
/* Start sinks pipe first */
if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
if (sink_mconfig->pipe->conn_type !=
@@ -714,6 +844,10 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
if (ret)
return ret;
+ /* set module params after bind */
+ skl_tplg_set_module_bind_params(source, src_mconfig, ctx);
+ skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx);
+
if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
ret = skl_run_pipe(ctx, sink_mconfig->pipe);
}
@@ -1091,6 +1225,66 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
return NULL;
}
+static struct skl_module_cfg *skl_get_mconfig_pb_cpr(
+ struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+ struct skl_module_cfg *mconfig = NULL;
+
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (w->endpoints[SND_SOC_DAPM_DIR_OUT] > 0) {
+ if (p->connect &&
+ (p->sink->id == snd_soc_dapm_aif_out) &&
+ p->source->priv) {
+ mconfig = p->source->priv;
+ return mconfig;
+ }
+ mconfig = skl_get_mconfig_pb_cpr(dai, p->source);
+ if (mconfig)
+ return mconfig;
+ }
+ }
+ return mconfig;
+}
+
+static struct skl_module_cfg *skl_get_mconfig_cap_cpr(
+ struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+ struct skl_module_cfg *mconfig = NULL;
+
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (w->endpoints[SND_SOC_DAPM_DIR_IN] > 0) {
+ if (p->connect &&
+ (p->source->id == snd_soc_dapm_aif_in) &&
+ p->sink->priv) {
+ mconfig = p->sink->priv;
+ return mconfig;
+ }
+ mconfig = skl_get_mconfig_cap_cpr(dai, p->sink);
+ if (mconfig)
+ return mconfig;
+ }
+ }
+ return mconfig;
+}
+
+struct skl_module_cfg *
+skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream)
+{
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mconfig;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ w = dai->playback_widget;
+ mconfig = skl_get_mconfig_pb_cpr(dai, w);
+ } else {
+ w = dai->capture_widget;
+ mconfig = skl_get_mconfig_cap_cpr(dai, w);
+ }
+ return mconfig;
+}
+
static u8 skl_tplg_be_link_type(int dev_type)
{
int ret;
@@ -1464,8 +1658,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
if (!ac->params)
return -ENOMEM;
- if (dfw_ac->params)
- memcpy(ac->params, dfw_ac->params, ac->max);
+ memcpy(ac->params, dfw_ac->params, ac->max);
}
be->dobj.private = ac;
@@ -1523,11 +1716,16 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl *skl = ebus_to_skl(ebus);
- ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+ ret = request_firmware(&fw, skl->tplg_name, bus->dev);
if (ret < 0) {
dev_err(bus->dev, "tplg fw %s load failed with %d\n",
- "dfw_sst.bin", ret);
- return ret;
+ skl->tplg_name, ret);
+ ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+ if (ret < 0) {
+ dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n",
+ "dfw_sst.bin", ret);
+ return ret;
+ }
}
/*
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index 9aa2a2b6598a..de3c401284d9 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -113,6 +113,29 @@ struct skl_cpr_gtw_cfg {
u32 config_data[1];
} __packed;
+struct skl_i2s_config_blob {
+ u32 gateway_attrib;
+ u32 tdm_ts_group[8];
+ u32 ssc0;
+ u32 ssc1;
+ u32 sscto;
+ u32 sspsp;
+ u32 sstsa;
+ u32 ssrsa;
+ u32 ssc2;
+ u32 sspsp2;
+ u32 ssc3;
+ u32 ssioc;
+ u32 mdivc;
+ u32 mdivr;
+} __packed;
+
+struct skl_dma_control {
+ u32 node_id;
+ u32 config_length;
+ u32 config_data[1];
+} __packed;
+
struct skl_cpr_cfg {
struct skl_base_cfg base_cfg;
struct skl_audio_data_format out_fmt;
@@ -313,6 +336,8 @@ static inline struct skl *get_skl_ctx(struct device *dev)
int skl_tplg_be_update_params(struct snd_soc_dai *dai,
struct skl_pipe_params *params);
+int skl_dsp_set_dma_control(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig);
void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
struct skl_pipe_params *params, int stream);
int skl_tplg_init(struct snd_soc_platform *platform,
@@ -345,5 +370,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
u32 param_id, struct skl_module_cfg *mcfg);
+struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai,
+ int stream);
enum skl_bitdepth skl_get_bit_depth(int params);
#endif
diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h
index c9ae010b3cc8..1db88a63ac17 100644
--- a/sound/soc/intel/skylake/skl-tplg-interface.h
+++ b/sound/soc/intel/skylake/skl-tplg-interface.h
@@ -144,7 +144,8 @@ enum module_pin_type {
enum skl_module_param_type {
SKL_PARAM_DEFAULT = 0,
SKL_PARAM_INIT,
- SKL_PARAM_SET
+ SKL_PARAM_SET,
+ SKL_PARAM_BIND
};
struct skl_dfw_module_pin {
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 092705e73db4..ab5e25aaeee3 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -28,6 +28,9 @@
#include <linux/firmware.h>
#include <sound/pcm.h>
#include "../common/sst-acpi.h"
+#include <sound/hda_register.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
#include "skl.h"
#include "skl-sst-dsp.h"
#include "skl-sst-ipc.h"
@@ -243,6 +246,16 @@ static int skl_resume(struct device *dev)
struct hdac_bus *bus = ebus_to_hbus(ebus);
int ret;
+ /* Turned OFF in HDMI codec driver after codec reconfiguration */
+ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
+ ret = snd_hdac_display_power(bus, true);
+ if (ret < 0) {
+ dev_err(bus->dev,
+ "Cannot turn on display power on i915\n");
+ return ret;
+ }
+ }
+
/*
* resume only when we are not in suspend active, otherwise need to
* restore the device
@@ -481,6 +494,27 @@ static int skl_create(struct pci_dev *pci,
return 0;
}
+static int skl_i915_init(struct hdac_bus *bus)
+{
+ int err;
+
+ /*
+ * The HDMI codec is in GPU so we need to ensure that it is powered
+ * up and ready for probe
+ */
+ err = snd_hdac_i915_init(bus);
+ if (err < 0)
+ return err;
+
+ err = snd_hdac_display_power(bus, true);
+ if (err < 0) {
+ dev_err(bus->dev, "Cannot turn on display power on i915\n");
+ return err;
+ }
+
+ return err;
+}
+
static int skl_first_init(struct hdac_ext_bus *ebus)
{
struct skl *skl = ebus_to_skl(ebus);
@@ -543,6 +577,12 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
/* initialize chip */
skl_init_pci(skl);
+ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
+ err = skl_i915_init(bus);
+ if (err < 0)
+ return err;
+ }
+
skl_init_chip(bus, true);
/* codec detection */
@@ -573,11 +613,15 @@ static int skl_probe(struct pci_dev *pci,
if (err < 0)
goto out_free;
+ skl->pci_id = pci->device;
+
skl->nhlt = skl_nhlt_init(bus->dev);
if (skl->nhlt == NULL)
goto out_free;
+ skl_nhlt_update_topology_bin(skl);
+
pci_set_drvdata(skl->pci, ebus);
/* check if dsp is there */
@@ -613,6 +657,14 @@ static int skl_probe(struct pci_dev *pci,
if (err < 0)
goto out_unregister;
+ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
+ err = snd_hdac_display_power(bus, false);
+ if (err < 0) {
+ dev_err(bus->dev, "Cannot turn off display power on i915\n");
+ return err;
+ }
+ }
+
/*configure PM */
pm_runtime_put_noidle(bus->dev);
pm_runtime_allow(bus->dev);
@@ -634,6 +686,31 @@ out_free:
return err;
}
+static void skl_shutdown(struct pci_dev *pci)
+{
+ struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct hdac_stream *s;
+ struct hdac_ext_stream *stream;
+ struct skl *skl;
+
+ if (ebus == NULL)
+ return;
+
+ skl = ebus_to_skl(ebus);
+
+ if (skl->init_failed)
+ return;
+
+ snd_hdac_ext_stop_streams(ebus);
+ list_for_each_entry(s, &bus->stream_list, list) {
+ stream = stream_to_hdac_ext_stream(s);
+ snd_hdac_ext_stream_decouple(ebus, stream, false);
+ }
+
+ snd_hdac_bus_stop_chip(bus);
+}
+
static void skl_remove(struct pci_dev *pci)
{
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
@@ -642,6 +719,9 @@ static void skl_remove(struct pci_dev *pci)
if (skl->tplg)
release_firmware(skl->tplg);
+ if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI))
+ snd_hdac_i915_exit(&ebus->bus);
+
if (pci_dev_run_wake(pci))
pm_runtime_get_noresume(&pci->dev);
pci_dev_put(pci);
@@ -662,11 +742,18 @@ static struct sst_acpi_mach sst_skl_devdata[] = {
{}
};
+static struct sst_acpi_mach sst_bxtp_devdata[] = {
+ { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+};
+
/* PCI IDs */
static const struct pci_device_id skl_ids[] = {
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = (unsigned long)&sst_skl_devdata},
+ /* BXT-P */
+ { PCI_DEVICE(0x8086, 0x5a98),
+ .driver_data = (unsigned long)&sst_bxtp_devdata},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, skl_ids);
@@ -677,6 +764,7 @@ static struct pci_driver skl_driver = {
.id_table = skl_ids,
.probe = skl_probe,
.remove = skl_remove,
+ .shutdown = skl_shutdown,
.driver = {
.pm = &skl_pm,
},
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index 4d18293b5537..39e16fa7a92b 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -73,6 +73,8 @@ struct skl {
struct list_head ppl_list;
const char *fw_name;
+ char tplg_name[64];
+ unsigned short pci_id;
const struct firmware *tplg;
int supend_active;
@@ -88,6 +90,16 @@ struct skl_dma_params {
u8 stream_tag;
};
+struct skl_dsp_ops {
+ int id;
+ struct skl_dsp_loader_ops (*loader_ops)(void);
+ int (*init)(struct device *dev, void __iomem *mmio_base,
+ int irq, const char *fw_name,
+ struct skl_dsp_loader_ops loader_ops,
+ struct skl_sst **skl_sst);
+ void (*cleanup)(struct device *dev, struct skl_sst *ctx);
+};
+
int skl_platform_unregister(struct device *dev);
int skl_platform_register(struct device *dev);
@@ -96,8 +108,9 @@ void skl_nhlt_free(void *addr);
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
+int skl_nhlt_update_topology_bin(struct skl *skl);
int skl_init_dsp(struct skl *skl);
-void skl_free_dsp(struct skl *skl);
+int skl_free_dsp(struct skl *skl);
int skl_suspend_dsp(struct skl *skl);
int skl_resume_dsp(struct skl *skl);
#endif /* __SOUND_SOC_SKL_H */
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 976967675387..f7e789e97fbc 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -17,6 +17,27 @@ config SND_SOC_MT8173_MAX98090
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT8173_RT5650
+ tristate "ASoC Audio driver for MT8173 with RT5650 codec"
+ depends on SND_SOC_MEDIATEK && I2C
+ select SND_SOC_RT5645
+ help
+ This adds ASoC driver for Mediatek MT8173 boards
+ with the RT5650 audio codec.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8173_RT5650_RT5514
+ tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
+ depends on SND_SOC_MEDIATEK && I2C
+ select SND_SOC_RT5645
+ select SND_SOC_RT5514
+ help
+ This adds ASoC driver for Mediatek MT8173 boards
+ with the RT5650 and RT5514 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
config SND_SOC_MT8173_RT5650_RT5676
tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
depends on SND_SOC_MEDIATEK && I2C
@@ -27,4 +48,3 @@ config SND_SOC_MT8173_RT5650_RT5676
with the RT5650 and RT5676 codecs.
Select Y if you have such device.
If unsure select "N".
-
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 75effbec438d..d486860c0a88 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -2,4 +2,6 @@
obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
# Machine support
obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173-rt5650-rt5514.c
new file mode 100644
index 000000000000..58e083642dea
--- /dev/null
+++ b/sound/soc/mediatek/mt8173-rt5650-rt5514.c
@@ -0,0 +1,258 @@
+/*
+ * mt8173-rt5650-rt5514.c -- MT8173 machine driver with RT5650/5514 codecs
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/rt5645.h"
+
+#define MCLK_FOR_CODECS 12288000
+
+static const struct snd_soc_dapm_widget mt8173_rt5650_rt5514_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = {
+ {"Speaker", NULL, "SPOL"},
+ {"Speaker", NULL, "SPOR"},
+ {"Sub DMIC1L", NULL, "Int Mic"},
+ {"Sub DMIC1R", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Headset Mic", NULL, "micbias1"},
+ {"Headset Mic", NULL, "micbias2"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int i, ret;
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+
+ /* pll from mclk 12.288M */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+ params_rate(params) * 512);
+ if (ret)
+ return ret;
+
+ /* sysclk from pll */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_ops mt8173_rt5650_rt5514_ops = {
+ .hw_params = mt8173_rt5650_rt5514_hw_params,
+};
+
+static struct snd_soc_jack mt8173_rt5650_rt5514_jack;
+
+static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
+ int ret;
+
+ rt5645_sel_asrc_clk_src(codec,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &mt8173_rt5650_rt5514_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
+ return ret;
+ }
+
+ return rt5645_set_jack_detect(codec,
+ &mt8173_rt5650_rt5514_jack,
+ &mt8173_rt5650_rt5514_jack,
+ &mt8173_rt5650_rt5514_jack);
+}
+
+static struct snd_soc_dai_link_component mt8173_rt5650_rt5514_codecs[] = {
+ {
+ .dai_name = "rt5645-aif1",
+ },
+ {
+ .dai_name = "rt5514-aif1",
+ },
+};
+
+enum {
+ DAI_LINK_PLAYBACK,
+ DAI_LINK_CAPTURE,
+ DAI_LINK_CODEC_I2S,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = {
+ /* Front End DAI links */
+ [DAI_LINK_PLAYBACK] = {
+ .name = "rt5650_rt5514 Playback",
+ .stream_name = "rt5650_rt5514 Playback",
+ .cpu_dai_name = "DL1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ [DAI_LINK_CAPTURE] = {
+ .name = "rt5650_rt5514 Capture",
+ .stream_name = "rt5650_rt5514 Capture",
+ .cpu_dai_name = "VUL",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ /* Back End DAI links */
+ [DAI_LINK_CODEC_I2S] = {
+ .name = "Codec",
+ .cpu_dai_name = "I2S",
+ .no_pcm = 1,
+ .codecs = mt8173_rt5650_rt5514_codecs,
+ .num_codecs = 2,
+ .init = mt8173_rt5650_rt5514_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &mt8173_rt5650_rt5514_ops,
+ .ignore_pmdown_time = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+static struct snd_soc_codec_conf mt8173_rt5650_rt5514_codec_conf[] = {
+ {
+ .name_prefix = "Sub",
+ },
+};
+
+static struct snd_soc_card mt8173_rt5650_rt5514_card = {
+ .name = "mtk-rt5650-rt5514",
+ .owner = THIS_MODULE,
+ .dai_link = mt8173_rt5650_rt5514_dais,
+ .num_links = ARRAY_SIZE(mt8173_rt5650_rt5514_dais),
+ .codec_conf = mt8173_rt5650_rt5514_codec_conf,
+ .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5514_codec_conf),
+ .controls = mt8173_rt5650_rt5514_controls,
+ .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5514_controls),
+ .dapm_widgets = mt8173_rt5650_rt5514_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5514_widgets),
+ .dapm_routes = mt8173_rt5650_rt5514_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5514_routes),
+};
+
+static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8173_rt5650_rt5514_card;
+ struct device_node *platform_node;
+ int i, ret;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (mt8173_rt5650_rt5514_dais[i].platform_name)
+ continue;
+ mt8173_rt5650_rt5514_dais[i].platform_of_node = platform_node;
+ }
+
+ mt8173_rt5650_rt5514_codecs[0].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
+ if (!mt8173_rt5650_rt5514_codecs[0].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ mt8173_rt5650_rt5514_codecs[1].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
+ if (!mt8173_rt5650_rt5514_codecs[1].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ mt8173_rt5650_rt5514_codec_conf[0].of_node =
+ mt8173_rt5650_rt5514_codecs[1].of_node;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static const struct of_device_id mt8173_rt5650_rt5514_dt_match[] = {
+ { .compatible = "mediatek,mt8173-rt5650-rt5514", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5514_dt_match);
+
+static struct platform_driver mt8173_rt5650_rt5514_driver = {
+ .driver = {
+ .name = "mtk-rt5650-rt5514",
+ .of_match_table = mt8173_rt5650_rt5514_dt_match,
+#ifdef CONFIG_PM
+ .pm = &snd_soc_pm_ops,
+#endif
+ },
+ .probe = mt8173_rt5650_rt5514_dev_probe,
+};
+
+module_platform_driver(mt8173_rt5650_rt5514_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 RT5650 and RT5514 SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-rt5650-rt5514");
+
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
index 50ba538eccb3..5c4c58c69c51 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173-rt5650-rt5676.c
@@ -131,10 +131,17 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
},
};
+enum {
+ DAI_LINK_PLAYBACK,
+ DAI_LINK_CAPTURE,
+ DAI_LINK_CODEC_I2S,
+ DAI_LINK_INTERCODEC
+};
+
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
/* Front End DAI links */
- {
+ [DAI_LINK_PLAYBACK] = {
.name = "rt5650_rt5676 Playback",
.stream_name = "rt5650_rt5676 Playback",
.cpu_dai_name = "DL1",
@@ -144,7 +151,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
.dynamic = 1,
.dpcm_playback = 1,
},
- {
+ [DAI_LINK_CAPTURE] = {
.name = "rt5650_rt5676 Capture",
.stream_name = "rt5650_rt5676 Capture",
.cpu_dai_name = "VUL",
@@ -156,7 +163,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
},
/* Back End DAI links */
- {
+ [DAI_LINK_CODEC_I2S] = {
.name = "Codec",
.cpu_dai_name = "I2S",
.no_pcm = 1,
@@ -170,7 +177,8 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
.dpcm_playback = 1,
.dpcm_capture = 1,
},
- { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
+ /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
+ [DAI_LINK_INTERCODEC] = {
.name = "rt5650_rt5676 intercodec",
.stream_name = "rt5650_rt5676 intercodec",
.cpu_dai_name = "snd-soc-dummy-dai",
@@ -240,7 +248,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
mt8173_rt5650_rt5676_codec_conf[0].of_node =
mt8173_rt5650_rt5676_codecs[1].of_node;
- mt8173_rt5650_rt5676_dais[3].codec_of_node =
+ mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node =
mt8173_rt5650_rt5676_codecs[1].of_node;
card->dev = &pdev->dev;
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c
new file mode 100644
index 000000000000..bb09bb1b7f1c
--- /dev/null
+++ b/sound/soc/mediatek/mt8173-rt5650.c
@@ -0,0 +1,236 @@
+/*
+ * mt8173-rt5650.c -- MT8173 machine driver with RT5650 codecs
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/rt5645.h"
+
+#define MCLK_FOR_CODECS 12288000
+
+static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {
+ {"Speaker", NULL, "SPOL"},
+ {"Speaker", NULL, "SPOR"},
+ {"DMIC L1", NULL, "Int Mic"},
+ {"DMIC R1", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Headset Mic", NULL, "micbias1"},
+ {"Headset Mic", NULL, "micbias2"},
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int i, ret;
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+
+ /* pll from mclk 12.288M */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+ params_rate(params) * 512);
+ if (ret)
+ return ret;
+
+ /* sysclk from pll */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_ops mt8173_rt5650_ops = {
+ .hw_params = mt8173_rt5650_hw_params,
+};
+
+static struct snd_soc_jack mt8173_rt5650_jack;
+
+static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
+ int ret;
+
+ rt5645_sel_asrc_clk_src(codec,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+ /* enable jack detection */
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3,
+ &mt8173_rt5650_jack, NULL, 0);
+ if (ret) {
+ dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
+ return ret;
+ }
+
+ return rt5645_set_jack_detect(codec,
+ &mt8173_rt5650_jack,
+ &mt8173_rt5650_jack,
+ &mt8173_rt5650_jack);
+}
+
+static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
+ {
+ .dai_name = "rt5645-aif1",
+ },
+};
+
+enum {
+ DAI_LINK_PLAYBACK,
+ DAI_LINK_CAPTURE,
+ DAI_LINK_CODEC_I2S,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
+ /* Front End DAI links */
+ [DAI_LINK_PLAYBACK] = {
+ .name = "rt5650 Playback",
+ .stream_name = "rt5650 Playback",
+ .cpu_dai_name = "DL1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ [DAI_LINK_CAPTURE] = {
+ .name = "rt5650 Capture",
+ .stream_name = "rt5650 Capture",
+ .cpu_dai_name = "VUL",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ /* Back End DAI links */
+ [DAI_LINK_CODEC_I2S] = {
+ .name = "Codec",
+ .cpu_dai_name = "I2S",
+ .no_pcm = 1,
+ .codecs = mt8173_rt5650_codecs,
+ .num_codecs = 1,
+ .init = mt8173_rt5650_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &mt8173_rt5650_ops,
+ .ignore_pmdown_time = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+static struct snd_soc_card mt8173_rt5650_card = {
+ .name = "mtk-rt5650",
+ .owner = THIS_MODULE,
+ .dai_link = mt8173_rt5650_dais,
+ .num_links = ARRAY_SIZE(mt8173_rt5650_dais),
+ .controls = mt8173_rt5650_controls,
+ .num_controls = ARRAY_SIZE(mt8173_rt5650_controls),
+ .dapm_widgets = mt8173_rt5650_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets),
+ .dapm_routes = mt8173_rt5650_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes),
+};
+
+static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8173_rt5650_card;
+ struct device_node *platform_node;
+ int i, ret;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < card->num_links; i++) {
+ if (mt8173_rt5650_dais[i].platform_name)
+ continue;
+ mt8173_rt5650_dais[i].platform_of_node = platform_node;
+ }
+
+ mt8173_rt5650_codecs[0].of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
+ if (!mt8173_rt5650_codecs[0].of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+ __func__, ret);
+ return ret;
+}
+
+static const struct of_device_id mt8173_rt5650_dt_match[] = {
+ { .compatible = "mediatek,mt8173-rt5650", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match);
+
+static struct platform_driver mt8173_rt5650_driver = {
+ .driver = {
+ .name = "mtk-rt5650",
+ .of_match_table = mt8173_rt5650_dt_match,
+#ifdef CONFIG_PM
+ .pm = &snd_soc_pm_ops,
+#endif
+ },
+ .probe = mt8173_rt5650_dev_probe,
+};
+
+module_platform_driver(mt8173_rt5650_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-rt5650");
+
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
index 9b1af1a70874..f341f623e887 100644
--- a/sound/soc/mediatek/mtk-afe-common.h
+++ b/sound/soc/mediatek/mtk-afe-common.h
@@ -87,6 +87,7 @@ struct mtk_afe_memif_data {
int irq_en_shift;
int irq_fs_shift;
int irq_clr_shift;
+ int msb_shift;
};
struct mtk_afe_memif {
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mtk-afe-pcm.c
index 08af9f5dc4ab..f1c58a2c12fb 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mtk-afe-pcm.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include "mtk-afe-common.h"
@@ -35,9 +36,11 @@
#define AFE_I2S_CON1 0x0034
#define AFE_I2S_CON2 0x0038
#define AFE_CONN_24BIT 0x006c
+#define AFE_MEMIF_MSB 0x00cc
#define AFE_CONN1 0x0024
#define AFE_CONN2 0x0028
+#define AFE_CONN3 0x002c
#define AFE_CONN7 0x0460
#define AFE_CONN8 0x0464
#define AFE_HDMI_CONN0 0x0390
@@ -61,6 +64,7 @@
#define AFE_HDMI_OUT_CUR 0x0378
#define AFE_HDMI_OUT_END 0x037c
+#define AFE_ADDA_TOP_CON0 0x0120
#define AFE_ADDA2_TOP_CON0 0x0600
#define AFE_HDMI_OUT_CON0 0x0370
@@ -257,6 +261,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
return -EINVAL;
/* from external ADC */
+ regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1);
regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1);
/* set input */
@@ -281,20 +286,13 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
regmap_read(afe->regmap, AFE_I2S_CON2, &val);
if (!!(val & AFE_I2S_CON2_EN) == enable)
- return; /* must skip soft reset */
-
- /* I2S soft reset begin */
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4);
+ return;
/* input */
regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable);
/* output */
regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
-
- /* I2S soft reset end */
- udelay(1);
- regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0);
}
static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
@@ -363,6 +361,7 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
return 0;
mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
+ mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
return 0;
@@ -382,6 +381,7 @@ static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
+ mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL);
}
static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
@@ -395,6 +395,9 @@ static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
mtk_afe_dais_set_clks(afe,
afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
NULL, 0);
+ mtk_afe_dais_set_clks(afe,
+ afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
+ NULL, 0);
/* config I2S */
ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
if (ret)
@@ -592,6 +595,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ int msb_at_bit33 = 0;
int ret;
dev_dbg(afe->dev,
@@ -603,7 +607,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- memif->phys_buf_addr = substream->runtime->dma_addr;
+ msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+ memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
memif->buffer_size = substream->runtime->dma_bytes;
/* start */
@@ -614,6 +619,11 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
memif->phys_buf_addr + memif->buffer_size - 1);
+ /* set MSB to 33-bit */
+ regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
+ 1 << memif->data->msb_shift,
+ msb_at_bit33 << memif->data->msb_shift);
+
/* set channel */
if (memif->data->mono_shift >= 0) {
unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
@@ -894,15 +904,19 @@ static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
};
static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
};
static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
};
static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
/* inter-connections */
+ SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -925,12 +939,16 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
{"I2S Playback", NULL, "O04"},
{"VUL", NULL, "O09"},
{"VUL", NULL, "O10"},
+ {"I03", NULL, "I2S Capture"},
+ {"I04", NULL, "I2S Capture"},
{"I17", NULL, "I2S Capture"},
{"I18", NULL, "I2S Capture"},
{ "O03", "I05 Switch", "I05" },
{ "O04", "I06 Switch", "I06" },
{ "O09", "I17 Switch", "I17" },
+ { "O09", "I03 Switch", "I03" },
{ "O10", "I18 Switch", "I18" },
+ { "O10", "I04 Switch", "I04" },
};
static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
@@ -978,6 +996,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
.irq_en_shift = 0,
.irq_fs_shift = 4,
.irq_clr_shift = 0,
+ .msb_shift = 0,
}, {
.name = "DL2",
.id = MTK_AFE_MEMIF_DL2,
@@ -991,6 +1010,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
.irq_en_shift = 2,
.irq_fs_shift = 16,
.irq_clr_shift = 2,
+ .msb_shift = 1,
}, {
.name = "VUL",
.id = MTK_AFE_MEMIF_VUL,
@@ -1004,6 +1024,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
.irq_en_shift = 1,
.irq_fs_shift = 8,
.irq_clr_shift = 1,
+ .msb_shift = 6,
}, {
.name = "DAI",
.id = MTK_AFE_MEMIF_DAI,
@@ -1017,6 +1038,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
.irq_en_shift = 3,
.irq_fs_shift = 20,
.irq_clr_shift = 3,
+ .msb_shift = 5,
}, {
.name = "AWB",
.id = MTK_AFE_MEMIF_AWB,
@@ -1030,6 +1052,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
.irq_en_shift = 14,
.irq_fs_shift = 24,
.irq_clr_shift = 6,
+ .msb_shift = 3,
}, {
.name = "MOD_DAI",
.id = MTK_AFE_MEMIF_MOD_DAI,
@@ -1043,6 +1066,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
.irq_en_shift = 3,
.irq_fs_shift = 20,
.irq_clr_shift = 3,
+ .msb_shift = 4,
}, {
.name = "HDMI",
.id = MTK_AFE_MEMIF_HDMI,
@@ -1056,6 +1080,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
.irq_en_shift = 12,
.irq_fs_shift = -1,
.irq_clr_shift = 4,
+ .msb_shift = 8,
},
};
@@ -1189,6 +1214,10 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
struct mtk_afe *afe;
struct resource *res;
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
+ if (ret)
+ return ret;
+
afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
if (!afe)
return -ENOMEM;
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index a6c7b8d87cd2..13631003cb7c 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -418,7 +418,7 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
}
stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_BUSY) {
+ if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) {
dev_err(cpu_dai->dev, "error: busy\n");
return -EBUSY;
}
diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c
index f83cc2bc0fc4..64425d352962 100644
--- a/sound/soc/omap/omap-hdmi-audio.c
+++ b/sound/soc/omap/omap-hdmi-audio.c
@@ -345,6 +345,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
dai_drv = &omap4_hdmi_dai;
break;
case OMAPDSS_VER_OMAP5:
+ case OMAPDSS_VER_DRA7xx:
dai_drv = &omap5_hdmi_dai;
break;
default:
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index 416ea646c3b1..ec522e94b0e2 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -52,7 +52,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int freq_out, sspa_mclk, sysclk;
- int sspa_div;
if (params_rate(params) > 11025) {
freq_out = params_rate(params) * 512;
@@ -63,7 +62,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
sysclk = params_rate(params) * 512;
sspa_mclk = params_rate(params) * 64;
}
- sspa_div = freq_out / sspa_mclk;
snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 3cc252e55468..8ec9a074b38b 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -11,21 +11,24 @@ config SND_SOC_LPASS_CPU
config SND_SOC_LPASS_PLATFORM
tristate
+ depends on HAS_DMA
select REGMAP_MMIO
config SND_SOC_LPASS_IPQ806X
tristate
+ depends on HAS_DMA
select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM
config SND_SOC_LPASS_APQ8016
tristate
+ depends on HAS_DMA
select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM
config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards"
- depends on SND_SOC_QCOM
+ depends on SND_SOC_QCOM && HAS_DMA
select SND_SOC_LPASS_IPQ806X
select SND_SOC_MAX98357A
help
@@ -34,7 +37,7 @@ config SND_SOC_STORM
config SND_SOC_APQ8016_SBC
tristate "SoC Audio support for APQ8016 SBC platforms"
- depends on SND_SOC_QCOM
+ depends on SND_SOC_QCOM && HAS_DMA
select SND_SOC_LPASS_APQ8016
help
Support for Qualcomm Technologies LPASS audio block in
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index 1efdf0088ecd..1289543c8fb2 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -30,6 +30,7 @@ struct apq8016_sbc_data {
struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
};
+#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21)
#define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17)
#define MIC_CTRL_TLMM_SCLK_EN BIT(1)
#define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16))
@@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
MIC_CTRL_TLMM_SCLK_EN,
pdata->mic_iomux);
break;
+ case MI2S_TERTIARY:
+ writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL |
+ MIC_CTRL_TLMM_SCLK_EN,
+ pdata->mic_iomux);
+
+ break;
default:
dev_err(card->dev, "unsupported cpu dai configuration\n");
@@ -126,9 +133,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
}
link->platform_of_node = link->cpu_of_node;
- /* For now we only support playback */
- link->playback_only = true;
-
ret = of_property_read_string(np, "link-name", &link->name);
if (ret) {
dev_err(card->dev, "error getting codec dai_link name\n");
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
index 94efc01020c4..3eef0c37ba50 100644
--- a/sound/soc/qcom/lpass-apq8016.c
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -133,23 +133,36 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
},
};
-static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
+ int direction)
{
struct lpass_variant *v = drvdata->variant;
- int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+ int chan = 0;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
v->rdma_channels);
- if (chan >= v->rdma_channels)
- return -EBUSY;
+ if (chan >= v->rdma_channels)
+ return -EBUSY;
+ } else {
+ chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+ v->wrdma_channel_start +
+ v->wrdma_channels,
+ v->wrdma_channel_start);
+
+ if (chan >= v->wrdma_channel_start + v->wrdma_channels)
+ return -EBUSY;
+ }
- set_bit(chan, &drvdata->rdma_ch_bit_map);
+ set_bit(chan, &drvdata->dma_ch_bit_map);
return chan;
}
static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
{
- clear_bit(chan, &drvdata->rdma_ch_bit_map);
+ clear_bit(chan, &drvdata->dma_ch_bit_map);
return 0;
}
@@ -212,7 +225,11 @@ static struct lpass_variant apq8016_data = {
.rdma_reg_base = 0x8400,
.rdma_reg_stride = 0x1000,
.rdma_channels = 2,
- .rdmactl_audif_start = 1,
+ .dmactl_audif_start = 1,
+ .wrdma_reg_base = 0xB000,
+ .wrdma_reg_stride = 0x1000,
+ .wrdma_channel_start = 5,
+ .wrdma_channels = 2,
.dai_driver = apq8016_lpass_cpu_dai_driver,
.num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
.init = apq8016_lpass_init,
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index e5101e0d2d37..3cde9fb977fa 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- switch (channels) {
- case 1:
- regval |= LPAIF_I2SCTL_SPKMODE_SD0;
- regval |= LPAIF_I2SCTL_SPKMONO_MONO;
- break;
- case 2:
- regval |= LPAIF_I2SCTL_SPKMODE_SD0;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
- break;
- case 4:
- regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
- break;
- case 6:
- regval |= LPAIF_I2SCTL_SPKMODE_6CH;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
- break;
- case 8:
- regval |= LPAIF_I2SCTL_SPKMODE_8CH;
- regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
- break;
- default:
- dev_err(dai->dev, "%s() invalid channels given: %u\n",
- __func__, channels);
- return -EINVAL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (channels) {
+ case 1:
+ regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+ regval |= LPAIF_I2SCTL_SPKMONO_MONO;
+ break;
+ case 2:
+ regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+ regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ break;
+ case 4:
+ regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
+ regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ break;
+ case 6:
+ regval |= LPAIF_I2SCTL_SPKMODE_6CH;
+ regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ break;
+ case 8:
+ regval |= LPAIF_I2SCTL_SPKMODE_8CH;
+ regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+ break;
+ default:
+ dev_err(dai->dev, "%s() invalid channels given: %u\n",
+ __func__, channels);
+ return -EINVAL;
+ }
+ } else {
+ switch (channels) {
+ case 1:
+ regval |= LPAIF_I2SCTL_MICMODE_SD0;
+ regval |= LPAIF_I2SCTL_MICMONO_MONO;
+ break;
+ case 2:
+ regval |= LPAIF_I2SCTL_MICMODE_SD0;
+ regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+ break;
+ case 4:
+ regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
+ regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+ break;
+ case 6:
+ regval |= LPAIF_I2SCTL_MICMODE_6CH;
+ regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+ break;
+ case 8:
+ regval |= LPAIF_I2SCTL_MICMODE_8CH;
+ regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+ break;
+ default:
+ dev_err(dai->dev, "%s() invalid channels given: %u\n",
+ __func__, channels);
+ return -EINVAL;
+ }
}
ret = regmap_write(drvdata->lpaif_map,
@@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret;
+ unsigned int val, mask;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val = LPAIF_I2SCTL_SPKEN_ENABLE;
+ mask = LPAIF_I2SCTL_SPKEN_MASK;
+ } else {
+ val = LPAIF_I2SCTL_MICEN_ENABLE;
+ mask = LPAIF_I2SCTL_MICEN_MASK;
+ }
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
- LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
+ mask, val);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
@@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret = -EINVAL;
+ unsigned int val, mask;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val = LPAIF_I2SCTL_SPKEN_ENABLE;
+ mask = LPAIF_I2SCTL_SPKEN_MASK;
+ } else {
+ val = LPAIF_I2SCTL_MICEN_ENABLE;
+ mask = LPAIF_I2SCTL_MICEN_MASK;
+ }
+
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant,
dai->driver->id),
- LPAIF_I2SCTL_SPKEN_MASK,
- LPAIF_I2SCTL_SPKEN_ENABLE);
+ mask, val);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
@@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ val = LPAIF_I2SCTL_SPKEN_DISABLE;
+ mask = LPAIF_I2SCTL_SPKEN_MASK;
+ } else {
+ val = LPAIF_I2SCTL_MICEN_DISABLE;
+ mask = LPAIF_I2SCTL_MICEN_MASK;
+ }
+
ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant,
dai->driver->id),
- LPAIF_I2SCTL_SPKEN_MASK,
- LPAIF_I2SCTL_SPKEN_DISABLE);
+ mask, val);
if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret);
@@ -294,6 +347,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
return true;
}
+ for (i = 0; i < v->wrdma_channels; ++i) {
+ if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+ return true;
+ }
+
return false;
}
@@ -327,6 +391,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
return true;
}
+ for (i = 0; i < v->wrdma_channels; ++i) {
+ if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+ return true;
+ if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+ return true;
+ }
+
return false;
}
@@ -344,6 +421,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
if (reg == LPAIF_RDMACURR_REG(v, i))
return true;
+ for (i = 0; i < v->wrdma_channels; ++i)
+ if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+ return true;
+
return false;
}
@@ -398,8 +479,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR((void const __force *)drvdata->lpaif);
}
- lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
- variant->rdma_channels);
+ lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
+ variant->wrdma_channels +
+ variant->wrdma_channel_start);
drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
&lpass_cpu_regmap_config);
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
index 7a4167952711..608c1a92af8a 100644
--- a/sound/soc/qcom/lpass-ipq806x.c
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -63,9 +63,12 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
.ops = &asoc_qcom_lpass_cpu_dai_ops,
};
-static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir)
{
- return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+ else /* Capture currently not implemented */
+ return -EINVAL;
}
static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
@@ -83,6 +86,10 @@ static struct lpass_variant ipq806x_data = {
.rdma_reg_base = 0x6000,
.rdma_reg_stride = 0x1000,
.rdma_channels = 4,
+ .wrdma_reg_base = 0xB000,
+ .wrdma_reg_stride = 0x1000,
+ .wrdma_channel_start = 5,
+ .wrdma_channels = 4,
.dai_driver = &ipq806x_lpass_cpu_dai_driver,
.num_dai = 1,
.alloc_dma_channel = ipq806x_lpass_alloc_dma_channel,
diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h
index 95e22f131052..2240bc68e6ec 100644
--- a/sound/soc/qcom/lpass-lpaif-reg.h
+++ b/sound/soc/qcom/lpass-lpaif-reg.h
@@ -47,6 +47,28 @@
#define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT)
#define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT)
+#define LPAIF_I2SCTL_MICEN_MASK GENMASK(8, 8)
+#define LPAIF_I2SCTL_MICEN_SHIFT 8
+#define LPAIF_I2SCTL_MICEN_DISABLE (0 << LPAIF_I2SCTL_MICEN_SHIFT)
+#define LPAIF_I2SCTL_MICEN_ENABLE (1 << LPAIF_I2SCTL_MICEN_SHIFT)
+
+#define LPAIF_I2SCTL_MICMODE_MASK GENMASK(7, 4)
+#define LPAIF_I2SCTL_MICMODE_SHIFT 4
+#define LPAIF_I2SCTL_MICMODE_NONE (0 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD0 (1 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD1 (2 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD2 (3 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD3 (4 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_QUAD01 (5 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_QUAD23 (6 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_6CH (7 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_8CH (8 << LPAIF_I2SCTL_MICMODE_SHIFT)
+
+#define LPAIF_I2SCTL_MIMONO_MASK GENMASK(3, 3)
+#define LPAIF_I2SCTL_MICMONO_SHIFT 3
+#define LPAIF_I2SCTL_MICMONO_STEREO (0 << LPAIF_I2SCTL_MICMONO_SHIFT)
+#define LPAIF_I2SCTL_MICMONO_MONO (1 << LPAIF_I2SCTL_MICMONO_SHIFT)
+
#define LPAIF_I2SCTL_WSSRC_MASK 0x0004
#define LPAIF_I2SCTL_WSSRC_SHIFT 2
#define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT)
@@ -90,37 +112,65 @@
#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
-#define LPAIF_RDMACTL_BURSTEN_MASK 0x800
-#define LPAIF_RDMACTL_BURSTEN_SHIFT 11
-#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-
-#define LPAIF_RDMACTL_WPSCNT_MASK 0x700
-#define LPAIF_RDMACTL_WPSCNT_SHIFT 8
-#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-
-#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0
-#define LPAIF_RDMACTL_AUDINTF_SHIFT 4
-
-#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E
-#define LPAIF_RDMACTL_FIFOWM_SHIFT 1
-#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-
-#define LPAIF_RDMACTL_ENABLE_MASK 0x1
-#define LPAIF_RDMACTL_ENABLE_SHIFT 0
-#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
-#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
-
+#define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \
+ (v->wrdma_reg_base + (addr) + \
+ v->wrdma_reg_stride * (chan - v->wrdma_channel_start))
+
+#define LPAIF_WRDMACTL_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_WRDMABASE_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan))
+#define LPAIF_WRDMABUFF_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_WRDMACURR_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan))
+#define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
+#define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
+
+#define __LPAIF_DMA_REG(v, chan, dir, reg) \
+ (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
+ LPAIF_RDMA##reg##_REG(v, chan) : \
+ LPAIF_WRDMA##reg##_REG(v, chan)
+
+#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL)
+#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE)
+#define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF)
+#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR)
+#define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER)
+#define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT)
+
+#define LPAIF_DMACTL_BURSTEN_MASK 0x800
+#define LPAIF_DMACTL_BURSTEN_SHIFT 11
+#define LPAIF_DMACTL_BURSTEN_SINGLE (0 << LPAIF_DMACTL_BURSTEN_SHIFT)
+#define LPAIF_DMACTL_BURSTEN_INCR4 (1 << LPAIF_DMACTL_BURSTEN_SHIFT)
+
+#define LPAIF_DMACTL_WPSCNT_MASK 0x700
+#define LPAIF_DMACTL_WPSCNT_SHIFT 8
+#define LPAIF_DMACTL_WPSCNT_ONE (0 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_TWO (1 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_THREE (2 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_FOUR (3 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_SIX (5 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_EIGHT (7 << LPAIF_DMACTL_WPSCNT_SHIFT)
+
+#define LPAIF_DMACTL_AUDINTF_MASK 0x0F0
+#define LPAIF_DMACTL_AUDINTF_SHIFT 4
+#define LPAIF_DMACTL_AUDINTF(id) (id << LPAIF_DMACTL_AUDINTF_SHIFT)
+
+#define LPAIF_DMACTL_FIFOWM_MASK 0x00E
+#define LPAIF_DMACTL_FIFOWM_SHIFT 1
+#define LPAIF_DMACTL_FIFOWM_1 (0 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_2 (1 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_3 (2 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_4 (3 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_5 (4 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_6 (5 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_7 (6 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_8 (7 << LPAIF_DMACTL_FIFOWM_SHIFT)
+
+#define LPAIF_DMACTL_ENABLE_MASK 0x1
+#define LPAIF_DMACTL_ENABLE_SHIFT 0
+#define LPAIF_DMACTL_ENABLE_OFF (0 << LPAIF_DMACTL_ENABLE_SHIFT)
+#define LPAIF_DMACTL_ENABLE_ON (1 << LPAIF_DMACTL_ENABLE_SHIFT)
+
+#define LPAIF_DMACTL_DYNCLK_MASK BIT(12)
+#define LPAIF_DMACTL_DYNCLK_SHIFT 12
+#define LPAIF_DMACTL_DYNCLK_OFF (0 << LPAIF_DMACTL_DYNCLK_SHIFT)
+#define LPAIF_DMACTL_DYNCLK_ON (1 << LPAIF_DMACTL_DYNCLK_SHIFT)
#endif /* __LPASS_LPAIF_REG_H__ */
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 4aeb8e1a7160..6e8665430bd5 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -26,6 +26,7 @@
struct lpass_pcm_data {
int rdma_ch;
+ int wrdma_ch;
int i2s_port;
};
@@ -90,8 +91,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params);
unsigned int regval;
+ int ch, dir = substream->stream;
int bitwidth;
- int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
+ int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ ch = pcm_data->rdma_ch;
+ else
+ ch = pcm_data->wrdma_ch;
bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) {
@@ -100,25 +107,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
return bitwidth;
}
- regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
- LPAIF_RDMACTL_AUDINTF(rdma_port) |
- LPAIF_RDMACTL_FIFOWM_8;
+ regval = LPAIF_DMACTL_BURSTEN_INCR4 |
+ LPAIF_DMACTL_AUDINTF(dma_port) |
+ LPAIF_DMACTL_FIFOWM_8;
switch (bitwidth) {
case 16:
switch (channels) {
case 1:
case 2:
- regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+ regval |= LPAIF_DMACTL_WPSCNT_ONE;
break;
case 4:
- regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+ regval |= LPAIF_DMACTL_WPSCNT_TWO;
break;
case 6:
- regval |= LPAIF_RDMACTL_WPSCNT_THREE;
+ regval |= LPAIF_DMACTL_WPSCNT_THREE;
break;
case 8:
- regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+ regval |= LPAIF_DMACTL_WPSCNT_FOUR;
break;
default:
dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
@@ -130,19 +137,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
case 32:
switch (channels) {
case 1:
- regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+ regval |= LPAIF_DMACTL_WPSCNT_ONE;
break;
case 2:
- regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+ regval |= LPAIF_DMACTL_WPSCNT_TWO;
break;
case 4:
- regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+ regval |= LPAIF_DMACTL_WPSCNT_FOUR;
break;
case 6:
- regval |= LPAIF_RDMACTL_WPSCNT_SIX;
+ regval |= LPAIF_DMACTL_WPSCNT_SIX;
break;
case 8:
- regval |= LPAIF_RDMACTL_WPSCNT_EIGHT;
+ regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
break;
default:
dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
@@ -157,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
}
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
+ LPAIF_DMACTL_REG(v, ch, dir), regval);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@@ -174,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
+ unsigned int reg;
int ret;
- ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch);
+ else
+ reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch);
+
+ ret = regmap_write(drvdata->lpaif_map, reg, 0);
if (ret)
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@@ -193,10 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
- int ret, ch = pcm_data->rdma_ch;
+ int ret, ch, dir = substream->stream;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ ch = pcm_data->rdma_ch;
+ else
+ ch = pcm_data->wrdma_ch;
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMABASE_REG(v, ch),
+ LPAIF_DMABASE_REG(v, ch, dir),
runtime->dma_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@@ -205,7 +222,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMABUFF_REG(v, ch),
+ LPAIF_DMABUFF_REG(v, ch, dir),
(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@@ -214,7 +231,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_write(drvdata->lpaif_map,
- LPAIF_RDMAPER_REG(v, ch),
+ LPAIF_DMAPER_REG(v, ch, dir),
(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@@ -223,8 +240,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
}
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, ch),
- LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
+ LPAIF_DMACTL_REG(v, ch, dir),
+ LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@@ -242,7 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
- int ret, ch = pcm_data->rdma_ch;
+ int ret, ch, dir = substream->stream;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ ch = pcm_data->rdma_ch;
+ else
+ ch = pcm_data->wrdma_ch;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -269,9 +291,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
}
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, ch),
- LPAIF_RDMACTL_ENABLE_MASK,
- LPAIF_RDMACTL_ENABLE_ON);
+ LPAIF_DMACTL_REG(v, ch, dir),
+ LPAIF_DMACTL_ENABLE_MASK,
+ LPAIF_DMACTL_ENABLE_ON);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@@ -282,9 +304,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(drvdata->lpaif_map,
- LPAIF_RDMACTL_REG(v, ch),
- LPAIF_RDMACTL_ENABLE_MASK,
- LPAIF_RDMACTL_ENABLE_OFF);
+ LPAIF_DMACTL_REG(v, ch, dir),
+ LPAIF_DMACTL_ENABLE_MASK,
+ LPAIF_DMACTL_ENABLE_OFF);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
@@ -314,10 +336,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
unsigned int base_addr, curr_addr;
- int ret, ch = pcm_data->rdma_ch;
+ int ret, ch, dir = substream->stream;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ ch = pcm_data->rdma_ch;
+ else
+ ch = pcm_data->wrdma_ch;
ret = regmap_read(drvdata->lpaif_map,
- LPAIF_RDMABASE_REG(v, ch), &base_addr);
+ LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
__func__, ret);
@@ -325,7 +352,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
}
ret = regmap_read(drvdata->lpaif_map,
- LPAIF_RDMACURR_REG(v, ch), &curr_addr);
+ LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
__func__, ret);
@@ -439,101 +466,124 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
- struct snd_soc_pcm_runtime *rt)
-{
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = rt->platform->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr,
- GFP_KERNEL);
- if (!buf->area) {
- dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n",
- __func__);
- return -ENOMEM;
- }
- buf->bytes = size;
-
- return 0;
-}
-
-static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
- struct snd_soc_pcm_runtime *rt)
-{
- struct snd_dma_buffer *buf = &substream->dma_buffer;
-
- if (buf->area) {
- dma_free_coherent(rt->dev, buf->bytes, buf->area,
- buf->addr);
- }
- buf->area = NULL;
-}
-
static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
{
struct snd_pcm *pcm = soc_runtime->pcm;
- struct snd_pcm_substream *substream =
- pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ struct snd_pcm_substream *psubstream, *csubstream;
struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
int ret;
struct lpass_pcm_data *data;
+ size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- if (v->alloc_dma_channel)
- data->rdma_ch = v->alloc_dma_channel(drvdata);
+ data->i2s_port = cpu_dai->driver->id;
+ snd_soc_pcm_set_drvdata(soc_runtime, data);
- if (IS_ERR_VALUE(data->rdma_ch))
- return data->rdma_ch;
+ psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (psubstream) {
+ if (v->alloc_dma_channel)
+ data->rdma_ch = v->alloc_dma_channel(drvdata,
+ SNDRV_PCM_STREAM_PLAYBACK);
- drvdata->substream[data->rdma_ch] = substream;
- data->i2s_port = cpu_dai->driver->id;
+ if (IS_ERR_VALUE(data->rdma_ch))
+ return data->rdma_ch;
- snd_soc_pcm_set_drvdata(soc_runtime, data);
+ drvdata->substream[data->rdma_ch] = psubstream;
- ret = lpass_platform_alloc_buffer(substream, soc_runtime);
- if (ret)
- return ret;
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ soc_runtime->platform->dev,
+ size, &psubstream->dma_buffer);
+ if (ret)
+ goto playback_alloc_err;
- ret = regmap_write(drvdata->lpaif_map,
+ ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
- if (ret) {
- dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "%s() error writing to rdmactl reg: %d\n",
+ __func__, ret);
+ goto capture_alloc_err;
+ }
+ }
+
+ csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (csubstream) {
+ if (v->alloc_dma_channel)
+ data->wrdma_ch = v->alloc_dma_channel(drvdata,
+ SNDRV_PCM_STREAM_CAPTURE);
+
+ if (IS_ERR_VALUE(data->wrdma_ch))
+ goto capture_alloc_err;
+
+ drvdata->substream[data->wrdma_ch] = csubstream;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+ soc_runtime->platform->dev,
+ size, &csubstream->dma_buffer);
+ if (ret)
+ goto capture_alloc_err;
+
+ ret = regmap_write(drvdata->lpaif_map,
+ LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0);
+ if (ret) {
+ dev_err(soc_runtime->dev,
+ "%s() error writing to wrdmactl reg: %d\n",
__func__, ret);
- goto err_buf;
+ goto capture_reg_err;
+ }
}
return 0;
-err_buf:
- lpass_platform_free_buffer(substream, soc_runtime);
+capture_reg_err:
+ if (csubstream)
+ snd_dma_free_pages(&csubstream->dma_buffer);
+
+capture_alloc_err:
+ if (psubstream)
+ snd_dma_free_pages(&psubstream->dma_buffer);
+
+ playback_alloc_err:
+ dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
+
return ret;
}
static void lpass_platform_pcm_free(struct snd_pcm *pcm)
{
- struct snd_pcm_substream *substream =
- pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct lpass_data *drvdata =
- snd_soc_platform_get_drvdata(soc_runtime->platform);
- struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
- struct lpass_variant *v = drvdata->variant;
-
- drvdata->substream[data->rdma_ch] = NULL;
-
- if (v->free_dma_channel)
- v->free_dma_channel(drvdata, data->rdma_ch);
-
- lpass_platform_free_buffer(substream, soc_runtime);
+ struct snd_soc_pcm_runtime *rt;
+ struct lpass_data *drvdata;
+ struct lpass_pcm_data *data;
+ struct lpass_variant *v;
+ struct snd_pcm_substream *substream;
+ int ch, i;
+
+ for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
+ substream = pcm->streams[i].substream;
+ if (substream) {
+ rt = substream->private_data;
+ data = snd_soc_pcm_get_drvdata(rt);
+ drvdata = snd_soc_platform_get_drvdata(rt->platform);
+
+ ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ? data->rdma_ch
+ : data->wrdma_ch;
+ v = drvdata->variant;
+ drvdata->substream[ch] = NULL;
+ if (v->free_dma_channel)
+ v->free_dma_channel(drvdata, ch);
+
+ snd_dma_free_pages(&substream->dma_buffer);
+ substream->dma_buffer.area = NULL;
+ substream->dma_buffer.addr = 0;
+ }
+ }
}
static struct snd_soc_platform_driver lpass_platform_driver = {
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 0b63e2e5bcc9..30714ad1e138 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -50,7 +50,7 @@ struct lpass_data {
struct lpass_variant *variant;
/* bit map to keep track of static channel allocations */
- unsigned long rdma_ch_bit_map;
+ unsigned long dma_ch_bit_map;
/* used it for handling interrupt per dma channel */
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
@@ -71,16 +71,20 @@ struct lpass_variant {
u32 rdma_reg_base;
u32 rdma_reg_stride;
u32 rdma_channels;
+ u32 wrdma_reg_base;
+ u32 wrdma_reg_stride;
+ u32 wrdma_channels;
/**
* on SOCs like APQ8016 the channel control bits start
* at different offset to ipq806x
**/
- u32 rdmactl_audif_start;
+ u32 dmactl_audif_start;
+ u32 wrdma_channel_start;
/* SOC specific intialization like clocks */
int (*init)(struct platform_device *pdev);
int (*exit)(struct platform_device *pdev);
- int (*alloc_dma_channel)(struct lpass_data *data);
+ int (*alloc_dma_channel)(struct lpass_data *data, int direction);
int (*free_dma_channel)(struct lpass_data *data, int ch);
/* SOC specific dais */
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 6561c4cc2edd..2f8e20416bd3 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -440,11 +440,21 @@ static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
}
}
+static const struct reg_default rockchip_i2s_reg_defaults[] = {
+ {0x00, 0x0000000f},
+ {0x04, 0x0000000f},
+ {0x08, 0x00071f1f},
+ {0x10, 0x001f0000},
+ {0x14, 0x01f00000},
+};
+
static const struct regmap_config rockchip_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = I2S_RXDR,
+ .reg_defaults = rockchip_i2s_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults),
.writeable_reg = rockchip_i2s_wr_reg,
.readable_reg = rockchip_i2s_rd_reg,
.volatile_reg = rockchip_i2s_volatile_reg,
@@ -575,6 +585,9 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
static const struct of_device_id rockchip_i2s_match[] = {
{ .compatible = "rockchip,rk3066-i2s", },
+ { .compatible = "rockchip,rk3188-i2s", },
+ { .compatible = "rockchip,rk3288-i2s", },
+ { .compatible = "rockchip,rk3399-i2s", },
{},
};
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 5a806da89f42..100781e37848 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -28,6 +28,7 @@ enum rk_spdif_type {
RK_SPDIF_RK3066,
RK_SPDIF_RK3188,
RK_SPDIF_RK3288,
+ RK_SPDIF_RK3366,
};
#define RK3288_GRF_SOC_CON2 0x24c
@@ -45,16 +46,22 @@ struct rk_spdif_dev {
static const struct of_device_id rk_spdif_match[] = {
{ .compatible = "rockchip,rk3066-spdif",
- .data = (void *) RK_SPDIF_RK3066 },
+ .data = (void *)RK_SPDIF_RK3066 },
{ .compatible = "rockchip,rk3188-spdif",
- .data = (void *) RK_SPDIF_RK3188 },
+ .data = (void *)RK_SPDIF_RK3188 },
{ .compatible = "rockchip,rk3288-spdif",
- .data = (void *) RK_SPDIF_RK3288 },
+ .data = (void *)RK_SPDIF_RK3288 },
+ { .compatible = "rockchip,rk3366-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3368-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
+ { .compatible = "rockchip,rk3399-spdif",
+ .data = (void *)RK_SPDIF_RK3366 },
{},
};
MODULE_DEVICE_TABLE(of, rk_spdif_match);
-static int rk_spdif_runtime_suspend(struct device *dev)
+static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
@@ -64,7 +71,7 @@ static int rk_spdif_runtime_suspend(struct device *dev)
return 0;
}
-static int rk_spdif_runtime_resume(struct device *dev)
+static int __maybe_unused rk_spdif_runtime_resume(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
int ret;
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index df65c5b494b1..b6ab3fc5789e 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -709,7 +709,7 @@ static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
#endif
int s3c_i2sv2_register_component(struct device *dev, int id,
- struct snd_soc_component_driver *cmp_drv,
+ const struct snd_soc_component_driver *cmp_drv,
struct snd_soc_dai_driver *dai_drv)
{
struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h
index 90abab364b49..d0684145ed1f 100644
--- a/sound/soc/samsung/s3c-i2s-v2.h
+++ b/sound/soc/samsung/s3c-i2s-v2.h
@@ -101,7 +101,7 @@ extern int s3c_i2sv2_probe(struct snd_soc_dai *dai,
* soc core.
*/
extern int s3c_i2sv2_register_component(struct device *dev, int id,
- struct snd_soc_component_driver *cmp_drv,
+ const struct snd_soc_component_driver *cmp_drv,
struct snd_soc_dai_driver *dai_drv);
#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 6d3ef366d536..606399de684d 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
return (0x6 + ws) << 8;
}
+static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ unsigned int target_rate,
+ unsigned int *target_val,
+ unsigned int *target_en)
+{
+ struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ int idx, sel, div, step;
+ unsigned int val, en;
+ unsigned int min, diff;
+ unsigned int sel_rate[] = {
+ clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
+ clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
+ clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
+ adg->rbga_rate_for_441khz, /* 0011: RBGA */
+ adg->rbgb_rate_for_48khz, /* 0100: RBGB */
+ };
+
+ min = ~0;
+ val = 0;
+ en = 0;
+ for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+ idx = 0;
+ step = 2;
+
+ if (!sel_rate[sel])
+ continue;
+
+ for (div = 2; div <= 98304; div += step) {
+ diff = abs(target_rate - sel_rate[sel] / div);
+ if (min > diff) {
+ val = (sel << 8) | idx;
+ min = diff;
+ en = 1 << (sel + 1); /* fixme */
+ }
+
+ /*
+ * step of 0_0000 / 0_0001 / 0_1101
+ * are out of order
+ */
+ if ((idx > 2) && (idx % 2))
+ step *= 2;
+ if (idx == 0x1c) {
+ div += step;
+ step *= 2;
+ }
+ idx++;
+ }
+ }
+
+ if (min == ~0) {
+ dev_err(dev, "no Input clock\n");
+ return;
+ }
+
+ *target_val = val;
+ if (target_en)
+ *target_en = en;
+}
+
+static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ unsigned int in_rate,
+ unsigned int out_rate,
+ u32 *in, u32 *out, u32 *en)
+{
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ unsigned int target_rate;
+ u32 *target_val;
+ u32 _in;
+ u32 _out;
+ u32 _en;
+
+ /* default = SSI WS */
+ _in =
+ _out = rsnd_adg_ssi_ws_timing_gen2(io);
+
+ target_rate = 0;
+ target_val = NULL;
+ _en = 0;
+ if (runtime->rate != in_rate) {
+ target_rate = out_rate;
+ target_val = &_out;
+ } else if (runtime->rate != out_rate) {
+ target_rate = in_rate;
+ target_val = &_in;
+ }
+
+ if (target_rate)
+ __rsnd_adg_get_timesel_ratio(priv, io,
+ target_rate,
+ target_val, &_en);
+
+ if (in)
+ *in = _in;
+ if (out)
+ *out = _out;
+ if (en)
+ *en = _en;
+}
+
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
struct rsnd_dai_stream *io)
{
@@ -100,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
int shift = (id % 2) ? 16 : 0;
u32 mask, val;
- val = rsnd_adg_ssi_ws_timing_gen2(io);
+ rsnd_adg_get_timesel_ratio(priv, io,
+ rsnd_src_get_in_rate(priv, io),
+ rsnd_src_get_out_rate(priv, io),
+ NULL, &val, NULL);
val = val << shift;
mask = 0xffff << shift;
@@ -110,25 +215,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
return 0;
}
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io,
- u32 timsel)
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
+ struct rsnd_dai_stream *io,
+ unsigned int in_rate,
+ unsigned int out_rate)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- int is_play = rsnd_io_is_play(io);
+ u32 in, out;
+ u32 mask, en;
int id = rsnd_mod_id(src_mod);
int shift = (id % 2) ? 16 : 0;
- u32 mask, ws;
- u32 in, out;
rsnd_mod_confirm_src(src_mod);
- ws = rsnd_adg_ssi_ws_timing_gen2(io);
-
- in = (is_play) ? timsel : ws;
- out = (is_play) ? ws : timsel;
+ rsnd_adg_get_timesel_ratio(priv, io,
+ in_rate, out_rate,
+ &in, &out, &en);
in = in << shift;
out = out << shift;
@@ -157,91 +261,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
break;
}
- return 0;
-}
-
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io,
- unsigned int src_rate,
- unsigned int dst_rate)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
- struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
- struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
- struct device *dev = rsnd_priv_to_dev(priv);
- int idx, sel, div, step, ret;
- u32 val, en;
- unsigned int min, diff;
- unsigned int sel_rate [] = {
- clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */
- clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */
- clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */
- adg->rbga_rate_for_441khz, /* 0011: RBGA */
- adg->rbgb_rate_for_48khz, /* 0100: RBGB */
- };
-
- rsnd_mod_confirm_src(src_mod);
-
- min = ~0;
- val = 0;
- en = 0;
- for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
- idx = 0;
- step = 2;
-
- if (!sel_rate[sel])
- continue;
-
- for (div = 2; div <= 98304; div += step) {
- diff = abs(src_rate - sel_rate[sel] / div);
- if (min > diff) {
- val = (sel << 8) | idx;
- min = diff;
- en = 1 << (sel + 1); /* fixme */
- }
-
- /*
- * step of 0_0000 / 0_0001 / 0_1101
- * are out of order
- */
- if ((idx > 2) && (idx % 2))
- step *= 2;
- if (idx == 0x1c) {
- div += step;
- step *= 2;
- }
- idx++;
- }
- }
-
- if (min == ~0) {
- dev_err(dev, "no Input clock\n");
- return -EIO;
- }
-
- ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
- if (ret < 0) {
- dev_err(dev, "timsel error\n");
- return ret;
- }
-
- rsnd_mod_bset(adg_mod, DIV_EN, en, en);
-
- dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
+ if (en)
+ rsnd_mod_bset(adg_mod, DIV_EN, en, en);
return 0;
}
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
- struct rsnd_dai_stream *io)
-{
- u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
-
- rsnd_mod_confirm_src(src_mod);
-
- return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
-}
-
static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
@@ -518,13 +543,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
return -ENOMEM;
}
- /*
- * ADG is special module.
- * Use ADG mod without rsnd_mod_init() to make debug easy
- * for rsnd_write/rsnd_read
- */
- adg->mod.ops = &adg_ops;
- adg->mod.priv = priv;
+ rsnd_mod_init(priv, &adg->mod, &adg_ops,
+ NULL, NULL, 0, 0);
rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg);
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c
index cd1f064e63c4..abb5eaac854a 100644
--- a/sound/soc/sh/rcar/cmd.c
+++ b/sound/soc/sh/rcar/cmd.c
@@ -29,7 +29,6 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
{
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
- struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct device *dev = rsnd_priv_to_dev(priv);
u32 data;
@@ -38,6 +37,8 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
if (mix) {
struct rsnd_dai *rdai;
+ struct rsnd_mod *src;
+ struct rsnd_dai_stream *tio;
int i;
u32 path[] = {
[0] = 0,
@@ -55,16 +56,20 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
*/
data = 0;
for_each_rsnd_dai(rdai, priv, i) {
- io = &rdai->playback;
- if (mix == rsnd_io_to_mod_mix(io))
+ tio = &rdai->playback;
+ src = rsnd_io_to_mod_src(tio);
+ if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
- io = &rdai->capture;
- if (mix == rsnd_io_to_mod_mix(io))
+ tio = &rdai->capture;
+ src = rsnd_io_to_mod_src(tio);
+ if (mix == rsnd_io_to_mod_mix(tio))
data |= path[rsnd_mod_id(src)];
}
} else {
+ struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
u32 path[] = {
[0] = 0x30000,
[1] = 0x30001,
@@ -152,7 +157,8 @@ int rsnd_cmd_probe(struct rsnd_priv *priv)
for_each_rsnd_cmd(cmd, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
- &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
+ &rsnd_cmd_ops, NULL,
+ rsnd_mod_get_status, RSND_MOD_CMD, i);
if (ret)
return ret;
}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 02b4b085b8d7..3351a701c60e 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
return mod->ops->dma_req(io, mod);
}
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type)
+{
+ return &mod->status;
+}
+
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
- struct rsnd_mod_ops *ops,
- struct clk *clk,
- enum rsnd_mod_type type,
- int id)
+ struct rsnd_mod_ops *ops,
+ struct clk *clk,
+ u32* (*get_status)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type),
+ enum rsnd_mod_type type,
+ int id)
{
int ret = clk_prepare(clk);
@@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv,
mod->type = type;
mod->clk = clk;
mod->priv = priv;
+ mod->get_status = get_status;
return ret;
}
@@ -163,6 +174,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
{
if (mod->clk)
clk_unprepare(mod->clk);
+ mod->clk = NULL;
}
void rsnd_mod_interrupt(struct rsnd_mod *mod,
@@ -212,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io)
return rdai->slots_num;
}
-int rsnd_get_slot_width(struct rsnd_dai_stream *io)
+int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- int chan = runtime->channels;
- /* Multi channel Mode */
- if (rsnd_ssi_multi_slaves(io))
+ return runtime->channels;
+}
+
+int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io)
+{
+ int chan = rsnd_runtime_channel_original(io);
+ struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io);
+
+ if (ctu_mod) {
+ u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod);
+
+ if (converted_chan)
+ return converted_chan;
+ }
+
+ return chan;
+}
+
+int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
+{
+ int chan = rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io);
+
+ /* Use Multi SSI */
+ if (rsnd_runtime_is_ssi_multi(io))
chan /= rsnd_get_slot_num(io);
/* TDM Extend Mode needs 8ch */
@@ -228,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io)
return chan;
}
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
+{
+ int slots = rsnd_get_slot_num(io);
+ int chan = rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io);
+
+ return (chan >= 6) && (slots > 1);
+}
+
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
+{
+ return rsnd_runtime_channel_for_ssi(io) >= 6;
+}
+
/*
* ADINR function
*/
@@ -249,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
return 0;
}
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct device *dev = rsnd_priv_to_dev(priv);
- u32 chan = runtime->channels;
-
- switch (chan) {
- case 1:
- case 2:
- case 4:
- case 6:
- case 8:
- break;
- default:
- dev_warn(dev, "not supported channel\n");
- chan = 0;
- break;
- }
-
- return chan;
-}
-
/*
* DALIGN function
*/
@@ -324,31 +351,73 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \
struct rsnd_mod *mod = (io)->mod[idx]; \
struct device *dev = rsnd_priv_to_dev(priv); \
- u32 *status = (io)->mod_status + idx; \
+ u32 *status = mod->get_status(io, mod, idx); \
u32 mask = 0xF << __rsnd_mod_shift_##func; \
u8 val = (*status >> __rsnd_mod_shift_##func) & 0xF; \
u8 add = ((val + __rsnd_mod_add_##func) & 0xF); \
int ret = 0; \
int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
- *status = (*status & ~mask) + \
- (add << __rsnd_mod_shift_##func); \
+ if (add == 0xF) \
+ call = 0; \
+ else \
+ *status = (*status & ~mask) + \
+ (add << __rsnd_mod_shift_##func); \
dev_dbg(dev, "%s[%d]\t0x%08x %s\n", \
rsnd_mod_name(mod), rsnd_mod_id(mod), \
*status, call ? #func : ""); \
if (call) \
ret = (mod)->ops->func(mod, io, param); \
+ if (ret) \
+ dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n", \
+ rsnd_mod_name(mod), rsnd_mod_id(mod), ret); \
ret; \
})
+static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
+ {
+ /* CAPTURE */
+ RSND_MOD_AUDMAPP,
+ RSND_MOD_AUDMA,
+ RSND_MOD_DVC,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ RSND_MOD_SSIU,
+ RSND_MOD_SSIM3,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIP,
+ RSND_MOD_SSI,
+ }, {
+ /* PLAYBACK */
+ RSND_MOD_AUDMAPP,
+ RSND_MOD_AUDMA,
+ RSND_MOD_SSIM3,
+ RSND_MOD_SSIM2,
+ RSND_MOD_SSIM1,
+ RSND_MOD_SSIP,
+ RSND_MOD_SSI,
+ RSND_MOD_SSIU,
+ RSND_MOD_DVC,
+ RSND_MOD_MIX,
+ RSND_MOD_CTU,
+ RSND_MOD_CMD,
+ RSND_MOD_SRC,
+ },
+};
+
#define rsnd_dai_call(fn, io, param...) \
({ \
struct rsnd_mod *mod; \
+ int type, is_play = rsnd_io_is_play(io); \
int ret = 0, i; \
for (i = 0; i < RSND_MOD_MAX; i++) { \
- mod = (io)->mod[i]; \
+ type = rsnd_mod_sequence[is_play][i]; \
+ mod = (io)->mod[type]; \
if (!mod) \
continue; \
- ret |= rsnd_mod_call(i, io, fn, param); \
+ ret |= rsnd_mod_call(type, io, fn, param); \
} \
ret; \
})
@@ -363,6 +432,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod,
if (!mod)
return -EIO;
+ if (io->mod[type] == mod)
+ return 0;
+
if (io->mod[type])
return -EINVAL;
@@ -511,9 +583,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = rsnd_dai_call(start, io, priv);
if (ret < 0)
goto dai_trigger_end;
+
+ ret = rsnd_dai_call(irq, io, priv, 1);
+ if (ret < 0)
+ goto dai_trigger_end;
+
break;
case SNDRV_PCM_TRIGGER_STOP:
- ret = rsnd_dai_call(stop, io, priv);
+ ret = rsnd_dai_call(irq, io, priv, 0);
+
+ ret |= rsnd_dai_call(stop, io, priv);
ret |= rsnd_dai_call(quit, io, priv);
@@ -863,7 +942,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
}
}
- if (change)
+ if (change && cfg->update)
cfg->update(cfg->io, mod);
return change;
@@ -923,7 +1002,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
int ch_size,
u32 max)
{
- if (ch_size > RSND_DVC_CHANNELS)
+ if (ch_size > RSND_MAX_CHANNELS)
return -EINVAL;
_cfg->cfg.max = max;
@@ -1055,7 +1134,6 @@ static int rsnd_probe(struct platform_device *pdev)
struct rsnd_priv *priv;
struct device *dev = &pdev->dev;
struct rsnd_dai *rdai;
- const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
int (*probe_func[])(struct rsnd_priv *priv) = {
rsnd_gen_probe,
rsnd_dma_probe,
@@ -1081,7 +1159,7 @@ static int rsnd_probe(struct platform_device *pdev)
}
priv->pdev = pdev;
- priv->flags = (unsigned long)of_id->data;
+ priv->flags = (unsigned long)of_device_get_match_data(dev);
spin_lock_init(&priv->lock);
/*
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c
index d53a225d19e9..9dcc1f9db026 100644
--- a/sound/soc/sh/rcar/ctu.c
+++ b/sound/soc/sh/rcar/ctu.c
@@ -12,8 +12,75 @@
#define CTU_NAME_SIZE 16
#define CTU_NAME "ctu"
+/*
+ * User needs to setup CTU by amixer, and its settings are
+ * based on below registers
+ *
+ * CTUn_CPMDR : amixser set "CTU Pass"
+ * CTUn_SV0xR : amixser set "CTU SV0"
+ * CTUn_SV1xR : amixser set "CTU SV1"
+ * CTUn_SV2xR : amixser set "CTU SV2"
+ * CTUn_SV3xR : amixser set "CTU SV3"
+ *
+ * [CTU Pass]
+ * 0000: default
+ * 0001: Connect input data of channel 0
+ * 0010: Connect input data of channel 1
+ * 0011: Connect input data of channel 2
+ * 0100: Connect input data of channel 3
+ * 0101: Connect input data of channel 4
+ * 0110: Connect input data of channel 5
+ * 0111: Connect input data of channel 6
+ * 1000: Connect input data of channel 7
+ * 1001: Connect calculated data by scale values of matrix row 0
+ * 1010: Connect calculated data by scale values of matrix row 1
+ * 1011: Connect calculated data by scale values of matrix row 2
+ * 1100: Connect calculated data by scale values of matrix row 3
+ *
+ * [CTU SVx]
+ * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07]
+ * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17]
+ * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27]
+ * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37]
+ * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+ *
+ * [SVxx]
+ * Plus Minus
+ * value time dB value time dB
+ * -----------------------------------------------------------------------
+ * H'7F_FFFF 2 6 H'80_0000 2 6
+ * ...
+ * H'40_0000 1 0 H'C0_0000 1 0
+ * ...
+ * H'00_0001 2.38 x 10^-7 -132
+ * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132
+ *
+ *
+ * Ex) Input ch -> Output ch
+ * 1ch -> 0ch
+ * 0ch -> 1ch
+ *
+ * amixer set "CTU Reset" on
+ * amixer set "CTU Pass" 9,10
+ * amixer set "CTU SV0" 0,4194304
+ * amixer set "CTU SV1" 4194304,0
+ * or
+ * amixer set "CTU Reset" on
+ * amixer set "CTU Pass" 2,1
+ */
+
struct rsnd_ctu {
struct rsnd_mod mod;
+ struct rsnd_kctrl_cfg_m pass;
+ struct rsnd_kctrl_cfg_m sv0;
+ struct rsnd_kctrl_cfg_m sv1;
+ struct rsnd_kctrl_cfg_m sv2;
+ struct rsnd_kctrl_cfg_m sv3;
+ struct rsnd_kctrl_cfg_s reset;
+ int channels;
};
#define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
@@ -23,12 +90,28 @@ struct rsnd_ctu {
((pos) = (struct rsnd_ctu *)(priv)->ctu + i); \
i++)
+#define rsnd_mod_to_ctu(_mod) \
+ container_of((_mod), struct rsnd_ctu, mod)
+
#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
-#define rsnd_ctu_initialize_lock(mod) __rsnd_ctu_initialize_lock(mod, 1)
-#define rsnd_ctu_initialize_unlock(mod) __rsnd_ctu_initialize_lock(mod, 0)
-static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
+
+static void rsnd_ctu_activation(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, CTU_SWRSR, 0);
+ rsnd_mod_write(mod, CTU_SWRSR, 1);
+}
+
+static void rsnd_ctu_halt(struct rsnd_mod *mod)
+{
+ rsnd_mod_write(mod, CTU_CTUIR, 1);
+ rsnd_mod_write(mod, CTU_SWRSR, 0);
+}
+
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod)
{
- rsnd_mod_write(mod, CTU_CTUIR, enable);
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+
+ return ctu->channels;
}
static int rsnd_ctu_probe_(struct rsnd_mod *mod,
@@ -38,17 +121,103 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod,
return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
}
+static void rsnd_ctu_value_init(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ u32 cpmdr = 0;
+ u32 scmdr = 0;
+ int i;
+
+ for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+ u32 val = ctu->pass.val[i];
+
+ cpmdr |= val << (28 - (i * 4));
+
+ if ((val > 0x8) && (scmdr < (val - 0x8)))
+ scmdr = val - 0x8;
+ }
+
+ rsnd_mod_write(mod, CTU_CTUIR, 1);
+
+ rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io));
+
+ rsnd_mod_write(mod, CTU_CPMDR, cpmdr);
+
+ rsnd_mod_write(mod, CTU_SCMDR, scmdr);
+
+ if (scmdr > 0) {
+ rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]);
+ rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]);
+ rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]);
+ rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]);
+ rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]);
+ rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]);
+ rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]);
+ rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]);
+ }
+ if (scmdr > 1) {
+ rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]);
+ rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]);
+ rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]);
+ rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]);
+ rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]);
+ rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]);
+ rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]);
+ rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]);
+ }
+ if (scmdr > 2) {
+ rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]);
+ rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]);
+ rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]);
+ rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]);
+ rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]);
+ rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]);
+ rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]);
+ rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]);
+ }
+ if (scmdr > 3) {
+ rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]);
+ rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]);
+ rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]);
+ rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]);
+ rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]);
+ rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]);
+ rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]);
+ rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]);
+ }
+
+ rsnd_mod_write(mod, CTU_CTUIR, 0);
+}
+
+static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ int i;
+
+ if (!ctu->reset.val)
+ return;
+
+ for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+ ctu->pass.val[i] = 0;
+ ctu->sv0.val[i] = 0;
+ ctu->sv1.val[i] = 0;
+ ctu->sv2.val[i] = 0;
+ ctu->sv3.val[i] = 0;
+ }
+ ctu->reset.val = 0;
+}
+
static int rsnd_ctu_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
rsnd_mod_power_on(mod);
- rsnd_ctu_initialize_lock(mod);
-
- rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io));
+ rsnd_ctu_activation(mod);
- rsnd_ctu_initialize_unlock(mod);
+ rsnd_ctu_value_init(io, mod);
return 0;
}
@@ -57,16 +226,110 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
+ rsnd_ctu_halt(mod);
+
rsnd_mod_power_off(mod);
return 0;
}
+static int rsnd_ctu_hw_params(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *fe_params)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+ /*
+ * CTU assumes that it is used under DPCM if user want to use
+ * channel transfer. Then, CTU should be FE.
+ * And then, this function will be called *after* BE settings.
+ * this means, each BE already has fixuped hw_params.
+ * see
+ * dpcm_fe_dai_hw_params()
+ * dpcm_be_dai_hw_params()
+ */
+ ctu->channels = 0;
+ if (fe->dai_link->dynamic) {
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct snd_soc_dpcm *dpcm;
+ struct snd_pcm_hw_params *be_params;
+ int stream = substream->stream;
+
+ list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ be_params = &dpcm->hw_params;
+ if (params_channels(fe_params) != params_channels(be_params))
+ ctu->channels = params_channels(be_params);
+ }
+
+ dev_dbg(dev, "CTU convert channels %d\n", ctu->channels);
+ }
+
+ return 0;
+}
+
+static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+ int ret;
+
+ /* CTU Pass */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
+ NULL,
+ &ctu->pass, RSND_MAX_CHANNELS,
+ 0xC);
+
+ /* ROW0 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
+ NULL,
+ &ctu->sv0, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW1 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
+ NULL,
+ &ctu->sv1, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW2 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
+ NULL,
+ &ctu->sv2, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* ROW3 */
+ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
+ NULL,
+ &ctu->sv3, RSND_MAX_CHANNELS,
+ 0x00FFFFFF);
+ if (ret < 0)
+ return ret;
+
+ /* Reset */
+ ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
+ rsnd_ctu_value_reset,
+ &ctu->reset, 1);
+
+ return ret;
+}
+
static struct rsnd_mod_ops rsnd_ctu_ops = {
.name = CTU_NAME,
.probe = rsnd_ctu_probe_,
.init = rsnd_ctu_init,
.quit = rsnd_ctu_quit,
+ .hw_params = rsnd_ctu_hw_params,
+ .pcm_new = rsnd_ctu_pcm_new,
};
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
@@ -129,7 +392,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
- clk, RSND_MOD_CTU, i);
+ clk, rsnd_mod_get_status, RSND_MOD_CTU, i);
if (ret)
goto rsnd_ctu_probe_done;
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 418e6fdd06a3..7658e8fd7bdc 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -622,15 +622,13 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
}
}
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod, int id)
+int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+ struct rsnd_mod **dma_mod, int id)
{
- struct rsnd_mod *dma_mod;
struct rsnd_mod *mod_from = NULL;
struct rsnd_mod *mod_to = NULL;
struct rsnd_priv *priv = rsnd_io_to_priv(io);
struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
- struct rsnd_dma *dma;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod_ops *ops;
enum rsnd_mod_type type;
@@ -646,17 +644,10 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
* rsnd_rdai_continuance_probe()
*/
if (!dmac)
- return ERR_PTR(-EAGAIN);
-
- dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
- if (!dma)
- return ERR_PTR(-ENOMEM);
+ return -EAGAIN;
rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
- dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
- dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
-
/* for Gen2 */
if (mod_from && mod_to) {
ops = &rsnd_dmapp_ops;
@@ -678,27 +669,38 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
type = RSND_MOD_AUDMA;
}
- dma_mod = rsnd_mod_get(dma);
+ if (!(*dma_mod)) {
+ struct rsnd_dma *dma;
- ret = rsnd_mod_init(priv, dma_mod,
- ops, NULL, type, dma_id);
- if (ret < 0)
- return ERR_PTR(ret);
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
- dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
- rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
- rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
- rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
+ *dma_mod = rsnd_mod_get(dma);
- ret = attach(io, dma, id, mod_from, mod_to);
- if (ret < 0)
- return ERR_PTR(ret);
+ dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+ dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0);
+
+ ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
+ rsnd_mod_get_status, type, dma_id);
+ if (ret < 0)
+ return ret;
- ret = rsnd_dai_connect(dma_mod, io, type);
+ dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
+ rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod),
+ rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
+ rsnd_mod_name(mod_to), rsnd_mod_id(mod_to));
+
+ ret = attach(io, dma, id, mod_from, mod_to);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = rsnd_dai_connect(*dma_mod, io, type);
if (ret < 0)
- return ERR_PTR(ret);
+ return ret;
- return rsnd_mod_get(dma);
+ return 0;
}
int rsnd_dma_probe(struct rsnd_priv *priv)
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index d45ffe496397..02d971f69eff 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -8,6 +8,29 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+
+/*
+ * Playback Volume
+ * amixer set "DVC Out" 100%
+ *
+ * Capture Volume
+ * amixer set "DVC In" 100%
+ *
+ * Playback Mute
+ * amixer set "DVC Out Mute" on
+ *
+ * Capture Mute
+ * amixer set "DVC In Mute" on
+ *
+ * Volume Ramp
+ * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps"
+ * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
+ * amixer set "DVC Out Ramp" on
+ * aplay xxx.wav &
+ * amixer set "DVC Out" 80% // Volume Down
+ * amixer set "DVC Out" 100% // Volume Up
+ */
+
#include "rsnd.h"
#define RSND_DVC_NAME_SIZE 16
@@ -83,15 +106,15 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
struct rsnd_mod *mod)
{
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
- u32 val[RSND_DVC_CHANNELS];
+ u32 val[RSND_MAX_CHANNELS];
int i;
/* Enable Ramp */
if (dvc->ren.val)
- for (i = 0; i < RSND_DVC_CHANNELS; i++)
+ for (i = 0; i < RSND_MAX_CHANNELS; i++)
val[i] = dvc->volume.cfg.max;
else
- for (i = 0; i < RSND_DVC_CHANNELS; i++)
+ for (i = 0; i < RSND_MAX_CHANNELS; i++)
val[i] = dvc->volume.val[i];
/* Enable Digital Volume */
@@ -116,7 +139,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
u32 vrdbr = 0;
adinr = rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io);
+ rsnd_runtime_channel_after_ctu(io);
/* Enable Digital Volume, Zero Cross Mute Mode */
dvucr |= 0x101;
@@ -373,7 +396,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
- clk, RSND_MOD_DVC, i);
+ clk, rsnd_mod_get_status, RSND_MOD_DVC, i);
if (ret)
goto rsnd_dvc_probe_done;
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index ea24247eba73..46c0ba7b6414 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -104,23 +104,6 @@ void rsnd_write(struct rsnd_priv *priv,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
- regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
-
- dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- rsnd_reg_name(gen, reg), reg, data);
-}
-
-void rsnd_force_write(struct rsnd_priv *priv,
- struct rsnd_mod *mod,
- enum rsnd_reg reg, u32 data)
-{
- struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
- if (!rsnd_is_accessible_reg(priv, gen, reg))
- return;
-
regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
@@ -137,8 +120,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
- regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
- mask, data);
+ regmap_fields_force_update_bits(gen->regs[reg],
+ rsnd_mod_id(mod), mask, data);
dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
rsnd_mod_name(mod), rsnd_mod_id(mod),
@@ -260,8 +243,43 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
+ RSND_GEN_M_REG(CTU_SWRSR, 0x500, 0x100),
RSND_GEN_M_REG(CTU_CTUIR, 0x504, 0x100),
RSND_GEN_M_REG(CTU_ADINR, 0x508, 0x100),
+ RSND_GEN_M_REG(CTU_CPMDR, 0x510, 0x100),
+ RSND_GEN_M_REG(CTU_SCMDR, 0x514, 0x100),
+ RSND_GEN_M_REG(CTU_SV00R, 0x518, 0x100),
+ RSND_GEN_M_REG(CTU_SV01R, 0x51c, 0x100),
+ RSND_GEN_M_REG(CTU_SV02R, 0x520, 0x100),
+ RSND_GEN_M_REG(CTU_SV03R, 0x524, 0x100),
+ RSND_GEN_M_REG(CTU_SV04R, 0x528, 0x100),
+ RSND_GEN_M_REG(CTU_SV05R, 0x52c, 0x100),
+ RSND_GEN_M_REG(CTU_SV06R, 0x530, 0x100),
+ RSND_GEN_M_REG(CTU_SV07R, 0x534, 0x100),
+ RSND_GEN_M_REG(CTU_SV10R, 0x538, 0x100),
+ RSND_GEN_M_REG(CTU_SV11R, 0x53c, 0x100),
+ RSND_GEN_M_REG(CTU_SV12R, 0x540, 0x100),
+ RSND_GEN_M_REG(CTU_SV13R, 0x544, 0x100),
+ RSND_GEN_M_REG(CTU_SV14R, 0x548, 0x100),
+ RSND_GEN_M_REG(CTU_SV15R, 0x54c, 0x100),
+ RSND_GEN_M_REG(CTU_SV16R, 0x550, 0x100),
+ RSND_GEN_M_REG(CTU_SV17R, 0x554, 0x100),
+ RSND_GEN_M_REG(CTU_SV20R, 0x558, 0x100),
+ RSND_GEN_M_REG(CTU_SV21R, 0x55c, 0x100),
+ RSND_GEN_M_REG(CTU_SV22R, 0x560, 0x100),
+ RSND_GEN_M_REG(CTU_SV23R, 0x564, 0x100),
+ RSND_GEN_M_REG(CTU_SV24R, 0x568, 0x100),
+ RSND_GEN_M_REG(CTU_SV25R, 0x56c, 0x100),
+ RSND_GEN_M_REG(CTU_SV26R, 0x570, 0x100),
+ RSND_GEN_M_REG(CTU_SV27R, 0x574, 0x100),
+ RSND_GEN_M_REG(CTU_SV30R, 0x578, 0x100),
+ RSND_GEN_M_REG(CTU_SV31R, 0x57c, 0x100),
+ RSND_GEN_M_REG(CTU_SV32R, 0x580, 0x100),
+ RSND_GEN_M_REG(CTU_SV33R, 0x584, 0x100),
+ RSND_GEN_M_REG(CTU_SV34R, 0x588, 0x100),
+ RSND_GEN_M_REG(CTU_SV35R, 0x58c, 0x100),
+ RSND_GEN_M_REG(CTU_SV36R, 0x590, 0x100),
+ RSND_GEN_M_REG(CTU_SV37R, 0x594, 0x100),
RSND_GEN_M_REG(MIX_SWRSR, 0xd00, 0x40),
RSND_GEN_M_REG(MIX_MIXIR, 0xd04, 0x40),
RSND_GEN_M_REG(MIX_ADINR, 0xd08, 0x40),
diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c
index 65542b6a89e9..195fc7bb22af 100644
--- a/sound/soc/sh/rcar/mix.c
+++ b/sound/soc/sh/rcar/mix.c
@@ -51,7 +51,7 @@ static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, MIX_MIXIR, 1);
/* General Information */
- rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
+ rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io));
/* volume step */
rsnd_mod_write(mod, MIX_MIXMR, 0);
@@ -172,7 +172,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
- clk, RSND_MOD_MIX, i);
+ clk, rsnd_mod_get_status, RSND_MOD_MIX, i);
if (ret)
goto rsnd_mix_probe_done;
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 317dd793149a..fc89a67258ca 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -86,8 +86,43 @@ enum rsnd_reg {
RSND_REG_CMD_BUSIF_DALIGN, /* Gen2 only */
RSND_REG_CMD_ROUTE_SLCT,
RSND_REG_CMDOUT_TIMSEL, /* Gen2 only */
+ RSND_REG_CTU_SWRSR,
RSND_REG_CTU_CTUIR,
RSND_REG_CTU_ADINR,
+ RSND_REG_CTU_CPMDR,
+ RSND_REG_CTU_SCMDR,
+ RSND_REG_CTU_SV00R,
+ RSND_REG_CTU_SV01R,
+ RSND_REG_CTU_SV02R,
+ RSND_REG_CTU_SV03R,
+ RSND_REG_CTU_SV04R,
+ RSND_REG_CTU_SV05R,
+ RSND_REG_CTU_SV06R,
+ RSND_REG_CTU_SV07R,
+ RSND_REG_CTU_SV10R,
+ RSND_REG_CTU_SV11R,
+ RSND_REG_CTU_SV12R,
+ RSND_REG_CTU_SV13R,
+ RSND_REG_CTU_SV14R,
+ RSND_REG_CTU_SV15R,
+ RSND_REG_CTU_SV16R,
+ RSND_REG_CTU_SV17R,
+ RSND_REG_CTU_SV20R,
+ RSND_REG_CTU_SV21R,
+ RSND_REG_CTU_SV22R,
+ RSND_REG_CTU_SV23R,
+ RSND_REG_CTU_SV24R,
+ RSND_REG_CTU_SV25R,
+ RSND_REG_CTU_SV26R,
+ RSND_REG_CTU_SV27R,
+ RSND_REG_CTU_SV30R,
+ RSND_REG_CTU_SV31R,
+ RSND_REG_CTU_SV32R,
+ RSND_REG_CTU_SV33R,
+ RSND_REG_CTU_SV34R,
+ RSND_REG_CTU_SV35R,
+ RSND_REG_CTU_SV36R,
+ RSND_REG_CTU_SV37R,
RSND_REG_MIX_SWRSR,
RSND_REG_MIX_MIXIR,
RSND_REG_MIX_ADINR,
@@ -147,8 +182,6 @@ struct rsnd_dai_stream;
rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
#define rsnd_mod_write(m, r, d) \
rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
-#define rsnd_mod_force_write(m, r, d) \
- rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
#define rsnd_mod_bset(m, r, s, d) \
rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
@@ -160,14 +193,13 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
u32 mask, u32 data);
u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
/*
* R-Car DMA
*/
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod, int id);
+int rsnd_dma_attach(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id);
int rsnd_dma_probe(struct rsnd_priv *priv);
struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
struct rsnd_mod *mod, char *name);
@@ -214,6 +246,9 @@ struct rsnd_mod_ops {
int (*stop)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv);
+ int (*irq)(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv, int enable);
int (*pcm_new)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd);
@@ -233,47 +268,54 @@ struct rsnd_mod {
struct rsnd_mod_ops *ops;
struct rsnd_priv *priv;
struct clk *clk;
+ u32 *(*get_status)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type);
+ u32 status;
};
/*
* status
*
- * 0xH0000CBA
+ * 0xH0000CB0
*
- * A 0: probe 1: remove
* B 0: init 1: quit
* C 0: start 1: stop
*
* H is always called (see __rsnd_mod_call)
+ * H 0: probe 1: remove
* H 0: pcm_new
* H 0: fallback
* H 0: hw_params
*/
-#define __rsnd_mod_shift_probe 0
-#define __rsnd_mod_shift_remove 0
#define __rsnd_mod_shift_init 4
#define __rsnd_mod_shift_quit 4
#define __rsnd_mod_shift_start 8
#define __rsnd_mod_shift_stop 8
+#define __rsnd_mod_shift_probe 28 /* always called */
+#define __rsnd_mod_shift_remove 28 /* always called */
+#define __rsnd_mod_shift_irq 28 /* always called */
#define __rsnd_mod_shift_pcm_new 28 /* always called */
#define __rsnd_mod_shift_fallback 28 /* always called */
#define __rsnd_mod_shift_hw_params 28 /* always called */
-#define __rsnd_mod_add_probe 1
-#define __rsnd_mod_add_remove -1
+#define __rsnd_mod_add_probe 0
+#define __rsnd_mod_add_remove 0
#define __rsnd_mod_add_init 1
#define __rsnd_mod_add_quit -1
#define __rsnd_mod_add_start 1
#define __rsnd_mod_add_stop -1
+#define __rsnd_mod_add_irq 0
#define __rsnd_mod_add_pcm_new 0
#define __rsnd_mod_add_fallback 0
#define __rsnd_mod_add_hw_params 0
#define __rsnd_mod_call_probe 0
-#define __rsnd_mod_call_remove 1
+#define __rsnd_mod_call_remove 0
#define __rsnd_mod_call_init 0
#define __rsnd_mod_call_quit 1
#define __rsnd_mod_call_start 0
#define __rsnd_mod_call_stop 1
+#define __rsnd_mod_call_irq 0
#define __rsnd_mod_call_pcm_new 0
#define __rsnd_mod_call_fallback 0
#define __rsnd_mod_call_hw_params 0
@@ -286,10 +328,13 @@ struct rsnd_mod {
int rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
- struct rsnd_mod_ops *ops,
- struct clk *clk,
- enum rsnd_mod_type type,
- int id);
+ struct rsnd_mod_ops *ops,
+ struct clk *clk,
+ u32* (*get_status)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type),
+ enum rsnd_mod_type type,
+ int id);
void rsnd_mod_quit(struct rsnd_mod *mod);
char *rsnd_mod_name(struct rsnd_mod *mod);
struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
@@ -297,6 +342,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
void rsnd_mod_interrupt(struct rsnd_mod *mod,
void (*callback)(struct rsnd_mod *mod,
struct rsnd_dai_stream *io));
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type);
+
void rsnd_parse_connect_common(struct rsnd_dai *rdai,
struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
struct device_node *node,
@@ -306,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
void rsnd_set_slot(struct rsnd_dai *rdai,
int slots, int slots_total);
int rsnd_get_slot(struct rsnd_dai_stream *io);
-int rsnd_get_slot_width(struct rsnd_dai_stream *io);
int rsnd_get_slot_num(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_original(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
+
/*
* R-Car sound DAI
*/
@@ -319,7 +373,7 @@ struct rsnd_dai_stream {
struct rsnd_mod *mod[RSND_MOD_MAX];
struct rsnd_dai_path_info *info; /* rcar_snd.h */
struct rsnd_dai *rdai;
- u32 mod_status[RSND_MOD_MAX];
+ u32 parent_ssi_status;
int byte_pos;
int period_pos;
int byte_per_period;
@@ -392,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
int rsnd_adg_probe(struct rsnd_priv *priv);
void rsnd_adg_remove(struct rsnd_priv *priv);
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
struct rsnd_dai_stream *io,
- unsigned int src_rate,
- unsigned int dst_rate);
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io);
+ unsigned int in_rate,
+ unsigned int out_rate);
int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
struct rsnd_dai_stream *io);
@@ -498,10 +550,10 @@ struct rsnd_kctrl_cfg {
struct snd_kcontrol *kctrl;
};
-#define RSND_DVC_CHANNELS 8
+#define RSND_MAX_CHANNELS 8
struct rsnd_kctrl_cfg_m {
struct rsnd_kctrl_cfg cfg;
- u32 val[RSND_DVC_CHANNELS];
+ u32 val[RSND_MAX_CHANNELS];
};
struct rsnd_kctrl_cfg_s {
@@ -547,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
#define rsnd_ssi_is_pin_sharing(io) \
__rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
@@ -573,9 +625,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv);
int rsnd_src_probe(struct rsnd_priv *priv);
void rsnd_src_remove(struct rsnd_priv *priv);
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
- struct rsnd_dai_stream *io,
- struct snd_pcm_runtime *runtime);
+
+#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1)
+#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ int is_in);
+
#define rsnd_src_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
#define rsnd_parse_connect_src(rdai, playback, capture) \
@@ -588,6 +644,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
*/
int rsnd_ctu_probe(struct rsnd_priv *priv);
void rsnd_ctu_remove(struct rsnd_priv *priv);
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod);
struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
#define rsnd_ctu_of_node(priv) \
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
index 8a357fdf1077..1bc7ecfc42a9 100644
--- a/sound/soc/sh/rcar/rsrc-card.c
+++ b/sound/soc/sh/rcar/rsrc-card.c
@@ -66,12 +66,12 @@ struct rsrc_card_priv {
struct snd_soc_dai_link *dai_link;
int dai_num;
u32 convert_rate;
+ u32 convert_channels;
};
#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i))
-#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
static int rsrc_card_startup(struct snd_pcm_substream *substream)
{
@@ -145,11 +145,16 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
- if (!priv->convert_rate)
- return 0;
+ if (priv->convert_rate)
+ rate->min =
+ rate->max = priv->convert_rate;
- rate->min = rate->max = priv->convert_rate;
+ if (priv->convert_channels)
+ channels->min =
+ channels->max = priv->convert_channels;
return 0;
}
@@ -246,7 +251,7 @@ static int rsrc_card_parse_links(struct device_node *np,
struct device *dev = rsrc_priv_to_dev(priv);
const struct rsrc_card_of_data *of_data;
- of_data = rsrc_dev_to_of_data(dev);
+ of_data = of_device_get_match_data(dev);
/* FE is dummy */
dai_link->cpu_of_node = NULL;
@@ -396,7 +401,7 @@ static int rsrc_card_parse_of(struct device_node *node,
struct rsrc_card_priv *priv,
struct device *dev)
{
- const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+ const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev);
struct rsrc_card_dai *props;
struct snd_soc_dai_link *links;
int ret;
@@ -437,9 +442,13 @@ static int rsrc_card_parse_of(struct device_node *node,
/* sampling rate convert */
of_property_read_u32(node, "convert-rate", &priv->convert_rate);
- dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n",
- priv->snd_card.name ? priv->snd_card.name : "",
- priv->convert_rate);
+ /* channels transfer */
+ of_property_read_u32(node, "convert-channels", &priv->convert_channels);
+
+ dev_dbg(dev, "New rsrc-audio-card: %s\n",
+ priv->snd_card.name ? priv->snd_card.name : "");
+ dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate);
+ dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels);
ret = rsrc_card_dai_link_of(node, priv);
if (ret < 0)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 5eda056d9f20..15d6ffe8be74 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -25,7 +25,6 @@ struct rsnd_src {
struct rsnd_kctrl_cfg_s sen; /* sync convert enable */
struct rsnd_kctrl_cfg_s sync; /* sync convert */
u32 convert_rate; /* sampling rate convert */
- int err;
int irq;
};
@@ -34,7 +33,7 @@ struct rsnd_src {
#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
#define rsnd_src_to_dma(src) ((src)->dma)
#define rsnd_src_nr(priv) ((priv)->src_nr)
-#define rsnd_enable_sync_convert(src) ((src)->sen.val)
+#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val)
#define rsnd_mod_to_src(_mod) \
container_of((_mod), struct rsnd_src, mod)
@@ -94,15 +93,16 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
}
static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
- struct rsnd_src *src)
+ struct rsnd_mod *mod)
{
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 convert_rate;
if (!runtime)
return 0;
- if (!rsnd_enable_sync_convert(src))
+ if (!rsnd_src_sync_is_enabled(mod))
return src->convert_rate;
convert_rate = src->sync.val;
@@ -116,23 +116,33 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
return convert_rate;
}
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
- struct rsnd_dai_stream *io,
- struct snd_pcm_runtime *runtime)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+ struct rsnd_dai_stream *io,
+ int is_in)
{
struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
- struct rsnd_src *src;
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
unsigned int rate = 0;
+ int is_play = rsnd_io_is_play(io);
- if (src_mod) {
- src = rsnd_mod_to_src(src_mod);
+ /*
+ *
+ * Playback
+ * runtime_rate -> [SRC] -> convert_rate
+ *
+ * Capture
+ * convert_rate -> [SRC] -> runtime_rate
+ */
- /*
- * return convert rate if SRC is used,
- * otherwise, return runtime->rate as usual
- */
- rate = rsnd_src_convert_rate(io, src);
- }
+ if (is_play == is_in)
+ return runtime->rate;
+
+ /*
+ * return convert rate if SRC is used,
+ * otherwise, return runtime->rate as usual
+ */
+ if (src_mod)
+ rate = rsnd_src_convert_rate(io, src_mod);
if (!rate)
rate = runtime->rate;
@@ -179,8 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct device *dev = rsnd_priv_to_dev(priv);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- u32 convert_rate = rsnd_src_convert_rate(io, src);
+ u32 fin, fout;
u32 ifscr, fsrate, adinr;
u32 cr, route;
u32 bsdsr, bsisr;
@@ -189,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
if (!runtime)
return;
+ fin = rsnd_src_get_in_rate(priv, io);
+ fout = rsnd_src_get_out_rate(priv, io);
+
/* 6 - 1/6 are very enough ratio for SRC_BSDSR */
- if (!convert_rate)
+ if (fin == fout)
ratio = 0;
- else if (convert_rate > runtime->rate)
- ratio = 100 * convert_rate / runtime->rate;
+ else if (fin > fout)
+ ratio = 100 * fin / fout;
else
- ratio = 100 * runtime->rate / convert_rate;
+ ratio = 100 * fout / fin;
if (ratio > 600) {
dev_err(dev, "FSO/FSI ratio error\n");
@@ -206,16 +218,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
* SRC_ADINR
*/
adinr = rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io);
+ rsnd_runtime_channel_original(io);
/*
* SRC_IFSCR / SRC_IFSVR
*/
ifscr = 0;
fsrate = 0;
- if (convert_rate) {
+ if (fin != fout) {
ifscr = 1;
- fsrate = 0x0400000 / convert_rate * runtime->rate;
+ fsrate = 0x0400000 / fout * fin;
}
/*
@@ -223,10 +235,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
*/
cr = 0x00011110;
route = 0x0;
- if (convert_rate) {
+ if (fin != fout) {
route = 0x1;
- if (rsnd_enable_sync_convert(src)) {
+ if (rsnd_src_sync_is_enabled(mod)) {
cr |= 0x1;
route |= rsnd_io_is_play(io) ?
(0x1 << 24) : (0x1 << 25);
@@ -250,6 +262,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
break;
}
+ rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+
rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */
rsnd_mod_write(mod, SRC_ADINR, adinr);
rsnd_mod_write(mod, SRC_IFSCR, ifscr);
@@ -259,22 +273,17 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
rsnd_mod_write(mod, SRC_BSISR, bsisr);
rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */
- rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
- if (convert_rate)
- rsnd_adg_set_convert_clk_gen2(mod, io,
- runtime->rate,
- convert_rate);
- else
- rsnd_adg_set_convert_timing_gen2(mod, io);
+ rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
}
-#define rsnd_src_irq_enable(mod) rsnd_src_irq_ctrol(mod, 1)
-#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0)
-static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
+static int rsnd_src_irq(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv,
+ int enable)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 sys_int_val, int_val, sys_int_mask;
@@ -298,14 +307,16 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
/*
* WORKAROUND
*
- * ignore over flow error when rsnd_enable_sync_convert()
+ * ignore over flow error when rsnd_src_sync_is_enabled()
*/
- if (rsnd_enable_sync_convert(src))
+ if (rsnd_src_sync_is_enabled(mod))
sys_int_val = sys_int_val & 0xffff;
rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
+
+ return 0;
}
static void rsnd_src_status_clear(struct rsnd_mod *mod)
@@ -316,9 +327,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod)
rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
}
-static bool rsnd_src_record_error(struct rsnd_mod *mod)
+static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val0, val1;
bool ret = false;
@@ -327,18 +337,14 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod)
/*
* WORKAROUND
*
- * ignore over flow error when rsnd_enable_sync_convert()
+ * ignore over flow error when rsnd_src_sync_is_enabled()
*/
- if (rsnd_enable_sync_convert(src))
+ if (rsnd_src_sync_is_enabled(mod))
val0 = val0 & 0xffff;
if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) ||
- (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) {
- struct rsnd_src *src = rsnd_mod_to_src(mod);
-
- src->err++;
+ (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1))
ret = true;
- }
return ret;
}
@@ -347,7 +353,6 @@ static int rsnd_src_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- struct rsnd_src *src = rsnd_mod_to_src(mod);
u32 val;
/*
@@ -355,7 +360,7 @@ static int rsnd_src_start(struct rsnd_mod *mod,
*
* Enable SRC output if you want to use sync convert together with DVC
*/
- val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ?
+ val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ?
0x01 : 0x11;
rsnd_mod_write(mod, SRC_CTRL, val);
@@ -367,11 +372,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
{
- /*
- * stop SRC output only
- * see rsnd_src_quit
- */
- rsnd_mod_write(mod, SRC_CTRL, 0x01);
+ rsnd_mod_write(mod, SRC_CTRL, 0);
return 0;
}
@@ -390,10 +391,6 @@ static int rsnd_src_init(struct rsnd_mod *mod,
rsnd_src_status_clear(mod);
- rsnd_src_irq_enable(mod);
-
- src->err = 0;
-
/* reset sync convert_rate */
src->sync.val = 0;
@@ -405,21 +402,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
-
- rsnd_src_irq_disable(mod);
-
- /* stop both out/in */
- rsnd_mod_write(mod, SRC_CTRL, 0);
rsnd_src_halt(mod);
rsnd_mod_power_off(mod);
- if (src->err)
- dev_warn(dev, "%s[%d] under/over flow err = %d\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
-
src->convert_rate = 0;
/* reset sync convert_rate */
@@ -432,8 +419,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct rsnd_src *src = rsnd_mod_to_src(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
+ bool stop = false;
spin_lock(&priv->lock);
@@ -441,26 +427,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
if (!rsnd_io_is_working(io))
goto rsnd_src_interrupt_out;
- if (rsnd_src_record_error(mod)) {
-
- dev_dbg(dev, "%s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
-
- rsnd_src_stop(mod, io, priv);
- rsnd_src_start(mod, io, priv);
- }
-
- if (src->err > 1024) {
- rsnd_src_irq_disable(mod);
-
- dev_warn(dev, "no more %s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- }
+ if (rsnd_src_error_occurred(mod))
+ stop = true;
rsnd_src_status_clear(mod);
rsnd_src_interrupt_out:
spin_unlock(&priv->lock);
+
+ if (stop)
+ snd_pcm_stop_xrun(io->substream);
}
static irqreturn_t rsnd_src_interrupt(int irq, void *data)
@@ -485,7 +461,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
/*
* IRQ is not supported on non-DT
* see
- * rsnd_src_irq_enable()
+ * rsnd_src_irq()
*/
ret = devm_request_irq(dev, irq,
rsnd_src_interrupt,
@@ -495,9 +471,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
return ret;
}
- src->dma = rsnd_dma_attach(io, mod, 0);
- if (IS_ERR(src->dma))
- return PTR_ERR(src->dma);
+ ret = rsnd_dma_attach(io, mod, &src->dma, 0);
return ret;
}
@@ -506,8 +480,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct snd_soc_pcm_runtime *rtd)
{
- struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_src *src = rsnd_mod_to_src(mod);
int ret;
@@ -516,15 +488,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
*/
/*
- * SRC sync convert needs clock master
- */
- if (!rsnd_rdai_is_clk_master(rdai))
- return 0;
-
- /*
- * SRC In doesn't work if DVC was enabled
+ * It can't use SRC Synchronous convert
+ * when Capture if it uses CMD
*/
- if (dvc && !rsnd_io_is_play(io))
+ if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io))
return 0;
/*
@@ -557,6 +524,7 @@ static struct rsnd_mod_ops rsnd_src_ops = {
.quit = rsnd_src_quit,
.start = rsnd_src_start,
.stop = rsnd_src_stop,
+ .irq = rsnd_src_irq,
.hw_params = rsnd_src_hw_params,
.pcm_new = rsnd_src_pcm_new,
};
@@ -622,7 +590,8 @@ int rsnd_src_probe(struct rsnd_priv *priv)
}
ret = rsnd_mod_init(priv, rsnd_mod_get(src),
- &rsnd_src_ops, clk, RSND_MOD_SRC, i);
+ &rsnd_src_ops, clk, rsnd_mod_get_status,
+ RSND_MOD_SRC, i);
if (ret)
goto rsnd_src_probe_done;
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 7ee89da4dd5f..5f848f054745 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -64,7 +64,6 @@
#define SSI_NAME "ssi"
struct rsnd_ssi {
- struct rsnd_ssi *parent;
struct rsnd_mod mod;
struct rsnd_mod *dma;
@@ -75,7 +74,6 @@ struct rsnd_ssi {
u32 wsr;
int chan;
int rate;
- int err;
int irq;
unsigned int usrcnt;
};
@@ -96,7 +94,10 @@ struct rsnd_ssi {
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_ssi_mode_flags(p) ((p)->flags)
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
-#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
+#define rsnd_ssi_is_multi_slave(mod, io) \
+ (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
+#define rsnd_ssi_is_run_mods(mod, io) \
+ (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
@@ -141,43 +142,13 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
udelay(50);
}
- dev_warn(dev, "status check failed\n");
-}
-
-static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
- if (rsnd_is_gen1(priv))
- return 0;
-
- /* enable SSI interrupt if Gen2 */
- rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
- rsnd_ssi_is_dma_mode(ssi_mod) ?
- 0x0e000000 : 0x0f000000);
-
- return 0;
-}
-
-static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
-{
- struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
- if (rsnd_is_gen1(priv))
- return 0;
-
- /* disable SSI interrupt if Gen2 */
- rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
-
- return 0;
+ dev_warn(dev, "%s[%d] status check failed\n",
+ rsnd_mod_name(mod), rsnd_mod_id(mod));
}
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
+static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod;
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
- struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct device *dev = rsnd_priv_to_dev(priv);
enum rsnd_mod_type types[] = {
RSND_MOD_SSIM1,
RSND_MOD_SSIM2,
@@ -185,16 +156,6 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
};
int i, mask;
- switch (runtime->channels) {
- case 2: /* Multi channel is not needed for Stereo */
- return 0;
- case 6:
- break;
- default:
- dev_err(dev, "unsupported channel\n");
- return 0;
- }
-
mask = 0;
for (i = 0; i < ARRAY_SIZE(types); i++) {
mod = rsnd_io_to_mod(io, types[i]);
@@ -207,22 +168,41 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
return mask;
}
-static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
+static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io)
+{
+ struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+ struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
+
+ return rsnd_ssi_multi_slaves_runtime(io) |
+ 1 << rsnd_mod_id(ssi_mod) |
+ 1 << rsnd_mod_id(ssi_parent_mod);
+}
+
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io)
+{
+ if (rsnd_runtime_is_ssi_multi(io))
+ return rsnd_ssi_multi_slaves(io);
+
+ return 0;
+}
+
+static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
- int slots = rsnd_get_slot_width(io);
+ int chan = rsnd_runtime_channel_for_ssi(io);
int j, ret;
int ssi_clk_mul_table[] = {
1, 2, 4, 8, 16, 6, 12,
};
unsigned int main_rate;
- unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
+ unsigned int rate = rsnd_io_is_play(io) ?
+ rsnd_src_get_out_rate(priv, io) :
+ rsnd_src_get_in_rate(priv, io);
if (!rsnd_rdai_is_clk_master(rdai))
return 0;
@@ -249,10 +229,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
/*
* this driver is assuming that
- * system word is 32bit x slots
+ * system word is 32bit x chan
* see rsnd_ssi_init()
*/
- main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
+ main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
if (0 == ret) {
@@ -274,11 +254,11 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
return -EIO;
}
-static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
+static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
if (!rsnd_rdai_is_clk_master(rdai))
@@ -296,17 +276,18 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
rsnd_adg_ssi_clk_stop(mod);
}
-static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
+static void rsnd_ssi_config_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
u32 cr_own;
u32 cr_mode;
u32 wsr;
int is_tdm;
- is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
+ is_tdm = rsnd_runtime_is_ssi_tdm(io);
/*
* always use 32bit system word.
@@ -332,11 +313,9 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
case 32:
cr_own |= DWL_24;
break;
- default:
- return -EINVAL;
}
- if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) {
+ if (rsnd_ssi_is_dma_mode(mod)) {
cr_mode = UIEN | OIEN | /* over/under run */
DMEN; /* DMA : enable DMA */
} else {
@@ -357,8 +336,16 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
ssi->cr_own = cr_own;
ssi->cr_mode = cr_mode;
ssi->wsr = wsr;
+}
- return 0;
+static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+ rsnd_mod_write(mod, SSIWSR, ssi->wsr);
+ rsnd_mod_write(mod, SSICR, ssi->cr_own |
+ ssi->cr_clk |
+ ssi->cr_mode); /* without EN */
}
/*
@@ -371,28 +358,25 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int ret;
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
ssi->usrcnt++;
rsnd_mod_power_on(mod);
- ret = rsnd_ssi_master_clk_start(ssi, io);
+ ret = rsnd_ssi_master_clk_start(mod, io);
if (ret < 0)
return ret;
- if (rsnd_ssi_is_parent(mod, io))
- return 0;
-
- ret = rsnd_ssi_config_init(ssi, io);
- if (ret < 0)
- return ret;
+ if (!rsnd_ssi_is_parent(mod, io))
+ rsnd_ssi_config_init(mod, io);
- ssi->err = -1; /* ignore 1st error */
+ rsnd_ssi_register_setup(mod);
/* clear error status */
rsnd_ssi_status_clear(mod);
- rsnd_ssi_irq_enable(mod);
-
return 0;
}
@@ -403,25 +387,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct device *dev = rsnd_priv_to_dev(priv);
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
if (!ssi->usrcnt) {
dev_err(dev, "%s[%d] usrcnt error\n",
rsnd_mod_name(mod), rsnd_mod_id(mod));
return -EIO;
}
- if (!rsnd_ssi_is_parent(mod, io)) {
- if (ssi->err > 0)
- dev_warn(dev, "%s[%d] under/over flow err = %d\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod),
- ssi->err);
-
+ if (!rsnd_ssi_is_parent(mod, io))
ssi->cr_own = 0;
- ssi->err = 0;
- rsnd_ssi_irq_disable(mod);
- }
-
- rsnd_ssi_master_clk_stop(ssi, io);
+ rsnd_ssi_master_clk_stop(mod, io);
rsnd_mod_power_off(mod);
@@ -456,61 +434,43 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
return 0;
}
-static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
-{
- struct rsnd_mod *mod = rsnd_mod_get(ssi);
- u32 status = rsnd_ssi_status_get(mod);
-
- /* under/over flow error */
- if (status & (UIRQ | OIRQ))
- ssi->err++;
-
- return status;
-}
-
-static int __rsnd_ssi_start(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- u32 cr;
-
- cr = ssi->cr_own |
- ssi->cr_clk |
- ssi->cr_mode;
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
/*
* EN will be set via SSIU :: SSI_CONTROL
* if Multi channel mode
*/
- if (!rsnd_ssi_multi_slaves(io))
- cr |= EN;
+ if (rsnd_ssi_multi_slaves_runtime(io))
+ return 0;
- rsnd_mod_write(mod, SSICR, cr);
- rsnd_mod_write(mod, SSIWSR, ssi->wsr);
+ rsnd_mod_bset(mod, SSICR, EN, EN);
return 0;
}
-static int rsnd_ssi_start(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv)
{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 cr;
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
/*
- * no limit to start
+ * don't stop if not last user
* see also
- * rsnd_ssi_stop
+ * rsnd_ssi_start
* rsnd_ssi_interrupt
*/
- return __rsnd_ssi_start(mod, io, priv);
-}
-
-static int __rsnd_ssi_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
- u32 cr;
+ if (ssi->usrcnt > 1)
+ return 0;
/*
* disable all IRQ,
@@ -532,33 +492,38 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod,
return 0;
}
-static int rsnd_ssi_stop(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+static int rsnd_ssi_irq(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct rsnd_priv *priv,
+ int enable)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ u32 val = 0;
- /*
- * don't stop if not last user
- * see also
- * rsnd_ssi_start
- * rsnd_ssi_interrupt
- */
- if (ssi->usrcnt > 1)
+ if (rsnd_is_gen1(priv))
return 0;
- return __rsnd_ssi_stop(mod, io, priv);
+ if (rsnd_ssi_is_parent(mod, io))
+ return 0;
+
+ if (!rsnd_ssi_is_run_mods(mod, io))
+ return 0;
+
+ if (enable)
+ val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000;
+
+ rsnd_mod_write(mod, SSI_INT_ENABLE, val);
+
+ return 0;
}
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
struct rsnd_dai_stream *io)
{
- struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
int is_dma = rsnd_ssi_is_dma_mode(mod);
u32 status;
bool elapsed = false;
+ bool stop = false;
spin_lock(&priv->lock);
@@ -566,7 +531,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
if (!rsnd_io_is_working(io))
goto rsnd_ssi_interrupt_out;
- status = rsnd_ssi_record_error(ssi);
+ status = rsnd_ssi_status_get(mod);
/* PIO only */
if (!is_dma && (status & DIRQ)) {
@@ -588,23 +553,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
}
/* DMA only */
- if (is_dma && (status & (UIRQ | OIRQ))) {
- /*
- * restart SSI
- */
- dev_dbg(dev, "%s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
-
- __rsnd_ssi_stop(mod, io, priv);
- __rsnd_ssi_start(mod, io, priv);
- }
-
- if (ssi->err > 1024) {
- rsnd_ssi_irq_disable(mod);
-
- dev_warn(dev, "no more %s[%d] restart\n",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
- }
+ if (is_dma && (status & (UIRQ | OIRQ)))
+ stop = true;
rsnd_ssi_status_clear(mod);
rsnd_ssi_interrupt_out:
@@ -612,6 +562,10 @@ rsnd_ssi_interrupt_out:
if (elapsed)
rsnd_dai_period_elapsed(io);
+
+ if (stop)
+ snd_pcm_stop_xrun(io->substream);
+
}
static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
@@ -627,12 +581,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
* SSI PIO
*/
static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
+ struct rsnd_dai_stream *io)
{
+ struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
if (!__rsnd_ssi_is_pin_sharing(mod))
return;
+ if (!rsnd_rdai_is_clk_master(rdai))
+ return;
+
switch (rsnd_mod_id(mod)) {
case 1:
case 2:
@@ -647,6 +606,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
}
}
+static int rsnd_ssi_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ /*
+ * rsnd_rdai_is_clk_master() will be enabled after set_fmt,
+ * and, pcm_new will be called after it.
+ * This function reuse pcm_new at this point.
+ */
+ rsnd_ssi_parent_attach(mod, io);
+
+ return 0;
+}
+
static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
@@ -662,7 +635,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
if (rsnd_ssi_is_multi_slave(mod, io))
return 0;
- rsnd_ssi_parent_attach(mod, io, priv);
+ /*
+ * It can't judge ssi parent at this point
+ * see rsnd_ssi_pcm_new()
+ */
ret = rsnd_ssiu_attach(io, mod);
if (ret < 0)
@@ -683,6 +659,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
+ .irq = rsnd_ssi_irq,
+ .pcm_new = rsnd_ssi_pcm_new,
.hw_params = rsnd_ssi_hw_params,
};
@@ -705,9 +683,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
if (ret)
return ret;
- ssi->dma = rsnd_dma_attach(io, mod, dma_id);
- if (IS_ERR(ssi->dma))
- return PTR_ERR(ssi->dma);
+ /* SSI probe might be called many times in MUX multi path */
+ ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id);
return ret;
}
@@ -772,6 +749,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.quit = rsnd_ssi_quit,
.start = rsnd_ssi_start,
.stop = rsnd_ssi_stop,
+ .irq = rsnd_ssi_irq,
+ .pcm_new = rsnd_ssi_pcm_new,
.fallback = rsnd_ssi_fallback,
.hw_params = rsnd_ssi_hw_params,
};
@@ -858,6 +837,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
}
+static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod,
+ enum rsnd_mod_type type)
+{
+ /*
+ * SSIP (= SSI parent) needs to be special, otherwise,
+ * 2nd SSI might doesn't start. see also rsnd_mod_call()
+ *
+ * We can't include parent SSI status on SSI, because we don't know
+ * how many SSI requests parent SSI. Thus, it is localed on "io" now.
+ * ex) trouble case
+ * Playback: SSI0
+ * Capture : SSI1 (needs SSI0)
+ *
+ * 1) start Capture -> SSI0/SSI1 are started.
+ * 2) start Playback -> SSI0 doesn't work, because it is already
+ * marked as "started" on 1)
+ *
+ * OTOH, using each mod's status is good for MUX case.
+ * It doesn't need to start in 2nd start
+ * ex)
+ * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0
+ * |
+ * IO-1: SRC1 -> CTU2 -+
+ *
+ * 1) start IO-0 -> start SSI0
+ * 2) start IO-1 -> SSI0 doesn't need to start, because it is
+ * already started on 1)
+ */
+ if (type == RSND_MOD_SSIP)
+ return &io->parent_ssi_status;
+
+ return rsnd_mod_get_status(io, mod, type);
+}
+
int rsnd_ssi_probe(struct rsnd_priv *priv)
{
struct device_node *node;
@@ -920,7 +934,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
ops = &rsnd_ssi_dma_ops;
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
- RSND_MOD_SSI, i);
+ rsnd_ssi_get_status, RSND_MOD_SSI, i);
if (ret)
goto rsnd_ssi_probe_done;
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 06d72828e5bc..6f9b388ec5a8 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -27,7 +27,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
struct rsnd_priv *priv)
{
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
- u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
+ u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io);
int use_busif = rsnd_ssi_use_busif(io);
int id = rsnd_mod_id(mod);
u32 mask1, val1;
@@ -105,7 +105,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- if (rsnd_get_slot_width(io) >= 6) {
+ if (rsnd_runtime_is_ssi_tdm(io)) {
/*
* TDM Extend Mode
* see
@@ -115,13 +115,14 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
}
if (rsnd_ssi_use_busif(io)) {
- u32 val = rsnd_get_dalign(mod, io);
-
rsnd_mod_write(mod, SSI_BUSIF_ADINR,
rsnd_get_adinr_bit(mod, io) |
- rsnd_get_adinr_chan(mod, io));
+ (rsnd_io_is_play(io) ?
+ rsnd_runtime_channel_after_ctu(io) :
+ rsnd_runtime_channel_original(io)));
rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
- rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
+ rsnd_mod_write(mod, SSI_BUSIF_DALIGN,
+ rsnd_get_dalign(mod, io));
}
return 0;
@@ -136,7 +137,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SSI_CTRL, 0x1);
- if (rsnd_ssi_multi_slaves(io))
+ if (rsnd_ssi_multi_slaves_runtime(io))
rsnd_mod_write(mod, SSI_CONTROL, 0x1);
return 0;
@@ -151,7 +152,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
rsnd_mod_write(mod, SSI_CTRL, 0);
- if (rsnd_ssi_multi_slaves(io))
+ if (rsnd_ssi_multi_slaves_runtime(io))
rsnd_mod_write(mod, SSI_CONTROL, 0);
return 0;
@@ -206,7 +207,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
for_each_rsnd_ssiu(ssiu, priv, i) {
ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
- ops, NULL, RSND_MOD_SSIU, i);
+ ops, NULL, rsnd_mod_get_status,
+ RSND_MOD_SSIU, i);
if (ret)
return ret;
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 790ee2bf1a47..d2e62b159610 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -986,16 +986,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
- rtd = soc_new_pcm_runtime(card, dai_link);
- if (!rtd)
- return -ENOMEM;
-
if (soc_is_dai_link_bound(card, dai_link)) {
dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
dai_link->name);
return 0;
}
+ rtd = soc_new_pcm_runtime(card, dai_link);
+ if (!rtd)
+ return -ENOMEM;
+
cpu_dai_component.name = dai_link->cpu_name;
cpu_dai_component.of_node = dai_link->cpu_of_node;
cpu_dai_component.dai_name = dai_link->cpu_dai_name;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 581175a51ecf..801ae1a81dfd 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2805,7 +2805,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num)
{
- int i, ret = 0;
+ int i;
mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
for (i = 0; i < num; i++) {
@@ -2814,7 +2814,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
}
mutex_unlock(&dapm->card->dapm_mutex);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 1af4f23697a7..aa99dac31b3b 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1867,18 +1867,6 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
if (!snd_soc_dpcm_be_can_update(fe, be, stream))
continue;
- /* only allow hw_params() if no connected FEs are running */
- if (!snd_soc_dpcm_can_be_params(fe, be, stream))
- continue;
-
- if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
- continue;
-
- dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
- dpcm->fe->dai_link->name);
-
/* copy params for each dpcm */
memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
sizeof(struct snd_pcm_hw_params));
@@ -1895,6 +1883,18 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
}
}
+ /* only allow hw_params() if no connected FEs are running */
+ if (!snd_soc_dpcm_can_be_params(fe, be, stream))
+ continue;
+
+ if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
+ continue;
+
+ dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
+ dpcm->fe->dai_link->name);
+
ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
if (ret < 0) {
dev_err(dpcm->be->dev,
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 6963ba20991c..1cf94d7fb9f4 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -223,51 +223,6 @@ static int get_widget_id(int tplg_type)
return -EINVAL;
}
-static enum snd_soc_dobj_type get_dobj_mixer_type(
- struct snd_soc_tplg_ctl_hdr *control_hdr)
-{
- if (control_hdr == NULL)
- return SND_SOC_DOBJ_NONE;
-
- switch (control_hdr->ops.info) {
- case SND_SOC_TPLG_CTL_VOLSW:
- case SND_SOC_TPLG_CTL_VOLSW_SX:
- case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
- case SND_SOC_TPLG_CTL_RANGE:
- case SND_SOC_TPLG_CTL_STROBE:
- return SND_SOC_DOBJ_MIXER;
- case SND_SOC_TPLG_CTL_ENUM:
- case SND_SOC_TPLG_CTL_ENUM_VALUE:
- return SND_SOC_DOBJ_ENUM;
- case SND_SOC_TPLG_CTL_BYTES:
- return SND_SOC_DOBJ_BYTES;
- default:
- return SND_SOC_DOBJ_NONE;
- }
-}
-
-static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr,
- struct snd_soc_tplg_ctl_hdr *control_hdr)
-{
- switch (hdr->type) {
- case SND_SOC_TPLG_TYPE_MIXER:
- return get_dobj_mixer_type(control_hdr);
- case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
- case SND_SOC_TPLG_TYPE_MANIFEST:
- return SND_SOC_DOBJ_NONE;
- case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
- return SND_SOC_DOBJ_WIDGET;
- case SND_SOC_TPLG_TYPE_DAI_LINK:
- return SND_SOC_DOBJ_DAI_LINK;
- case SND_SOC_TPLG_TYPE_PCM:
- return SND_SOC_DOBJ_PCM;
- case SND_SOC_TPLG_TYPE_CODEC_LINK:
- return SND_SOC_DOBJ_CODEC_LINK;
- default:
- return SND_SOC_DOBJ_NONE;
- }
-}
-
static inline void soc_bind_err(struct soc_tplg *tplg,
struct snd_soc_tplg_ctl_hdr *hdr, int index)
{
@@ -330,12 +285,22 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg,
return 0;
}
-/* pass dynamic FEs configurations to component driver */
-static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg,
- struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai)
+/* pass DAI configurations to component driver for extra intialization */
+static int soc_tplg_dai_load(struct soc_tplg *tplg,
+ struct snd_soc_dai_driver *dai_drv)
+{
+ if (tplg->comp && tplg->ops && tplg->ops->dai_load)
+ return tplg->ops->dai_load(tplg->comp, dai_drv);
+
+ return 0;
+}
+
+/* pass link configurations to component driver for extra intialization */
+static int soc_tplg_dai_link_load(struct soc_tplg *tplg,
+ struct snd_soc_dai_link *link)
{
- if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load)
- return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai);
+ if (tplg->comp && tplg->ops && tplg->ops->link_load)
+ return tplg->ops->link_load(tplg->comp, link);
return 0;
}
@@ -495,18 +460,39 @@ static void remove_widget(struct snd_soc_component *comp,
/* widget w is freed by soc-dapm.c */
}
-/* remove PCM DAI configurations */
-static void remove_pcm_dai(struct snd_soc_component *comp,
+/* remove DAI configurations */
+static void remove_dai(struct snd_soc_component *comp,
+ struct snd_soc_dobj *dobj, int pass)
+{
+ struct snd_soc_dai_driver *dai_drv =
+ container_of(dobj, struct snd_soc_dai_driver, dobj);
+
+ if (pass != SOC_TPLG_PASS_PCM_DAI)
+ return;
+
+ if (dobj->ops && dobj->ops->dai_unload)
+ dobj->ops->dai_unload(comp, dobj);
+
+ list_del(&dobj->list);
+ kfree(dai_drv);
+}
+
+/* remove link configurations */
+static void remove_link(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
+ struct snd_soc_dai_link *link =
+ container_of(dobj, struct snd_soc_dai_link, dobj);
+
if (pass != SOC_TPLG_PASS_PCM_DAI)
return;
- if (dobj->ops && dobj->ops->pcm_dai_unload)
- dobj->ops->pcm_dai_unload(comp, dobj);
+ if (dobj->ops && dobj->ops->link_unload)
+ dobj->ops->link_unload(comp, dobj);
list_del(&dobj->list);
- kfree(dobj);
+ snd_soc_remove_dai_link(comp->card, link);
+ kfree(link);
}
/* bind a kcontrol to it's IO handlers */
@@ -1544,18 +1530,116 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
return 0;
}
-static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
+static void set_stream_info(struct snd_soc_pcm_stream *stream,
+ struct snd_soc_tplg_stream_caps *caps)
+{
+ stream->stream_name = kstrdup(caps->name, GFP_KERNEL);
+ stream->channels_min = caps->channels_min;
+ stream->channels_max = caps->channels_max;
+ stream->rates = caps->rates;
+ stream->rate_min = caps->rate_min;
+ stream->rate_max = caps->rate_max;
+ stream->formats = caps->formats;
+}
+
+static int soc_tplg_dai_create(struct soc_tplg *tplg,
+ struct snd_soc_tplg_pcm *pcm)
+{
+ struct snd_soc_dai_driver *dai_drv;
+ struct snd_soc_pcm_stream *stream;
+ struct snd_soc_tplg_stream_caps *caps;
+ int ret;
+
+ dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
+ if (dai_drv == NULL)
+ return -ENOMEM;
+
+ dai_drv->name = pcm->dai_name;
+ dai_drv->id = pcm->dai_id;
+
+ if (pcm->playback) {
+ stream = &dai_drv->playback;
+ caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
+ set_stream_info(stream, caps);
+ }
+
+ if (pcm->capture) {
+ stream = &dai_drv->capture;
+ caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE];
+ set_stream_info(stream, caps);
+ }
+
+ /* pass control to component driver for optional further init */
+ ret = soc_tplg_dai_load(tplg, dai_drv);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
+ kfree(dai_drv);
+ return ret;
+ }
+
+ dai_drv->dobj.index = tplg->index;
+ dai_drv->dobj.ops = tplg->ops;
+ dai_drv->dobj.type = SND_SOC_DOBJ_PCM;
+ list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list);
+
+ /* register the DAI to the component */
+ return snd_soc_register_dai(tplg->comp, dai_drv);
+}
+
+static int soc_tplg_link_create(struct soc_tplg *tplg,
+ struct snd_soc_tplg_pcm *pcm)
+{
+ struct snd_soc_dai_link *link;
+ int ret;
+
+ link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+ if (link == NULL)
+ return -ENOMEM;
+
+ link->name = pcm->pcm_name;
+ link->stream_name = pcm->pcm_name;
+
+ /* pass control to component driver for optional further init */
+ ret = soc_tplg_dai_link_load(tplg, link);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n");
+ kfree(link);
+ return ret;
+ }
+
+ link->dobj.index = tplg->index;
+ link->dobj.ops = tplg->ops;
+ link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
+ list_add(&link->dobj.list, &tplg->comp->dobj_list);
+
+ snd_soc_add_dai_link(tplg->comp->card, link);
+ return 0;
+}
+
+/* create a FE DAI and DAI link from the PCM object */
+static int soc_tplg_pcm_create(struct soc_tplg *tplg,
+ struct snd_soc_tplg_pcm *pcm)
+{
+ int ret;
+
+ ret = soc_tplg_dai_create(tplg, pcm);
+ if (ret < 0)
+ return ret;
+
+ return soc_tplg_link_create(tplg, pcm);
+}
+
+static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
- struct snd_soc_tplg_pcm_dai *pcm_dai;
- struct snd_soc_dobj *dobj;
+ struct snd_soc_tplg_pcm *pcm;
int count = hdr->count;
- int ret;
+ int i;
if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
return 0;
- pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos;
+ pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
if (soc_tplg_check_elem_count(tplg,
sizeof(struct snd_soc_tplg_pcm), count,
@@ -1565,31 +1649,16 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
return -EINVAL;
}
+ /* create the FE DAIs and DAI links */
+ for (i = 0; i < count; i++) {
+ soc_tplg_pcm_create(tplg, pcm);
+ pcm++;
+ }
+
dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count;
- dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL);
- if (dobj == NULL)
- return -ENOMEM;
-
- /* Call the platform driver call back to register the dais */
- ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count);
- if (ret < 0) {
- dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n");
- goto err;
- }
-
- dobj->type = get_dobj_type(hdr, NULL);
- dobj->pcm_dai.count = count;
- dobj->pcm_dai.pd = pcm_dai;
- dobj->ops = tplg->ops;
- dobj->index = tplg->index;
- list_add(&dobj->list, &tplg->comp->dobj_list);
return 0;
-
-err:
- kfree(dobj);
- return ret;
}
static int soc_tplg_manifest_load(struct soc_tplg *tplg,
@@ -1681,9 +1750,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
return soc_tplg_dapm_widget_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_PCM:
- case SND_SOC_TPLG_TYPE_DAI_LINK:
- case SND_SOC_TPLG_TYPE_CODEC_LINK:
- return soc_tplg_pcm_dai_elems_load(tplg, hdr);
+ return soc_tplg_pcm_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_MANIFEST:
return soc_tplg_manifest_load(tplg, hdr);
default:
@@ -1841,9 +1908,10 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index)
remove_widget(comp, dobj, pass);
break;
case SND_SOC_DOBJ_PCM:
+ remove_dai(comp, dobj, pass);
+ break;
case SND_SOC_DOBJ_DAI_LINK:
- case SND_SOC_DOBJ_CODEC_LINK:
- remove_pcm_dai(comp, dobj, pass);
+ remove_link(comp, dobj, pass);
break;
default:
dev_err(comp->dev, "ASoC: invalid component type %d for removal\n",
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
index 84c72ec6ad73..ae42294ef688 100644
--- a/sound/soc/sunxi/Kconfig
+++ b/sound/soc/sunxi/Kconfig
@@ -8,4 +8,12 @@ config SND_SUN4I_CODEC
Select Y or M to add support for the Codec embedded in the Allwinner
A10 and affiliated SoCs.
+config SND_SUN4I_SPDIF
+ tristate "Allwinner A10 SPDIF Support"
+ depends on OF
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M to add support for the S/PDIF audio block in the Allwinner
+ A10 and affiliated SoCs.
endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
index ea8a08c881d6..8f5e889667f1 100644
--- a/sound/soc/sunxi/Makefile
+++ b/sound/soc/sunxi/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
new file mode 100644
index 000000000000..0b04fb02125c
--- /dev/null
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -0,0 +1,550 @@
+/*
+ * ALSA SoC SPDIF Audio Layer
+ *
+ * Copyright 2015 Andrea Venturi <be17068@iperbole.bo.it>
+ * Copyright 2015 Marcus Cooper <codekipper@gmail.com>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define SUN4I_SPDIF_CTL (0x00)
+ #define SUN4I_SPDIF_CTL_MCLKDIV(v) ((v) << 4) /* v even */
+ #define SUN4I_SPDIF_CTL_MCLKOUTEN BIT(2)
+ #define SUN4I_SPDIF_CTL_GEN BIT(1)
+ #define SUN4I_SPDIF_CTL_RESET BIT(0)
+
+#define SUN4I_SPDIF_TXCFG (0x04)
+ #define SUN4I_SPDIF_TXCFG_SINGLEMOD BIT(31)
+ #define SUN4I_SPDIF_TXCFG_ASS BIT(17)
+ #define SUN4I_SPDIF_TXCFG_NONAUDIO BIT(16)
+ #define SUN4I_SPDIF_TXCFG_TXRATIO(v) ((v) << 4)
+ #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK GENMASK(8, 4)
+ #define SUN4I_SPDIF_TXCFG_FMTRVD GENMASK(3, 2)
+ #define SUN4I_SPDIF_TXCFG_FMT16BIT (0 << 2)
+ #define SUN4I_SPDIF_TXCFG_FMT20BIT (1 << 2)
+ #define SUN4I_SPDIF_TXCFG_FMT24BIT (2 << 2)
+ #define SUN4I_SPDIF_TXCFG_CHSTMODE BIT(1)
+ #define SUN4I_SPDIF_TXCFG_TXEN BIT(0)
+
+#define SUN4I_SPDIF_RXCFG (0x08)
+ #define SUN4I_SPDIF_RXCFG_LOCKFLAG BIT(4)
+ #define SUN4I_SPDIF_RXCFG_CHSTSRC BIT(3)
+ #define SUN4I_SPDIF_RXCFG_CHSTCP BIT(1)
+ #define SUN4I_SPDIF_RXCFG_RXEN BIT(0)
+
+#define SUN4I_SPDIF_TXFIFO (0x0C)
+
+#define SUN4I_SPDIF_RXFIFO (0x10)
+
+#define SUN4I_SPDIF_FCTL (0x14)
+ #define SUN4I_SPDIF_FCTL_FIFOSRC BIT(31)
+ #define SUN4I_SPDIF_FCTL_FTX BIT(17)
+ #define SUN4I_SPDIF_FCTL_FRX BIT(16)
+ #define SUN4I_SPDIF_FCTL_TXTL(v) ((v) << 8)
+ #define SUN4I_SPDIF_FCTL_TXTL_MASK GENMASK(12, 8)
+ #define SUN4I_SPDIF_FCTL_RXTL(v) ((v) << 3)
+ #define SUN4I_SPDIF_FCTL_RXTL_MASK GENMASK(7, 3)
+ #define SUN4I_SPDIF_FCTL_TXIM BIT(2)
+ #define SUN4I_SPDIF_FCTL_RXOM(v) ((v) << 0)
+ #define SUN4I_SPDIF_FCTL_RXOM_MASK GENMASK(1, 0)
+
+#define SUN4I_SPDIF_FSTA (0x18)
+ #define SUN4I_SPDIF_FSTA_TXE BIT(14)
+ #define SUN4I_SPDIF_FSTA_TXECNTSHT (8)
+ #define SUN4I_SPDIF_FSTA_RXA BIT(6)
+ #define SUN4I_SPDIF_FSTA_RXACNTSHT (0)
+
+#define SUN4I_SPDIF_INT (0x1C)
+ #define SUN4I_SPDIF_INT_RXLOCKEN BIT(18)
+ #define SUN4I_SPDIF_INT_RXUNLOCKEN BIT(17)
+ #define SUN4I_SPDIF_INT_RXPARERREN BIT(16)
+ #define SUN4I_SPDIF_INT_TXDRQEN BIT(7)
+ #define SUN4I_SPDIF_INT_TXUIEN BIT(6)
+ #define SUN4I_SPDIF_INT_TXOIEN BIT(5)
+ #define SUN4I_SPDIF_INT_TXEIEN BIT(4)
+ #define SUN4I_SPDIF_INT_RXDRQEN BIT(2)
+ #define SUN4I_SPDIF_INT_RXOIEN BIT(1)
+ #define SUN4I_SPDIF_INT_RXAIEN BIT(0)
+
+#define SUN4I_SPDIF_ISTA (0x20)
+ #define SUN4I_SPDIF_ISTA_RXLOCKSTA BIT(18)
+ #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA BIT(17)
+ #define SUN4I_SPDIF_ISTA_RXPARERRSTA BIT(16)
+ #define SUN4I_SPDIF_ISTA_TXUSTA BIT(6)
+ #define SUN4I_SPDIF_ISTA_TXOSTA BIT(5)
+ #define SUN4I_SPDIF_ISTA_TXESTA BIT(4)
+ #define SUN4I_SPDIF_ISTA_RXOSTA BIT(1)
+ #define SUN4I_SPDIF_ISTA_RXASTA BIT(0)
+
+#define SUN4I_SPDIF_TXCNT (0x24)
+
+#define SUN4I_SPDIF_RXCNT (0x28)
+
+#define SUN4I_SPDIF_TXCHSTA0 (0x2C)
+ #define SUN4I_SPDIF_TXCHSTA0_CLK(v) ((v) << 28)
+ #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v) ((v) << 24)
+ #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK GENMASK(27, 24)
+ #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v) ((v) << 20)
+ #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK GENMASK(23, 20)
+ #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v) ((v) << 16)
+ #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v) ((v) << 8)
+ #define SUN4I_SPDIF_TXCHSTA0_MODE(v) ((v) << 6)
+ #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v) ((v) << 3)
+ #define SUN4I_SPDIF_TXCHSTA0_CP BIT(2)
+ #define SUN4I_SPDIF_TXCHSTA0_AUDIO BIT(1)
+ #define SUN4I_SPDIF_TXCHSTA0_PRO BIT(0)
+
+#define SUN4I_SPDIF_TXCHSTA1 (0x30)
+ #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v) ((v) << 8)
+ #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v) << 4)
+ #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK GENMASK(7, 4)
+ #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v) << 1)
+ #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN BIT(0)
+
+#define SUN4I_SPDIF_RXCHSTA0 (0x34)
+ #define SUN4I_SPDIF_RXCHSTA0_CLK(v) ((v) << 28)
+ #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v) ((v) << 24)
+ #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v) ((v) << 20)
+ #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v) ((v) << 16)
+ #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v) ((v) << 8)
+ #define SUN4I_SPDIF_RXCHSTA0_MODE(v) ((v) << 6)
+ #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v) ((v) << 3)
+ #define SUN4I_SPDIF_RXCHSTA0_CP BIT(2)
+ #define SUN4I_SPDIF_RXCHSTA0_AUDIO BIT(1)
+ #define SUN4I_SPDIF_RXCHSTA0_PRO BIT(0)
+
+#define SUN4I_SPDIF_RXCHSTA1 (0x38)
+ #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v) ((v) << 8)
+ #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v) << 4)
+ #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v) << 1)
+ #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN BIT(0)
+
+/* Defines for Sampling Frequency */
+#define SUN4I_SPDIF_SAMFREQ_44_1KHZ 0x0
+#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED 0x1
+#define SUN4I_SPDIF_SAMFREQ_48KHZ 0x2
+#define SUN4I_SPDIF_SAMFREQ_32KHZ 0x3
+#define SUN4I_SPDIF_SAMFREQ_22_05KHZ 0x4
+#define SUN4I_SPDIF_SAMFREQ_24KHZ 0x6
+#define SUN4I_SPDIF_SAMFREQ_88_2KHZ 0x8
+#define SUN4I_SPDIF_SAMFREQ_76_8KHZ 0x9
+#define SUN4I_SPDIF_SAMFREQ_96KHZ 0xa
+#define SUN4I_SPDIF_SAMFREQ_176_4KHZ 0xc
+#define SUN4I_SPDIF_SAMFREQ_192KHZ 0xe
+
+struct sun4i_spdif_dev {
+ struct platform_device *pdev;
+ struct clk *spdif_clk;
+ struct clk *apb_clk;
+ struct snd_soc_dai_driver cpu_dai_drv;
+ struct regmap *regmap;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+};
+
+static void sun4i_spdif_configure(struct sun4i_spdif_dev *host)
+{
+ /* soft reset SPDIF */
+ regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET);
+
+ /* flush TX FIFO */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
+ SUN4I_SPDIF_FCTL_FTX, SUN4I_SPDIF_FCTL_FTX);
+
+ /* clear TX counter */
+ regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0);
+}
+
+static void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream,
+ struct sun4i_spdif_dev *host)
+{
+ if (substream->runtime->channels == 1)
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
+ SUN4I_SPDIF_TXCFG_SINGLEMOD,
+ SUN4I_SPDIF_TXCFG_SINGLEMOD);
+
+ /* SPDIF TX ENABLE */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
+ SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN);
+
+ /* DRQ ENABLE */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
+ SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN);
+
+ /* Global enable */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
+ SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN);
+}
+
+static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream,
+ struct sun4i_spdif_dev *host)
+{
+ /* SPDIF TX DISABLE */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
+ SUN4I_SPDIF_TXCFG_TXEN, 0);
+
+ /* DRQ DISABLE */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
+ SUN4I_SPDIF_INT_TXDRQEN, 0);
+
+ /* Global disable */
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
+ SUN4I_SPDIF_CTL_GEN, 0);
+}
+
+static int sun4i_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+
+ sun4i_spdif_configure(host);
+
+ return 0;
+}
+
+static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ int ret = 0;
+ int fmt;
+ unsigned long rate = params_rate(params);
+ u32 mclk_div = 0;
+ unsigned int mclk = 0;
+ u32 reg_val;
+ struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
+ struct platform_device *pdev = host->pdev;
+
+ /* Add the PCM and raw data select interface */
+ switch (params_channels(params)) {
+ case 1: /* PCM mode */
+ case 2:
+ fmt = 0;
+ break;
+ case 4: /* raw data mode */
+ fmt = SUN4I_SPDIF_TXCFG_NONAUDIO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rate) {
+ case 22050:
+ case 44100:
+ case 88200:
+ case 176400:
+ mclk = 22579200;
+ break;
+ case 24000:
+ case 32000:
+ case 48000:
+ case 96000:
+ case 192000:
+ mclk = 24576000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(host->spdif_clk, mclk);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Setting SPDIF clock rate for %d Hz failed!\n", mclk);
+ return ret;
+ }
+
+ regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
+ SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM);
+
+ switch (rate) {
+ case 22050:
+ case 24000:
+ mclk_div = 8;
+ break;
+ case 32000:
+ mclk_div = 6;
+ break;
+ case 44100:
+ case 48000:
+ mclk_div = 4;
+ break;
+ case 88200:
+ case 96000:
+ mclk_div = 2;
+ break;
+ case 176400:
+ case 192000:
+ mclk_div = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg_val = 0;
+ reg_val |= SUN4I_SPDIF_TXCFG_ASS;
+ reg_val |= fmt; /* set non audio and bit depth */
+ reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE;
+ reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1);
+ regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val);
+
+ return 0;
+}
+
+static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+ struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ sun4i_snd_txctrl_on(substream, host);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sun4i_snd_txctrl_off(substream, host);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = {
+ .startup = sun4i_spdif_startup,
+ .trigger = sun4i_spdif_trigger,
+ .hw_params = sun4i_spdif_hw_params,
+};
+
+static const struct regmap_config sun4i_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN4I_SPDIF_RXCHSTA1,
+};
+
+#define SUN4I_RATES SNDRV_PCM_RATE_8000_192000
+
+#define SUN4I_FORMATS (SNDRV_PCM_FORMAT_S16_LE | \
+ SNDRV_PCM_FORMAT_S20_3LE | \
+ SNDRV_PCM_FORMAT_S24_LE)
+
+static struct snd_soc_dai_driver sun4i_spdif_dai = {
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SUN4I_RATES,
+ .formats = SUN4I_FORMATS,
+ },
+ .probe = sun4i_spdif_soc_dai_probe,
+ .ops = &sun4i_spdif_dai_ops,
+ .name = "spdif",
+};
+
+static const struct snd_soc_dapm_widget dit_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("spdif-out"),
+};
+
+static const struct snd_soc_dapm_route dit_routes[] = {
+ { "spdif-out", NULL, "Playback" },
+};
+
+static const struct of_device_id sun4i_spdif_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-spdif", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
+
+static const struct snd_soc_component_driver sun4i_spdif_component = {
+ .name = "sun4i-spdif",
+};
+
+static int sun4i_spdif_runtime_suspend(struct device *dev)
+{
+ struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(host->spdif_clk);
+ clk_disable_unprepare(host->apb_clk);
+
+ return 0;
+}
+
+static int sun4i_spdif_runtime_resume(struct device *dev)
+{
+ struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
+
+ clk_prepare_enable(host->spdif_clk);
+ clk_prepare_enable(host->apb_clk);
+
+ return 0;
+}
+
+static int sun4i_spdif_probe(struct platform_device *pdev)
+{
+ struct sun4i_spdif_dev *host;
+ struct resource *res;
+ int ret;
+ void __iomem *base;
+
+ dev_dbg(&pdev->dev, "Entered %s\n", __func__);
+
+ host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ host->pdev = pdev;
+
+ /* Initialize this copy of the CPU DAI driver structure */
+ memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai));
+ host->cpu_dai_drv.name = dev_name(&pdev->dev);
+
+ /* Get the addresses */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sun4i_spdif_regmap_config);
+
+ /* Clocks */
+ host->apb_clk = devm_clk_get(&pdev->dev, "apb");
+ if (IS_ERR(host->apb_clk)) {
+ dev_err(&pdev->dev, "failed to get a apb clock.\n");
+ return PTR_ERR(host->apb_clk);
+ }
+
+ host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
+ if (IS_ERR(host->spdif_clk)) {
+ dev_err(&pdev->dev, "failed to get a spdif clock.\n");
+ ret = PTR_ERR(host->spdif_clk);
+ goto err_disable_apb_clk;
+ }
+
+ host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
+ host->dma_params_tx.maxburst = 4;
+ host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ platform_set_drvdata(pdev, host);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &sun4i_spdif_component, &sun4i_spdif_dai, 1);
+ if (ret)
+ goto err_disable_apb_clk;
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = sun4i_spdif_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_unregister;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ goto err_suspend;
+ return 0;
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ sun4i_spdif_runtime_suspend(&pdev->dev);
+err_unregister:
+ pm_runtime_disable(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
+err_disable_apb_clk:
+ clk_disable_unprepare(host->apb_clk);
+ return ret;
+}
+
+static int sun4i_spdif_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ sun4i_spdif_runtime_suspend(&pdev->dev);
+
+ snd_soc_unregister_platform(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops sun4i_spdif_pm = {
+ SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend,
+ sun4i_spdif_runtime_resume, NULL)
+};
+
+static struct platform_driver sun4i_spdif_driver = {
+ .driver = {
+ .name = "sun4i-spdif",
+ .of_match_table = of_match_ptr(sun4i_spdif_of_match),
+ .pm = &sun4i_spdif_pm,
+ },
+ .probe = sun4i_spdif_probe,
+ .remove = sun4i_spdif_remove,
+};
+
+module_platform_driver(sun4i_spdif_driver);
+
+MODULE_AUTHOR("Marcus Cooper <codekipper@gmail.com>");
+MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>");
+MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sun4i-spdif");
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 258cf7015ce2..63244bbba8c7 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -83,6 +83,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
static bool ignore_ctl_error;
static bool autoclock = true;
+static char *quirk_alias[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -101,6 +102,8 @@ MODULE_PARM_DESC(ignore_ctl_error,
"Ignore errors from USB controller for mixer interfaces.");
module_param(autoclock, bool, 0444);
MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
+module_param_array(quirk_alias, charp, NULL, 0444);
+MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
/*
* we keep the snd_usb_audio_t instances by ourselves for merging
@@ -172,8 +175,9 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
- int err = snd_usbmidi_create(chip->card, iface,
- &chip->midi_list, NULL);
+ int err = __snd_usbmidi_create(chip->card, iface,
+ &chip->midi_list, NULL,
+ chip->usb_id);
if (err < 0) {
dev_err(&dev->dev,
"%u:%d: cannot create sequencer device\n",
@@ -312,6 +316,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
snd_usb_endpoint_free(ep);
mutex_destroy(&chip->mutex);
+ dev_set_drvdata(&chip->dev->dev, NULL);
kfree(chip);
return 0;
}
@@ -456,6 +461,48 @@ static int snd_usb_audio_create(struct usb_interface *intf,
return 0;
}
+/* look for a matching quirk alias id */
+static bool get_alias_id(struct usb_device *dev, unsigned int *id)
+{
+ int i;
+ unsigned int src, dst;
+
+ for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) {
+ if (!quirk_alias[i] ||
+ sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 ||
+ src != *id)
+ continue;
+ dev_info(&dev->dev,
+ "device (%04x:%04x): applying quirk alias %04x:%04x\n",
+ USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id),
+ USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst));
+ *id = dst;
+ return true;
+ }
+
+ return false;
+}
+
+static struct usb_device_id usb_audio_ids[]; /* defined below */
+
+/* look for the corresponding quirk */
+static const struct snd_usb_audio_quirk *
+get_alias_quirk(struct usb_device *dev, unsigned int id)
+{
+ const struct usb_device_id *p;
+
+ for (p = usb_audio_ids; p->match_flags; p++) {
+ /* FIXME: this checks only vendor:product pair in the list */
+ if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) ==
+ USB_DEVICE_ID_MATCH_DEVICE &&
+ p->idVendor == USB_ID_VENDOR(id) &&
+ p->idProduct == USB_ID_PRODUCT(id))
+ return (const struct snd_usb_audio_quirk *)p->driver_info;
+ }
+
+ return NULL;
+}
+
/*
* probe the active usb device
*
@@ -482,10 +529,12 @@ static int usb_audio_probe(struct usb_interface *intf,
ifnum = get_iface_desc(alts)->bInterfaceNumber;
id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
+ if (get_alias_id(dev, &id))
+ quirk = get_alias_quirk(dev, id);
if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
return -ENXIO;
- err = snd_usb_apply_boot_quirk(dev, intf, quirk);
+ err = snd_usb_apply_boot_quirk(dev, intf, quirk, id);
if (err < 0)
return err;
@@ -504,6 +553,7 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error;
}
chip = usb_chip[i];
+ dev_set_drvdata(&dev->dev, chip);
atomic_inc(&chip->active); /* avoid autopm */
break;
}
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 2ed260b10f6d..7ccbcaf6a147 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -285,6 +285,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
unsigned char data[3];
int err, crate;
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
/* if endpoint doesn't have sampling rate control, bail out */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7b1cb365ffab..c07a7eda42a2 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -438,6 +438,9 @@ exit_clear:
*
* New endpoints will be added to chip->ep_list and must be freed by
* calling snd_usb_endpoint_free().
+ *
+ * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
+ * bNumEndpoints > 1 beforehand.
*/
struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
struct usb_host_interface *alts,
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 007cf5831121..47de8af42f16 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -2320,10 +2320,11 @@ EXPORT_SYMBOL(snd_usbmidi_resume);
/*
* Creates and registers everything needed for a MIDI streaming interface.
*/
-int snd_usbmidi_create(struct snd_card *card,
- struct usb_interface *iface,
- struct list_head *midi_list,
- const struct snd_usb_audio_quirk *quirk)
+int __snd_usbmidi_create(struct snd_card *card,
+ struct usb_interface *iface,
+ struct list_head *midi_list,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id)
{
struct snd_usb_midi *umidi;
struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS];
@@ -2341,8 +2342,10 @@ int snd_usbmidi_create(struct snd_card *card,
spin_lock_init(&umidi->disc_lock);
init_rwsem(&umidi->disc_rwsem);
mutex_init(&umidi->mutex);
- umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
+ if (!usb_id)
+ usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
le16_to_cpu(umidi->dev->descriptor.idProduct));
+ umidi->usb_id = usb_id;
setup_timer(&umidi->error_timer, snd_usbmidi_error_timer,
(unsigned long)umidi);
@@ -2463,4 +2466,4 @@ int snd_usbmidi_create(struct snd_card *card,
list_add_tail(&umidi->list, midi_list);
return 0;
}
-EXPORT_SYMBOL(snd_usbmidi_create);
+EXPORT_SYMBOL(__snd_usbmidi_create);
diff --git a/sound/usb/midi.h b/sound/usb/midi.h
index ad8a3211f8e7..5e25a3fd6c1d 100644
--- a/sound/usb/midi.h
+++ b/sound/usb/midi.h
@@ -39,10 +39,20 @@ struct snd_usb_midi_endpoint_info {
/* for QUIRK_MIDI_AKAI, data is NULL */
-int snd_usbmidi_create(struct snd_card *card,
+int __snd_usbmidi_create(struct snd_card *card,
+ struct usb_interface *iface,
+ struct list_head *midi_list,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id);
+
+static inline int snd_usbmidi_create(struct snd_card *card,
struct usb_interface *iface,
struct list_head *midi_list,
- const struct snd_usb_audio_quirk *quirk);
+ const struct snd_usb_audio_quirk *quirk)
+{
+ return __snd_usbmidi_create(card, iface, midi_list, quirk, 0);
+}
+
void snd_usbmidi_input_stop(struct list_head *p);
void snd_usbmidi_input_start(struct list_head *p);
void snd_usbmidi_disconnect(struct list_head *p);
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 279025650568..f6c3bf79af9a 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1519,7 +1519,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
/* use known values for that card: interface#1 altsetting#1 */
iface = usb_ifnum_to_if(chip->dev, 1);
+ if (!iface || iface->num_altsetting < 2)
+ return -EINVAL;
alts = &iface->altsetting[1];
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
err = snd_usb_ctl_msg(chip->dev,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index b0370d5f33f8..0e4e0640c504 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -160,6 +160,8 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
unsigned char data[1];
int err;
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
data[0] = 1;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c458d60d5030..a889d433cbdf 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -167,19 +167,20 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
- if (err < 0) {
- kfree(fp);
- kfree(rate_table);
- return err;
- }
+ if (err < 0)
+ goto error;
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
- kfree(fp);
- kfree(rate_table);
- return -EINVAL;
+ err = -EINVAL;
+ goto error;
}
alts = &iface->altsetting[fp->altset_idx];
altsd = get_iface_desc(alts);
+ if (altsd->bNumEndpoints < 1) {
+ err = -EINVAL;
+ goto error;
+ }
+
fp->protocol = altsd->bInterfaceProtocol;
if (fp->datainterval == 0)
@@ -190,6 +191,11 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
snd_usb_init_pitch(chip, fp->iface, alts, fp);
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
return 0;
+
+ error:
+ kfree(fp);
+ kfree(rate_table);
+ return err;
}
static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
@@ -446,8 +452,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
const struct snd_usb_audio_quirk *quirk =
chip->usb_id == USB_ID(0x0582, 0x002b)
? &ua700_quirk : &uaxx_quirk;
- return snd_usbmidi_create(chip->card, iface,
- &chip->midi_list, quirk);
+ return __snd_usbmidi_create(chip->card, iface,
+ &chip->midi_list, quirk,
+ chip->usb_id);
}
if (altsd->bNumEndpoints != 1)
@@ -974,11 +981,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int snd_usb_apply_boot_quirk(struct usb_device *dev,
struct usb_interface *intf,
- const struct snd_usb_audio_quirk *quirk)
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int id)
{
- u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
switch (id) {
case USB_ID(0x041e, 0x3000):
/* SB Extigy needs special boot-up sequence */
@@ -1184,7 +1189,7 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
* "Playback Design" products send bogus feedback data at the start
* of the stream. Ignore them.
*/
- if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+ if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba &&
ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
ep->skip_packets = 4;
@@ -1203,11 +1208,15 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
void snd_usb_set_interface_quirk(struct usb_device *dev)
{
+ struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+ if (!chip)
+ return;
/*
* "Playback Design" products need a 50ms delay after setting the
* USB interface.
*/
- switch (le16_to_cpu(dev->descriptor.idVendor)) {
+ switch (USB_ID_VENDOR(chip->usb_id)) {
case 0x23ba: /* Playback Design */
case 0x0644: /* TEAC Corp. */
mdelay(50);
@@ -1215,15 +1224,20 @@ void snd_usb_set_interface_quirk(struct usb_device *dev)
}
}
+/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */
void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value,
__u16 index, void *data, __u16 size)
{
+ struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+ if (!chip)
+ return;
/*
* "Playback Design" products need a 20ms delay after each
* class compliant request
*/
- if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+ if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
@@ -1231,23 +1245,21 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
* "TEAC Corp." products need a 20ms delay after each
* class compliant request
*/
- if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) &&
+ if (USB_ID_VENDOR(chip->usb_id) == 0x0644 &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
/* Marantz/Denon devices with USB DAC functionality need a delay
* after each class compliant request
*/
- if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct)))
+ if (is_marantz_denon_dac(chip->usb_id)
&& (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
/* Zoom R16/24 needs a tiny delay here, otherwise requests like
* get/set frequency return as failed despite actually succeeding.
*/
- if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) &&
- (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) &&
+ if (chip->usb_id == USB_ID(0x1686, 0x00dd) &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(1);
}
@@ -1264,7 +1276,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
unsigned int sample_bytes)
{
/* Playback Designs */
- if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) {
+ if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
switch (fp->altsetting) {
case 1:
fp->dsd_dop = true;
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index 2cd71ed1201f..192ff5ce9452 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -16,7 +16,8 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int snd_usb_apply_boot_quirk(struct usb_device *dev,
struct usb_interface *intf,
- const struct snd_usb_audio_quirk *quirk);
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id);
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt);