From 5949dae3a4512fa1ca61eac947c349442acc12b0 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Thu, 16 Feb 2017 19:05:04 +0800 Subject: ASoC: zx-tdm: add bindings doc for zte's tdm controller This patch adds dt-binding documentation for zte's tdm controller. Signed-off-by: Baoyou Xie Reviewed-by: Shawn Guo Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/zte,tdm.txt | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/zte,tdm.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/zte,tdm.txt b/Documentation/devicetree/bindings/sound/zte,tdm.txt new file mode 100644 index 000000000000..2a07ca655264 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/zte,tdm.txt @@ -0,0 +1,30 @@ +ZTE TDM DAI driver + +Required properties: + +- compatible : should be one of the following. + * zte,zx296718-tdm +- reg : physical base address of the controller and length of memory mapped + region. +- clocks : Pairs of phandle and specifier referencing the controller's clocks. +- clock-names: "wclk" for the wclk. + "pclk" for the pclk. +-#clock-cells: should be 1. +- zte,tdm-dma-sysctrl : Reference to the sysctrl controller controlling + the dma. includes: + phandle of sysctrl. + register offset in sysctrl for control dma. + mask of the register that be written to sysctrl. + +Example: + + tdm: tdm@1487000 { + compatible = "zte,zx296718-tdm"; + reg = <0x01487000 0x1000>; + clocks = <&audiocrm AUDIO_TDM_WCLK>, <&audiocrm AUDIO_TDM_PCLK>; + clock-names = "wclk", "pclk"; + #clock-cells = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&tdm_global_pin>; + zte,tdm-dma-sysctrl = <&sysctrl 0x10c 4>; + }; -- cgit v1.2.3 From 59c15fcd7b8f3e85baba167a3b7c0cbc692ebd90 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 27 Feb 2017 16:47:24 +0100 Subject: ASoC: es7134: add dt-bindings for the es7134 dac Signed-off-by: Jerome Brunet Acked-by: Rob Herring Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/everest,es7134.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/everest,es7134.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/everest,es7134.txt b/Documentation/devicetree/bindings/sound/everest,es7134.txt new file mode 100644 index 000000000000..5495a3cb8b7b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/everest,es7134.txt @@ -0,0 +1,10 @@ +ES7134 i2s DA converter + +Required properties: +- compatible : "everest,es7134" or "everest,es7144" + +Example: + +i2s_codec: external-codec { + compatible = "everest,es7134"; +}; -- cgit v1.2.3 From ed864461bfbdacefc242d4c0675989915a1431b0 Mon Sep 17 00:00:00 2001 From: Brian Austin Date: Mon, 6 Mar 2017 08:08:00 -0600 Subject: ASoC: cs35l35: Add bindings file for CS35L35 Add bindings documentation for CS35L35 Amplifier Signed-off-by: Brian Austin Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cs35l35.txt | 171 +++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/cs35l35.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt new file mode 100644 index 000000000000..07d9c0f177fb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs35l35.txt @@ -0,0 +1,171 @@ +CS35L35 Boosted Speaker Amplifier + +Required properties: + + - compatible : "cirrus,cs35l35" + + - reg : the I2C address of the device for I2C + + - VA-supply, VP-supply : power supplies for the device, + as covered in + Documentation/devicetree/bindings/regulator/regulator.txt. + + - interrupt-parent : Specifies the phandle of the interrupt controller to + which the IRQs from CS35L35 are delivered to. + - interrupts : IRQ line info CS35L35. + (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + for further information relating to interrupt properties) + +Optional properties: + - reset-gpios : gpio used to reset the amplifier + + - cirrus,stereo-config : Boolean to determine if there are 2 AMPs for a + Stereo configuration + + - cirrus,audio-channel : Set Location of Audio Signal on Serial Port + 0 = Data Packet received on Left I2S Channel + 1 = Data Packet received on Right I2S Channel + + - cirrus,advisory-channel : Set Location of Advisory Signal on Serial Port + 0 = Data Packet received on Left I2S Channel + 1 = Data Packet received on Right I2S Channel + + - cirrus,shared-boost : Boolean to enable ClassH tracking of Advisory Signal + if 2 Devices share Boost BST_CTL + + - cirrus,sp-drv-strength : Value for setting the Serial Port drive strength + Table 3-10 of the datasheet lists drive-strength specifications + 0 = 1x (Default) + 1 = .5x + + - cirrus,bst-pdn-fet-on : Boolean to determine if the Boost PDN control + powers down with a rectification FET On or Off. If VSPK is supplied + externally then FET is off. + + - cirrus,boost-ctl-millivolt : Boost Voltage Value. Configures the boost + converter's output voltage in mV. The range is from 2600mV to 9000mV with + increments of 100mV. + (Default) VP + + - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. + Configures the peak current by monitoring the current through the boost FET. + Range starts at 1680mA and goes to a maximum of 4480mA with increments of + 110mA. + (Default) 2.46 Amps + + - cirrus,amp-gain-zc : Boolean to determine if to use Amplifier gain-change + zero-cross + +Optional H/G Algorithm sub-node: + + The cs35l35 node can have a single "cirrus,classh-internal-algo" sub-node + that will disable automatic control of the internal H/G Algorithm. + + It is strongly recommended that the Datasheet be referenced when adjusting + or using these Class H Algorithm controls over the internal Algorithm. + Serious damage can occur to the Device and surrounding components. + + - cirrus,classh-internal-algo : Sub-node for the Internal Class H Algorithm + See Section 4.3 Internal Class H Algorithm in the Datasheet. + If not used, the device manages the ClassH Algorithm internally. + +Optional properties for the "cirrus,classh-internal-algo" Sub-node + + Section 7.29 Class H Control + - cirrus,classh-bst-overide : Boolean + - cirrus,classh-bst-max-limit + - cirrus,classh-mem-depth + + Section 7.30 Class H Headroom Control + - cirrus,classh-headroom + + Section 7.31 Class H Release Rate + - cirrus,classh-release-rate + + Section 7.32 Class H Weak FET Drive Control + - cirrus,classh-wk-fet-disable + - cirrus,classh-wk-fet-delay + - cirrus,classh-wk-fet-thld + + Section 7.34 Class H VP Control + - cirrus,classh-vpch-auto + - cirrus,classh-vpch-rate + - cirrus,classh-vpch-man + +Optional Monitor Signal Format sub-node: + + The cs35l35 node can have a single "cirrus,monitor-signal-format" sub-node + for adjusting the Depth, Location and Frame of the Monitoring Signals + for Algorithms. + + See Sections 4.8.2 through 4.8.4 Serial-Port Control in the Datasheet + + -cirrus,monitor-signal-format : Sub-node for the Monitor Signaling Formating + on the I2S Port. Each of the 3 8 bit values in the array contain the settings + for depth, location, and frame. + + If not used, the defaults for the 6 monitor signals is used. + + Sections 7.44 - 7.53 lists values for the depth, location, and frame + for each monitoring signal. + + - cirrus,imon : 3 8 bit values to set the depth, location, and frame + of the IMON monitor signal. + + - cirrus,vmon : 3 8 bit values to set the depth, location, and frame + of the VMON monitor signal. + + - cirrus,vpmon : 3 8 bit values to set the depth, location, and frame + of the VPMON monitor signal. + + - cirrus,vbstmon : 3 8 bit values to set the depth, location, and frame + of the VBSTMON monitor signal + + - cirrus,vpbrstat : 3 8 bit values to set the depth, location, and frame + of the VPBRSTAT monitor signal + + - cirrus,zerofill : 3 8 bit values to set the depth, location, and frame\ + of the ZEROFILL packet in the monitor signal + +Example: + +cs35l35: cs35l35@20 { + compatible = "cirrus,cs35l35"; + reg = <0x20>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + reset-gpios = <&axi_gpio 54 0>; + interrupt-parent = <&gpio8>; + interrupts = <3 IRQ_TYPE_LEVEL_LOW>; + cirrus,boost-ctl-millivolt = <9000>; + + cirrus,stereo-config; + cirrus,audio-channel = <0x00>; + cirrus,advisory-channel = <0x01>; + cirrus,shared-boost; + + cirrus,classh-internal-algo { + cirrus,classh-bst-overide; + cirrus,classh-bst-max-limit = <0x01>; + cirrus,classh-mem-depth = <0x01>; + cirrus,classh-release-rate = <0x08>; + cirrus,classh-headroom-millivolt = <0x0B>; + cirrus,classh-wk-fet-disable = <0x01>; + cirrus,classh-wk-fet-delay = <0x04>; + cirrus,classh-wk-fet-thld = <0x01>; + cirrus,classh-vpch-auto = <0x01>; + cirrus,classh-vpch-rate = <0x02>; + cirrus,classh-vpch-man = <0x05>; + }; + + /* Depth, Location, Frame */ + cirrus,monitor-signal-format { + cirrus,imon = /bits/ 8 <0x03 0x00 0x01>; + cirrus,vmon = /bits/ 8 <0x03 0x00 0x00>; + cirrus,vpmon = /bits/ 8 <0x03 0x04 0x00>; + cirrus,vbstmon = /bits/ 8 <0x03 0x04 0x01>; + cirrus,vpbrstat = /bits/ 8 <0x00 0x04 0x00>; + cirrus,zerofill = /bits/ 8 <0x00 0x00 0x00>; + }; + +}; -- cgit v1.2.3 From 1e03580b3a7213292fae9a6677edfaa83843fa4e Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Mon, 6 Mar 2017 18:44:51 +0100 Subject: ASoC: dio2125: add dt-bindings Signed-off-by: Jerome Brunet Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/dioo,dio2125.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/dioo,dio2125.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/dioo,dio2125.txt b/Documentation/devicetree/bindings/sound/dioo,dio2125.txt new file mode 100644 index 000000000000..63dbfe0f11d0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/dioo,dio2125.txt @@ -0,0 +1,12 @@ +DIO2125 Audio Driver + +Required properties: +- compatible : "dioo,dio2125" +- enable-gpios : the gpio connected to the enable pin of the dio2125 + +Example: + +amp: analog-amplifier { + compatible = "dioo,dio2125"; + enable-gpios = <&gpio GPIOH_3 0>; +}; -- cgit v1.2.3 From 45114c364d476ba0bbc294d63955f49fbf378779 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 8 Mar 2017 16:42:48 +0000 Subject: ASoC: cs35l35: Add device tree binding for I2S drive configuration Add a binding for setting how the I2S pins are driven in unused slots, currently the chip will just use the default of drive 0, however this causes issues when multiple devices are attached to the same bus. Signed-off-by: Charles Keepax Acked-by: Brian Austin Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cs35l35.txt | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt index 07d9c0f177fb..958f0eabfdfc 100644 --- a/Documentation/devicetree/bindings/sound/cs35l35.txt +++ b/Documentation/devicetree/bindings/sound/cs35l35.txt @@ -37,6 +37,11 @@ Optional properties: Table 3-10 of the datasheet lists drive-strength specifications 0 = 1x (Default) 1 = .5x + - cirrus,sp-drv-unused : Determines how unused slots should be driven on the + Serial Port. + 0 - Hi-Z + 2 - Drive 0's (Default) + 3 - Drive 1's - cirrus,bst-pdn-fet-on : Boolean to determine if the Boost PDN control powers down with a rectification FET On or Off. If VSPK is supplied -- cgit v1.2.3 From f49efce4ed6fdec17a32406eccb56fc167f43b6c Mon Sep 17 00:00:00 2001 From: Jianqun Xu Date: Wed, 15 Mar 2017 14:14:00 +0800 Subject: ASoC: rockchip: add bindings for rk3368 i2s Add devicetree bindings for i2s controller found on rk3368 processors from rockchip. Signed-off-by: Jianqun Xu Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rockchip-i2s.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/rockchip-i2s.txt b/Documentation/devicetree/bindings/sound/rockchip-i2s.txt index a6600f6dea64..206aba1b34bb 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,rk3368-i2s", "rockchip,rk3066-i2s": for rk3368 - "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399 - reg: physical base address of the controller and length of memory mapped region. -- cgit v1.2.3 From 6cd6ad0e45b9ea48bf3b62eee2dd7238f3e4dd7c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 13 Mar 2017 16:56:03 -0300 Subject: ASoC: tas2552: Improve DT binding document example Improve DT binding document example by providing the mandatory regulator properties. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tas2552.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt index c49992c0b62a..54c1c87c4ad8 100644 --- a/Documentation/devicetree/bindings/sound/tas2552.txt +++ b/Documentation/devicetree/bindings/sound/tas2552.txt @@ -25,6 +25,9 @@ Example: tas2552: tas2552@41 { compatible = "ti,tas2552"; reg = <0x41>; + vbat-supply = <®_vbat>; + iovdd-supply = <®_iovdd>; + avdd-supply = <®_avdd>; enable-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>; }; -- cgit v1.2.3 From b3bbef45e97526e8c56fd542d75e505776eecc01 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 20 Mar 2017 10:13:52 +0100 Subject: ASoC: wm8903: add regulator handling The WM8903 has four different voltage inputs: AVDD, CPVDD, DBVDD and DCVDD. On the Qualcomm APQ8060 Dragonboard these are all supplied from proper regulators and thus need activating and binding. This is a quick-and-dirty solution just grabbing and enabling the regulator supplies on probe() and disabling them on remove() and the errorpath. More elaborate power management is likely possible. I assume the nVidia designs using this codec have some hard-wired always-on power and will be happy with using the dummy regulators for this. But someone from the nVidia camp should probably check whether they can bind these to proper regulators instead. We also amend the DT binding document. A small change like this does not warrant a separate patch for augmenting these. Cc: devicetree@vger.kernel.org Cc: Mark Brown Cc: Liam Girdwood Cc: Stephen Warren Cc: Charles Keepax Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/wm8903.txt | 13 +++++++++ sound/soc/codecs/wm8903.c | 31 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/wm8903.txt b/Documentation/devicetree/bindings/sound/wm8903.txt index 94ec32c194bb..afc51caf1137 100644 --- a/Documentation/devicetree/bindings/sound/wm8903.txt +++ b/Documentation/devicetree/bindings/sound/wm8903.txt @@ -28,6 +28,14 @@ Optional properties: performed. If any entry has the value 0xffffffff, that GPIO's configuration will not be modified. + - AVDD-supply : Analog power supply regulator on the AVDD pin. + + - CPVDD-supply : Charge pump supply regulator on the CPVDD pin. + + - DBVDD-supply : Digital buffer supply regulator for the DBVDD pin. + + - DCVDD-supply : Digital core supply regulator for the DCVDD pin. + Pins on the device (for linking into audio routes): * IN1L @@ -54,6 +62,11 @@ codec: wm8903@1a { reg = <0x1a>; interrupts = < 347 >; + AVDD-supply = <&fooreg_a>; + CPVDD-supply = <&fooreg_b>; + DBVDD-supply = <&fooreg_c>; + DCVDC-supply = <&fooreg_d>; + gpio-controller; #gpio-cells = <2>; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 6e887c2c42b1..237eeb9a8b97 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -115,10 +116,19 @@ static const struct reg_default wm8903_reg_defaults[] = { { 172, 0x0000 }, /* R172 - Analogue Output Bias 0 */ }; +#define WM8903_NUM_SUPPLIES 4 +static const char *wm8903_supply_names[WM8903_NUM_SUPPLIES] = { + "AVDD", + "CPVDD", + "DBVDD", + "DCVDD", +}; + struct wm8903_priv { struct wm8903_platform_data *pdata; struct device *dev; struct regmap *regmap; + struct regulator_bulk_data supplies[WM8903_NUM_SUPPLIES]; int sysclk; int irq; @@ -2030,6 +2040,23 @@ static int wm8903_i2c_probe(struct i2c_client *i2c, pdata = wm8903->pdata; + for (i = 0; i < ARRAY_SIZE(wm8903->supplies); i++) + wm8903->supplies[i].supply = wm8903_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val); if (ret != 0) { dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret); @@ -2160,6 +2187,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c, return 0; err: + regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); return ret; } @@ -2167,6 +2196,8 @@ static int wm8903_i2c_remove(struct i2c_client *client) { struct wm8903_priv *wm8903 = i2c_get_clientdata(client); + regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies), + wm8903->supplies); if (client->irq) free_irq(client->irq, wm8903); wm8903_free_gpio(wm8903); -- cgit v1.2.3 From 768abad46df941b6f921f358d3b17e51451b29bd Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Wed, 29 Mar 2017 16:59:32 +0800 Subject: ASoC: mediatek: add bindings for wm8960 codec machine driver add device tree bindings for mt2701-wm8960. Signed-off-by: Ryder Lee Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mt2701-wm8960.txt | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/mt2701-wm8960.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/mt2701-wm8960.txt b/Documentation/devicetree/bindings/sound/mt2701-wm8960.txt new file mode 100644 index 000000000000..809b609ea9d0 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mt2701-wm8960.txt @@ -0,0 +1,24 @@ +MT2701 with WM8960 CODEC + +Required properties: +- compatible: "mediatek,mt2701-wm8960-machine" +- mediatek,platform: the phandle of MT2701 ASoC platform +- audio-routing: a list of the connections between audio +- mediatek,audio-codec: the phandles of wm8960 codec +- pinctrl-names: Should contain only one value - "default" +- pinctrl-0: Should specify pin control groups used for this controller. + +Example: + + sound:sound { + compatible = "mediatek,mt2701-wm8960-machine"; + mediatek,platform = <&afe>; + audio-routing = + "Headphone", "HP_L", + "Headphone", "HP_R", + "LINPUT1", "AMIC", + "RINPUT1", "AMIC"; + mediatek,audio-codec = <&wm8960>; + pinctrl-names = "default"; + pinctrl-0 = <&aud_pins_default>; + }; -- cgit v1.2.3 From 7c0c2000716e64151b3c0c62026c18f31537ebe9 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 4 Apr 2017 02:23:08 +0900 Subject: ASoC: Add support for Maxim Integrated MAX98927 Amplifier Signed-off-by: Ryan Lee Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/max98925.txt | 22 - .../devicetree/bindings/sound/max98926.txt | 32 - .../devicetree/bindings/sound/max9892x.txt | 41 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/max98927.c | 841 +++++++++++++++++++++ sound/soc/codecs/max98927.h | 272 +++++++ 7 files changed, 1161 insertions(+), 54 deletions(-) delete mode 100644 Documentation/devicetree/bindings/sound/max98925.txt delete mode 100644 Documentation/devicetree/bindings/sound/max98926.txt create mode 100644 Documentation/devicetree/bindings/sound/max9892x.txt create mode 100644 sound/soc/codecs/max98927.c create mode 100644 sound/soc/codecs/max98927.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/max98925.txt b/Documentation/devicetree/bindings/sound/max98925.txt deleted file mode 100644 index 27be63e2aa0d..000000000000 --- a/Documentation/devicetree/bindings/sound/max98925.txt +++ /dev/null @@ -1,22 +0,0 @@ -max98925 audio CODEC - -This device supports I2C. - -Required properties: - - - compatible : "maxim,max98925" - - - vmon-slot-no : slot number used to send voltage information - - - imon-slot-no : slot number used to send current information - - - reg : the I2C address of the device for I2C - -Example: - -codec: max98925@1a { - compatible = "maxim,max98925"; - vmon-slot-no = <0>; - imon-slot-no = <2>; - reg = <0x1a>; -}; diff --git a/Documentation/devicetree/bindings/sound/max98926.txt b/Documentation/devicetree/bindings/sound/max98926.txt deleted file mode 100644 index 0b7f4e4d5f9a..000000000000 --- a/Documentation/devicetree/bindings/sound/max98926.txt +++ /dev/null @@ -1,32 +0,0 @@ -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/max9892x.txt b/Documentation/devicetree/bindings/sound/max9892x.txt new file mode 100644 index 000000000000..f6171591ddc6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/max9892x.txt @@ -0,0 +1,41 @@ +Maxim Integrated MAX98925/MAX98926/MAX98927 Speaker Amplifier + +This device supports I2C. + +Required properties: + + - compatible : should be one of the following + - "maxim,max98925" + - "maxim,max98926" + - "maxim,max98927" + + - vmon-slot-no : slot number used to send voltage information + or in inteleave mode this will be used as + interleave slot. + MAX98925/MAX98926 slot range : 0 ~ 30, Default : 0 + MAX98927 slot range : 0 ~ 15, Default : 0 + + - imon-slot-no : slot number used to send current information + MAX98925/MAX98926 slot range : 0 ~ 30, Default : 0 + MAX98927 slot range : 0 ~ 15, Default : 0 + + - interleave-mode : When using two MAX9892X 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. + Range : 0 (off), 1 (on), Default : 0 + + - reg : the I2C address of the device for I2C + +Example: + +codec: max98927@3a { + compatible = "maxim,max98927"; + vmon-slot-no = <0>; + imon-slot-no = <1>; + interleave-mode = <0>; + reg = <0x3a>; +}; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9e1718a8cb1c..65e31ab88280 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -89,6 +89,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9867 if I2C select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98926 if I2C + select SND_SOC_MAX98927 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9860 if I2C select SND_SOC_MAX9768 if I2C @@ -585,6 +586,10 @@ config SND_SOC_MAX98925 config SND_SOC_MAX98926 tristate +config SND_SOC_MAX98927 + tristate "Maxim Integrated MAX98927 Speaker Amplifier" + depends on I2C + config SND_SOC_MAX9850 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7e1dad79610b..64656c43200c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -84,6 +84,7 @@ snd-soc-max98371-objs := max98371.o snd-soc-max9867-objs := max9867.o snd-soc-max98925-objs := max98925.o snd-soc-max98926-objs := max98926.o +snd-soc-max98927-objs := max98927.o snd-soc-max9850-objs := max9850.o snd-soc-max9860-objs := max9860.o snd-soc-mc13783-objs := mc13783.o @@ -312,6 +313,7 @@ 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_MAX98927) += snd-soc-max98927.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c new file mode 100644 index 000000000000..b5ee29499e16 --- /dev/null +++ b/sound/soc/codecs/max98927.c @@ -0,0 +1,841 @@ +/* + * max98927.c -- MAX98927 ALSA Soc Audio driver + * + * Copyright (C) 2016 Maxim Integrated Products + * Author: Ryan Lee + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "max98927.h" + +static struct reg_default max98927_reg[] = { + {MAX98927_R0001_INT_RAW1, 0x00}, + {MAX98927_R0002_INT_RAW2, 0x00}, + {MAX98927_R0003_INT_RAW3, 0x00}, + {MAX98927_R0004_INT_STATE1, 0x00}, + {MAX98927_R0005_INT_STATE2, 0x00}, + {MAX98927_R0006_INT_STATE3, 0x00}, + {MAX98927_R0007_INT_FLAG1, 0x00}, + {MAX98927_R0008_INT_FLAG2, 0x00}, + {MAX98927_R0009_INT_FLAG3, 0x00}, + {MAX98927_R000A_INT_EN1, 0x00}, + {MAX98927_R000B_INT_EN2, 0x00}, + {MAX98927_R000C_INT_EN3, 0x00}, + {MAX98927_R000D_INT_FLAG_CLR1, 0x00}, + {MAX98927_R000E_INT_FLAG_CLR2, 0x00}, + {MAX98927_R000F_INT_FLAG_CLR3, 0x00}, + {MAX98927_R0010_IRQ_CTRL, 0x00}, + {MAX98927_R0011_CLK_MON, 0x00}, + {MAX98927_R0012_WDOG_CTRL, 0x00}, + {MAX98927_R0013_WDOG_RST, 0x00}, + {MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH, 0x00}, + {MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH, 0x00}, + {MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS, 0x00}, + {MAX98927_R0017_PIN_CFG, 0x55}, + {MAX98927_R0018_PCM_RX_EN_A, 0x00}, + {MAX98927_R0019_PCM_RX_EN_B, 0x00}, + {MAX98927_R001A_PCM_TX_EN_A, 0x00}, + {MAX98927_R001B_PCM_TX_EN_B, 0x00}, + {MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0x00}, + {MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0x00}, + {MAX98927_R001E_PCM_TX_CH_SRC_A, 0x00}, + {MAX98927_R001F_PCM_TX_CH_SRC_B, 0x00}, + {MAX98927_R0020_PCM_MODE_CFG, 0x40}, + {MAX98927_R0021_PCM_MASTER_MODE, 0x00}, + {MAX98927_R0022_PCM_CLK_SETUP, 0x22}, + {MAX98927_R0023_PCM_SR_SETUP1, 0x00}, + {MAX98927_R0024_PCM_SR_SETUP2, 0x00}, + {MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, 0x00}, + {MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, 0x00}, + {MAX98927_R0027_ICC_RX_EN_A, 0x00}, + {MAX98927_R0028_ICC_RX_EN_B, 0x00}, + {MAX98927_R002B_ICC_TX_EN_A, 0x00}, + {MAX98927_R002C_ICC_TX_EN_B, 0x00}, + {MAX98927_R002E_ICC_HIZ_MANUAL_MODE, 0x00}, + {MAX98927_R002F_ICC_TX_HIZ_EN_A, 0x00}, + {MAX98927_R0030_ICC_TX_HIZ_EN_B, 0x00}, + {MAX98927_R0031_ICC_LNK_EN, 0x00}, + {MAX98927_R0032_PDM_TX_EN, 0x00}, + {MAX98927_R0033_PDM_TX_HIZ_CTRL, 0x00}, + {MAX98927_R0034_PDM_TX_CTRL, 0x00}, + {MAX98927_R0035_PDM_RX_CTRL, 0x00}, + {MAX98927_R0036_AMP_VOL_CTRL, 0x00}, + {MAX98927_R0037_AMP_DSP_CFG, 0x02}, + {MAX98927_R0038_TONE_GEN_DC_CFG, 0x00}, + {MAX98927_R0039_DRE_CTRL, 0x01}, + {MAX98927_R003A_AMP_EN, 0x00}, + {MAX98927_R003B_SPK_SRC_SEL, 0x00}, + {MAX98927_R003C_SPK_GAIN, 0x00}, + {MAX98927_R003D_SSM_CFG, 0x01}, + {MAX98927_R003E_MEAS_EN, 0x00}, + {MAX98927_R003F_MEAS_DSP_CFG, 0x04}, + {MAX98927_R0040_BOOST_CTRL0, 0x00}, + {MAX98927_R0041_BOOST_CTRL3, 0x00}, + {MAX98927_R0042_BOOST_CTRL1, 0x00}, + {MAX98927_R0043_MEAS_ADC_CFG, 0x00}, + {MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x00}, + {MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x00}, + {MAX98927_R0046_ADC_CH0_DIVIDE, 0x00}, + {MAX98927_R0047_ADC_CH1_DIVIDE, 0x00}, + {MAX98927_R0048_ADC_CH2_DIVIDE, 0x00}, + {MAX98927_R0049_ADC_CH0_FILT_CFG, 0x00}, + {MAX98927_R004A_ADC_CH1_FILT_CFG, 0x00}, + {MAX98927_R004B_ADC_CH2_FILT_CFG, 0x00}, + {MAX98927_R004C_MEAS_ADC_CH0_READ, 0x00}, + {MAX98927_R004D_MEAS_ADC_CH1_READ, 0x00}, + {MAX98927_R004E_MEAS_ADC_CH2_READ, 0x00}, + {MAX98927_R0051_BROWNOUT_STATUS, 0x00}, + {MAX98927_R0052_BROWNOUT_EN, 0x00}, + {MAX98927_R0053_BROWNOUT_INFINITE_HOLD, 0x00}, + {MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR, 0x00}, + {MAX98927_R0055_BROWNOUT_LVL_HOLD, 0x00}, + {MAX98927_R005A_BROWNOUT_LVL1_THRESH, 0x00}, + {MAX98927_R005B_BROWNOUT_LVL2_THRESH, 0x00}, + {MAX98927_R005C_BROWNOUT_LVL3_THRESH, 0x00}, + {MAX98927_R005D_BROWNOUT_LVL4_THRESH, 0x00}, + {MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS, 0x00}, + {MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL, 0x00}, + {MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL, 0x00}, + {MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE, 0x00}, + {MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT, 0x00}, + {MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1, 0x00}, + {MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2, 0x00}, + {MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3, 0x00}, + {MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT, 0x00}, + {MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1, 0x00}, + {MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2, 0x00}, + {MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3, 0x00}, + {MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT, 0x00}, + {MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1, 0x00}, + {MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2, 0x00}, + {MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3, 0x00}, + {MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT, 0x00}, + {MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, 0x00}, + {MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2, 0x00}, + {MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3, 0x00}, + {MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, 0x00}, + {MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY, 0x00}, + {MAX98927_R0084_ENV_TRACK_REL_RATE, 0x00}, + {MAX98927_R0085_ENV_TRACK_HOLD_RATE, 0x00}, + {MAX98927_R0086_ENV_TRACK_CTRL, 0x00}, + {MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, 0x00}, + {MAX98927_R00FF_GLOBAL_SHDN, 0x00}, + {MAX98927_R0100_SOFT_RESET, 0x00}, + {MAX98927_R01FF_REV_ID, 0x40}, +}; + +static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + unsigned int mode = 0; + unsigned int format = 0; + 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: + mode = MAX98927_PCM_MASTER_MODE_SLAVE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + max98927->master = true; + mode = MAX98927_PCM_MASTER_MODE_MASTER; + break; + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MASK, + mode); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + max98927->iface |= SND_SOC_DAIFMT_I2S; + format = MAX98927_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + max98927->iface |= SND_SOC_DAIFMT_LEFT_J; + format = MAX98927_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_PDM: + max98927->iface |= SND_SOC_DAIFMT_PDM; + break; + default: + return -EINVAL; + } + + /* pcm channel configuration */ + if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) { + regmap_update_bits(max98927->regmap, + MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN); + + regmap_update_bits(max98927->regmap, + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT); + + regmap_update_bits(max98927->regmap, + MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 0); + + } else + regmap_update_bits(max98927->regmap, + MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0); + + /* pdm channel configuration */ + if (max98927->iface & SND_SOC_DAIFMT_PDM) { + regmap_update_bits(max98927->regmap, + MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 1); + + regmap_update_bits(max98927->regmap, + MAX98927_R003B_SPK_SRC_SEL, + MAX98927_SPK_SRC_MASK, 3); + } else + regmap_update_bits(max98927->regmap, + MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 0); + return 0; +} + +/* codec MCLK rate in master mode */ +static const int rate_table[] = { + 5644800, 6000000, 6144000, 6500000, + 9600000, 11289600, 12000000, 12288000, + 13000000, 19200000, +}; + +static int max98927_set_clock(struct max98927_priv *max98927, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_codec *codec = max98927->codec; + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98927->ch_size; + int value; + + if (max98927->master) { + int i; + /* match rate to closest value */ + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i] >= max98927->sysclk) + break; + } + if (i == ARRAY_SIZE(rate_table)) { + dev_err(codec->dev, "failed to find proper clock rate.\n"); + return -EINVAL; + } + regmap_update_bits(max98927->regmap, + MAX98927_R0021_PCM_MASTER_MODE, + MAX98927_PCM_MASTER_MODE_MCLK_MASK, + i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); + } + + switch (blr_clk_ratio) { + case 32: + value = 2; + break; + case 48: + value = 3; + break; + case 64: + value = 4; + break; + default: + return -EINVAL; + } + regmap_update_bits(max98927->regmap, + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, + value); + return 0; +} + +static int max98927_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 max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(codec->dev, "format unsupported %d", + params_format(params)); + goto err; + } + + max98927->ch_size = snd_pcm_format_width(params_format(params)); + + regmap_update_bits(max98927->regmap, + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + dev_dbg(codec->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_8000; + break; + case 11025: + sampling_rate = MAX98927_PCM_SR_SET1_SR_11025; + break; + case 12000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_12000; + break; + case 16000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_16000; + break; + case 22050: + sampling_rate = MAX98927_PCM_SR_SET1_SR_22050; + break; + case 24000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_24000; + break; + case 32000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_32000; + break; + case 44100: + sampling_rate = MAX98927_PCM_SR_SET1_SR_44100; + break; + case 48000: + sampling_rate = MAX98927_PCM_SR_SET1_SR_48000; + break; + default: + dev_err(codec->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98927->regmap, + MAX98927_R0023_PCM_SR_SETUP1, + MAX98927_PCM_SR_SET1_SR_MASK, + sampling_rate); + regmap_update_bits(max98927->regmap, + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_SR_MASK, + sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT); + + /* set sampling rate of IV */ + if (max98927->interleave_mode && + sampling_rate > MAX98927_PCM_SR_SET1_SR_16000) + regmap_update_bits(max98927->regmap, + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate - 3); + else + regmap_update_bits(max98927->regmap, + MAX98927_R0024_PCM_SR_SETUP2, + MAX98927_PCM_SR_SET2_IVADC_SR_MASK, + sampling_rate); + return max98927_set_clock(max98927, params); +err: + return -EINVAL; +} + +#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int max98927_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + + max98927->sysclk = freq; + return 0; +} + +static const struct snd_soc_dai_ops max98927_dai_ops = { + .set_sysclk = max98927_dai_set_sysclk, + .set_fmt = max98927_dai_set_fmt, + .hw_params = max98927_dai_hw_params, +}; + +static int max98927_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 max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(max98927->regmap, + MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 1); + /* enable VMON and IMON */ + regmap_update_bits(max98927->regmap, + MAX98927_R003E_MEAS_EN, + MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN, + MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN); + regmap_update_bits(max98927->regmap, + MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 1); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(max98927->regmap, + MAX98927_R00FF_GLOBAL_SHDN, + MAX98927_GLOBAL_EN_MASK, 0); + regmap_update_bits(max98927->regmap, + MAX98927_R003A_AMP_EN, + MAX98927_AMP_EN_MASK, 0); + /* disable VMON and IMON */ + regmap_update_bits(max98927->regmap, + MAX98927_R003E_MEAS_EN, + MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN, 0); + break; + default: + return 0; + } + return 0; +} + +static const char * const max98927_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, + MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, + 3, max98927_switch_text); + +static const struct snd_kcontrol_new max98927_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN, + 0, 0, max98927_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98927_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), +}; + +static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0); +static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0); + +static bool max98927_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98927_R0001_INT_RAW1 ... MAX98927_R0028_ICC_RX_EN_B: + case MAX98927_R002B_ICC_TX_EN_A ... MAX98927_R002C_ICC_TX_EN_B: + case MAX98927_R002E_ICC_HIZ_MANUAL_MODE + ... MAX98927_R004E_MEAS_ADC_CH2_READ: + case MAX98927_R0051_BROWNOUT_STATUS + ... MAX98927_R0055_BROWNOUT_LVL_HOLD: + case MAX98927_R005A_BROWNOUT_LVL1_THRESH + ... MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE: + case MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT + ... MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ: + case MAX98927_R00FF_GLOBAL_SHDN: + case MAX98927_R0100_SOFT_RESET: + case MAX98927_R01FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98927_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98927_R0001_INT_RAW1 ... MAX98927_R0009_INT_FLAG3: + return true; + default: + return false; + } +} + +static const char * const max98927_boost_voltage_text[] = { + "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V", + "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V", + "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V", + "9.5V", "9.625V", "9.75V", "9.875V", "10V" +}; + +static SOC_ENUM_SINGLE_DECL(max98927_boost_voltage, + MAX98927_R0040_BOOST_CTRL0, 0, + max98927_boost_voltage_text); + +static const char * const max98927_current_limit_text[] = { + "1.00A", "1.10A", "1.20A", "1.30A", "1.40A", "1.50A", "1.60A", "1.70A", + "1.80A", "1.90A", "2.00A", "2.10A", "2.20A", "2.30A", "2.40A", "2.50A", + "2.60A", "2.70A", "2.80A", "2.90A", "3.00A", "3.10A", "3.20A", "3.30A", + "3.40A", "3.50A", "3.60A", "3.70A", "3.80A", "3.90A", "4.00A", "4.10A" +}; + +static SOC_ENUM_SINGLE_DECL(max98927_current_limit, + MAX98927_R0042_BOOST_CTRL1, 1, + max98927_current_limit_text); + +static const struct snd_kcontrol_new max98927_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, + 0, 6, 0, + max98927_spk_tlv), + SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL, + 0, (1<codec = codec; + codec->control_data = max98927->regmap; + codec->cache_bypass = 1; + + /* Software Reset */ + regmap_write(max98927->regmap, + MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET); + + /* IV default slot configuration */ + regmap_write(max98927->regmap, + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 0xFF); + regmap_write(max98927->regmap, + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 0xFF); + regmap_write(max98927->regmap, + MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, + 0x80); + regmap_write(max98927->regmap, + MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, + 0x1); + /* Set inital volume (+13dB) */ + regmap_write(max98927->regmap, + MAX98927_R0036_AMP_VOL_CTRL, + 0x38); + regmap_write(max98927->regmap, + MAX98927_R003C_SPK_GAIN, + 0x05); + /* Enable DC blocker */ + regmap_write(max98927->regmap, + MAX98927_R0037_AMP_DSP_CFG, + 0x03); + /* Enable IMON VMON DC blocker */ + regmap_write(max98927->regmap, + MAX98927_R003F_MEAS_DSP_CFG, + 0xF7); + /* Boost Output Voltage & Current limit */ + regmap_write(max98927->regmap, + MAX98927_R0040_BOOST_CTRL0, + 0x1C); + regmap_write(max98927->regmap, + MAX98927_R0042_BOOST_CTRL1, + 0x3E); + /* Measurement ADC config */ + regmap_write(max98927->regmap, + MAX98927_R0043_MEAS_ADC_CFG, + 0x04); + regmap_write(max98927->regmap, + MAX98927_R0044_MEAS_ADC_BASE_MSB, + 0x00); + regmap_write(max98927->regmap, + MAX98927_R0045_MEAS_ADC_BASE_LSB, + 0x24); + /* Brownout Level */ + regmap_write(max98927->regmap, + MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, + 0x06); + /* Envelope Tracking configuration */ + regmap_write(max98927->regmap, + MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, + 0x08); + regmap_write(max98927->regmap, + MAX98927_R0086_ENV_TRACK_CTRL, + 0x01); + regmap_write(max98927->regmap, + MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, + 0x10); + + /* voltage, current slot configuration */ + regmap_write(max98927->regmap, + MAX98927_R001E_PCM_TX_CH_SRC_A, + (max98927->i_l_slot<v_l_slot)&0xFF); + + if (max98927->v_l_slot < 8) { + regmap_update_bits(max98927->regmap, + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->v_l_slot, 0); + regmap_update_bits(max98927->regmap, + MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->v_l_slot, + 1 << max98927->v_l_slot); + } else { + regmap_update_bits(max98927->regmap, + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->v_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, + MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->v_l_slot - 8), + 1 << (max98927->v_l_slot - 8)); + } + + if (max98927->i_l_slot < 8) { + regmap_update_bits(max98927->regmap, + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + 1 << max98927->i_l_slot, 0); + regmap_update_bits(max98927->regmap, + MAX98927_R001A_PCM_TX_EN_A, + 1 << max98927->i_l_slot, + 1 << max98927->i_l_slot); + } else { + regmap_update_bits(max98927->regmap, + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + 1 << (max98927->i_l_slot - 8), 0); + regmap_update_bits(max98927->regmap, + MAX98927_R001B_PCM_TX_EN_B, + 1 << (max98927->i_l_slot - 8), + 1 << (max98927->i_l_slot - 8)); + } + + /* Set interleave mode */ + if (max98927->interleave_mode) + regmap_update_bits(max98927->regmap, + MAX98927_R001F_PCM_TX_CH_SRC_B, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK, + MAX98927_PCM_TX_CH_INTERLEAVE_MASK); + return 0; +} + +static const struct snd_soc_codec_driver soc_codec_dev_max98927 = { + .probe = max98927_probe, + .component_driver = { + .controls = max98927_snd_controls, + .num_controls = ARRAY_SIZE(max98927_snd_controls), + .dapm_widgets = max98927_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets), + .dapm_routes = max98927_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98927_audio_map), + }, +}; + +static const struct regmap_config max98927_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98927_R01FF_REV_ID, + .reg_defaults = max98927_reg, + .num_reg_defaults = ARRAY_SIZE(max98927_reg), + .readable_reg = max98927_readable_register, + .volatile_reg = max98927_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static void max98927_slot_config(struct i2c_client *i2c, + struct max98927_priv *max98927) +{ + int value; + + if (!of_property_read_u32(i2c->dev.of_node, + "vmon-slot-no", &value)) + max98927->v_l_slot = value & 0xF; + else + max98927->v_l_slot = 0; + if (!of_property_read_u32(i2c->dev.of_node, + "imon-slot-no", &value)) + max98927->i_l_slot = value & 0xF; + else + max98927->i_l_slot = 1; +} + +static int max98927_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + + int ret = 0, value; + int reg = 0; + struct max98927_priv *max98927 = NULL; + + max98927 = devm_kzalloc(&i2c->dev, + sizeof(*max98927), GFP_KERNEL); + + if (!max98927) { + ret = -ENOMEM; + return ret; + } + i2c_set_clientdata(i2c, max98927); + + /* update interleave mode info */ + if (!of_property_read_u32(i2c->dev.of_node, + "interleave_mode", &value)) { + if (value > 0) + max98927->interleave_mode = 1; + else + max98927->interleave_mode = 0; + } else + max98927->interleave_mode = 0; + + /* regmap initialization */ + max98927->regmap + = devm_regmap_init_i2c(i2c, &max98927_regmap); + if (IS_ERR(max98927->regmap)) { + ret = PTR_ERR(max98927->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + return ret; + } + + /* Check Revision ID */ + ret = regmap_read(max98927->regmap, + MAX98927_R01FF_REV_ID, ®); + if (ret < 0) { + dev_err(&i2c->dev, + "Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID); + return ret; + } + dev_info(&i2c->dev, "MAX98927 revisionID: 0x%02X\n", reg); + + /* voltage/current slot configuration */ + max98927_slot_config(i2c, max98927); + + /* codec registeration */ + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98927, + max98927_dai, ARRAY_SIZE(max98927_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static int max98927_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98927_i2c_id[] = { + { "max98927", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98927_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id max98927_of_match[] = { + { .compatible = "maxim,max98927", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98927_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id max98927_acpi_match[] = { + { "MX98927", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98927_acpi_match); +#endif + +static struct i2c_driver max98927_i2c_driver = { + .driver = { + .name = "max98927", + .of_match_table = of_match_ptr(max98927_of_match), + .acpi_match_table = ACPI_PTR(max98927_acpi_match), + .pm = NULL, + }, + .probe = max98927_i2c_probe, + .remove = max98927_i2c_remove, + .id_table = max98927_i2c_id, +}; + +module_i2c_driver(max98927_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98927 driver"); +MODULE_AUTHOR("Ryan Lee "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h new file mode 100644 index 000000000000..ece6a608cbe1 --- /dev/null +++ b/sound/soc/codecs/max98927.h @@ -0,0 +1,272 @@ +/* + * max98927.h -- MAX98927 ALSA Soc Audio driver + * + * Copyright 2013-15 Maxim Integrated Products + * Author: Ryan Lee + * + * 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. + * + */ +#ifndef _MAX98927_H +#define _MAX98927_H + +/* Register Values */ +#define MAX98927_R0001_INT_RAW1 0x0001 +#define MAX98927_R0002_INT_RAW2 0x0002 +#define MAX98927_R0003_INT_RAW3 0x0003 +#define MAX98927_R0004_INT_STATE1 0x0004 +#define MAX98927_R0005_INT_STATE2 0x0005 +#define MAX98927_R0006_INT_STATE3 0x0006 +#define MAX98927_R0007_INT_FLAG1 0x0007 +#define MAX98927_R0008_INT_FLAG2 0x0008 +#define MAX98927_R0009_INT_FLAG3 0x0009 +#define MAX98927_R000A_INT_EN1 0x000A +#define MAX98927_R000B_INT_EN2 0x000B +#define MAX98927_R000C_INT_EN3 0x000C +#define MAX98927_R000D_INT_FLAG_CLR1 0x000D +#define MAX98927_R000E_INT_FLAG_CLR2 0x000E +#define MAX98927_R000F_INT_FLAG_CLR3 0x000F +#define MAX98927_R0010_IRQ_CTRL 0x0010 +#define MAX98927_R0011_CLK_MON 0x0011 +#define MAX98927_R0012_WDOG_CTRL 0x0012 +#define MAX98927_R0013_WDOG_RST 0x0013 +#define MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH 0x0014 +#define MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH 0x0015 +#define MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS 0x0016 +#define MAX98927_R0017_PIN_CFG 0x0017 +#define MAX98927_R0018_PCM_RX_EN_A 0x0018 +#define MAX98927_R0019_PCM_RX_EN_B 0x0019 +#define MAX98927_R001A_PCM_TX_EN_A 0x001A +#define MAX98927_R001B_PCM_TX_EN_B 0x001B +#define MAX98927_R001C_PCM_TX_HIZ_CTRL_A 0x001C +#define MAX98927_R001D_PCM_TX_HIZ_CTRL_B 0x001D +#define MAX98927_R001E_PCM_TX_CH_SRC_A 0x001E +#define MAX98927_R001F_PCM_TX_CH_SRC_B 0x001F +#define MAX98927_R0020_PCM_MODE_CFG 0x0020 +#define MAX98927_R0021_PCM_MASTER_MODE 0x0021 +#define MAX98927_R0022_PCM_CLK_SETUP 0x0022 +#define MAX98927_R0023_PCM_SR_SETUP1 0x0023 +#define MAX98927_R0024_PCM_SR_SETUP2 0x0024 +#define MAX98927_R0025_PCM_TO_SPK_MONOMIX_A 0x0025 +#define MAX98927_R0026_PCM_TO_SPK_MONOMIX_B 0x0026 +#define MAX98927_R0027_ICC_RX_EN_A 0x0027 +#define MAX98927_R0028_ICC_RX_EN_B 0x0028 +#define MAX98927_R002B_ICC_TX_EN_A 0x002B +#define MAX98927_R002C_ICC_TX_EN_B 0x002C +#define MAX98927_R002E_ICC_HIZ_MANUAL_MODE 0x002E +#define MAX98927_R002F_ICC_TX_HIZ_EN_A 0x002F +#define MAX98927_R0030_ICC_TX_HIZ_EN_B 0x0030 +#define MAX98927_R0031_ICC_LNK_EN 0x0031 +#define MAX98927_R0032_PDM_TX_EN 0x0032 +#define MAX98927_R0033_PDM_TX_HIZ_CTRL 0x0033 +#define MAX98927_R0034_PDM_TX_CTRL 0x0034 +#define MAX98927_R0035_PDM_RX_CTRL 0x0035 +#define MAX98927_R0036_AMP_VOL_CTRL 0x0036 +#define MAX98927_R0037_AMP_DSP_CFG 0x0037 +#define MAX98927_R0038_TONE_GEN_DC_CFG 0x0038 +#define MAX98927_R0039_DRE_CTRL 0x0039 +#define MAX98927_R003A_AMP_EN 0x003A +#define MAX98927_R003B_SPK_SRC_SEL 0x003B +#define MAX98927_R003C_SPK_GAIN 0x003C +#define MAX98927_R003D_SSM_CFG 0x003D +#define MAX98927_R003E_MEAS_EN 0x003E +#define MAX98927_R003F_MEAS_DSP_CFG 0x003F +#define MAX98927_R0040_BOOST_CTRL0 0x0040 +#define MAX98927_R0041_BOOST_CTRL3 0x0041 +#define MAX98927_R0042_BOOST_CTRL1 0x0042 +#define MAX98927_R0043_MEAS_ADC_CFG 0x0043 +#define MAX98927_R0044_MEAS_ADC_BASE_MSB 0x0044 +#define MAX98927_R0045_MEAS_ADC_BASE_LSB 0x0045 +#define MAX98927_R0046_ADC_CH0_DIVIDE 0x0046 +#define MAX98927_R0047_ADC_CH1_DIVIDE 0x0047 +#define MAX98927_R0048_ADC_CH2_DIVIDE 0x0048 +#define MAX98927_R0049_ADC_CH0_FILT_CFG 0x0049 +#define MAX98927_R004A_ADC_CH1_FILT_CFG 0x004A +#define MAX98927_R004B_ADC_CH2_FILT_CFG 0x004B +#define MAX98927_R004C_MEAS_ADC_CH0_READ 0x004C +#define MAX98927_R004D_MEAS_ADC_CH1_READ 0x004D +#define MAX98927_R004E_MEAS_ADC_CH2_READ 0x004E +#define MAX98927_R0051_BROWNOUT_STATUS 0x0051 +#define MAX98927_R0052_BROWNOUT_EN 0x0052 +#define MAX98927_R0053_BROWNOUT_INFINITE_HOLD 0x0053 +#define MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR 0x0054 +#define MAX98927_R0055_BROWNOUT_LVL_HOLD 0x0055 +#define MAX98927_R005A_BROWNOUT_LVL1_THRESH 0x005A +#define MAX98927_R005B_BROWNOUT_LVL2_THRESH 0x005B +#define MAX98927_R005C_BROWNOUT_LVL3_THRESH 0x005C +#define MAX98927_R005D_BROWNOUT_LVL4_THRESH 0x005D +#define MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS 0x005E +#define MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL 0x005F +#define MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL 0x0060 +#define MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE 0x0061 +#define MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT 0x0072 +#define MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1 0x0073 +#define MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2 0x0074 +#define MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3 0x0075 +#define MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT 0x0076 +#define MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1 0x0077 +#define MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2 0x0078 +#define MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3 0x0079 +#define MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT 0x007A +#define MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1 0x007B +#define MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2 0x007C +#define MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3 0x007D +#define MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT 0x007E +#define MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1 0x007F +#define MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2 0x0080 +#define MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3 0x0081 +#define MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM 0x0082 +#define MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY 0x0083 +#define MAX98927_R0084_ENV_TRACK_REL_RATE 0x0084 +#define MAX98927_R0085_ENV_TRACK_HOLD_RATE 0x0085 +#define MAX98927_R0086_ENV_TRACK_CTRL 0x0086 +#define MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ 0x0087 +#define MAX98927_R00FF_GLOBAL_SHDN 0x00FF +#define MAX98927_R0100_SOFT_RESET 0x0100 +#define MAX98927_R01FF_REV_ID 0x01FF + +/* MAX98927_R0018_PCM_RX_EN_A */ +#define MAX98927_PCM_RX_CH0_EN (0x1 << 0) +#define MAX98927_PCM_RX_CH1_EN (0x1 << 1) +#define MAX98927_PCM_RX_CH2_EN (0x1 << 2) +#define MAX98927_PCM_RX_CH3_EN (0x1 << 3) +#define MAX98927_PCM_RX_CH4_EN (0x1 << 4) +#define MAX98927_PCM_RX_CH5_EN (0x1 << 5) +#define MAX98927_PCM_RX_CH6_EN (0x1 << 6) +#define MAX98927_PCM_RX_CH7_EN (0x1 << 7) + +/* MAX98927_R001A_PCM_TX_EN_A */ +#define MAX98927_PCM_TX_CH0_EN (0x1 << 0) +#define MAX98927_PCM_TX_CH1_EN (0x1 << 1) +#define MAX98927_PCM_TX_CH2_EN (0x1 << 2) +#define MAX98927_PCM_TX_CH3_EN (0x1 << 3) +#define MAX98927_PCM_TX_CH4_EN (0x1 << 4) +#define MAX98927_PCM_TX_CH5_EN (0x1 << 5) +#define MAX98927_PCM_TX_CH6_EN (0x1 << 6) +#define MAX98927_PCM_TX_CH7_EN (0x1 << 7) + +/* MAX98927_R001E_PCM_TX_CH_SRC_A */ +#define MAX98927_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98927_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98927_R001F_PCM_TX_CH_SRC_B */ +#define MAX98927_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 5) + +/* MAX98927_R0020_PCM_MODE_CFG */ +#define MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2) +#define MAX98927_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98927_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98927_PCM_FORMAT_LJ (0x1 << 0) + +#define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98927_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98927_R0021_PCM_MASTER_MODE */ +#define MAX98927_PCM_MASTER_MODE_MASK (0x3 << 0) +#define MAX98927_PCM_MASTER_MODE_SLAVE (0x0 << 0) +#define MAX98927_PCM_MASTER_MODE_MASTER (0x3 << 0) + +#define MAX98927_PCM_MASTER_MODE_MCLK_MASK (0xF << 2) +#define MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2) + +/* MAX98927_R0022_PCM_CLK_SETUP */ +#define MAX98927_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98927_R0023_PCM_SR_SETUP1 */ +#define MAX98927_PCM_SR_SET1_SR_MASK (0xF << 0) + +#define MAX98927_PCM_SR_SET1_SR_8000 (0x0 << 0) +#define MAX98927_PCM_SR_SET1_SR_11025 (0x1 << 0) +#define MAX98927_PCM_SR_SET1_SR_12000 (0x2 << 0) +#define MAX98927_PCM_SR_SET1_SR_16000 (0x3 << 0) +#define MAX98927_PCM_SR_SET1_SR_22050 (0x4 << 0) +#define MAX98927_PCM_SR_SET1_SR_24000 (0x5 << 0) +#define MAX98927_PCM_SR_SET1_SR_32000 (0x6 << 0) +#define MAX98927_PCM_SR_SET1_SR_44100 (0x7 << 0) +#define MAX98927_PCM_SR_SET1_SR_48000 (0x8 << 0) + +/* MAX98927_R0024_PCM_SR_SETUP2 */ +#define MAX98927_PCM_SR_SET2_SR_MASK (0xF << 4) +#define MAX98927_PCM_SR_SET2_SR_SHIFT (4) +#define MAX98927_PCM_SR_SET2_IVADC_SR_MASK (0xf << 0) + +/* MAX98927_R0025_PCM_TO_SPK_MONOMIX_A */ +#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6) +#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6) + +/* MAX98927_R0035_PDM_RX_CTRL */ +#define MAX98927_PDM_RX_EN_MASK (0x1 << 0) + +/* MAX98927_R0036_AMP_VOL_CTRL */ +#define MAX98927_AMP_VOL_SEL (0x1 << 7) +#define MAX98927_AMP_VOL_SEL_WIDTH (1) +#define MAX98927_AMP_VOL_SEL_SHIFT (7) +#define MAX98927_AMP_VOL_MASK (0x7f << 0) +#define MAX98927_AMP_VOL_WIDTH (7) +#define MAX98927_AMP_VOL_SHIFT (0) + +/* MAX98927_R0037_AMP_DSP_CFG */ +#define MAX98927_AMP_DSP_CFG_DCBLK_EN (0x1 << 0) +#define MAX98927_AMP_DSP_CFG_DITH_EN (0x1 << 1) +#define MAX98927_AMP_DSP_CFG_RMP_BYPASS (0x1 << 4) +#define MAX98927_AMP_DSP_CFG_DAC_INV (0x1 << 5) +#define MAX98927_AMP_DSP_CFG_RMP_SHIFT (4) + +/* MAX98927_R0039_DRE_CTRL */ +#define MAX98927_DRE_CTRL_DRE_EN (0x1 << 0) +#define MAX98927_DRE_EN_SHIFT 0x1 + +/* MAX98927_R003A_AMP_EN */ +#define MAX98927_AMP_EN_MASK (0x1 << 0) + +/* MAX98927_R003B_SPK_SRC_SEL */ +#define MAX98927_SPK_SRC_MASK (0x3 << 0) + +/* MAX98927_R003C_SPK_GAIN */ +#define MAX98927_SPK_PCM_GAIN_MASK (0x7 << 0) +#define MAX98927_SPK_PDM_GAIN_MASK (0x7 << 4) +#define MAX98927_SPK_GAIN_WIDTH (3) + +/* MAX98927_R003E_MEAS_EN */ +#define MAX98927_MEAS_V_EN (0x1 << 0) +#define MAX98927_MEAS_I_EN (0x1 << 1) + +/* MAX98927_R0040_BOOST_CTRL0 */ +#define MAX98927_BOOST_CTRL0_VOUT_MASK (0x1f << 0) +#define MAX98927_BOOST_CTRL0_PVDD_MASK (0x1 << 7) +#define MAX98927_BOOST_CTRL0_PVDD_EN_SHIFT (7) + +/* MAX98927_R0052_BROWNOUT_EN */ +#define MAX98927_BROWNOUT_BDE_EN (0x1 << 0) +#define MAX98927_BROWNOUT_AMP_EN (0x1 << 1) +#define MAX98927_BROWNOUT_DSP_EN (0x1 << 2) +#define MAX98927_BROWNOUT_DSP_SHIFT (2) + +/* MAX98927_R0100_SOFT_RESET */ +#define MAX98927_SOFT_RESET (0x1 << 0) + +/* MAX98927_R00FF_GLOBAL_SHDN */ +#define MAX98927_GLOBAL_EN_MASK (0x1 << 0) + +struct max98927_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct max98927_pdata *pdata; + unsigned int spk_gain; + unsigned int sysclk; + unsigned int v_l_slot; + unsigned int i_l_slot; + bool interleave_mode; + unsigned int ch_size; + unsigned int rate; + unsigned int iface; + unsigned int master; + unsigned int digital_gain; +}; +#endif -- cgit v1.2.3 From 0c67fb5544061cb990e07a73e1b7ba1f8ca22479 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 6 Apr 2017 13:52:14 +0100 Subject: ASoC: cs35l35: Add DT binding to specify usage of an external boost supply Add a device tree binding to let the driver know that the amplifier is configured to use an external boost supply. Signed-off-by: Charles Keepax Acked-by: Brian Austin Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/cs35l35.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt index 958f0eabfdfc..457d176dcee0 100644 --- a/Documentation/devicetree/bindings/sound/cs35l35.txt +++ b/Documentation/devicetree/bindings/sound/cs35l35.txt @@ -33,6 +33,10 @@ Optional properties: - cirrus,shared-boost : Boolean to enable ClassH tracking of Advisory Signal if 2 Devices share Boost BST_CTL + - cirrus,external-boost : Boolean to specify the device is using an external + boost supply, note that sharing a boost from another cs35l35 would constitute + using an external supply for the slave device + - cirrus,sp-drv-strength : Value for setting the Serial Port drive strength Table 3-10 of the datasheet lists drive-strength specifications 0 = 1x (Default) -- cgit v1.2.3 From 7480316c265c9fcdbf73b1b8dec061b893b7e987 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Apr 2017 17:37:34 +0200 Subject: ALSA: hda - Allow to enable/disable vmaster build explicitly Another preliminary patch for the dual-codec support: since the support of vmaster over multiple codecs is difficult, simply disable it by a new flag to hda_codec struct. A new user hint is added as well for consistency. Signed-off-by: Takashi Iwai --- Documentation/sound/hd-audio/notes.rst | 2 ++ sound/pci/hda/hda_generic.c | 7 +++++-- sound/pci/hda/hda_generic.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/sound/hd-audio/notes.rst b/Documentation/sound/hd-audio/notes.rst index 9eeb9b468706..f59c3cdbfaf4 100644 --- a/Documentation/sound/hd-audio/notes.rst +++ b/Documentation/sound/hd-audio/notes.rst @@ -494,6 +494,8 @@ add_hp_mic (bool) hp_mic_detect (bool) enable/disable the hp/mic shared input for a single built-in mic case; default true +vmaster (bool) + enable/disable the virtual Master control; default true mixer_nid (int) specifies the widget NID of the analog-loopback mixer diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 443832870a44..2842c82363c0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -196,6 +196,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "hp_mic_detect"); if (val >= 0) spec->suppress_hp_mic_detect = !val; + val = snd_hda_get_bool_hint(codec, "vmaster"); + if (val >= 0) + spec->suppress_vmaster = !val; if (!snd_hda_get_int_hint(codec, "mixer_nid", &val)) spec->mixer_nid = val; @@ -5033,7 +5036,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) } /* if we have no master control, let's create it */ - if (!spec->no_analog && + if (!spec->no_analog && !spec->suppress_vmaster && !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { err = snd_hda_add_vmaster(codec, "Master Playback Volume", spec->vmaster_tlv, slave_pfxs, @@ -5041,7 +5044,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) if (err < 0) return err; } - if (!spec->no_analog && + if (!spec->no_analog && !spec->suppress_vmaster && !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { err = __snd_hda_add_vmaster(codec, "Master Playback Switch", NULL, slave_pfxs, diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index f66fc7e25e07..61772317de46 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -229,6 +229,7 @@ struct hda_gen_spec { unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */ unsigned int power_down_unused:1; /* power down unused widgets */ unsigned int dac_min_mute:1; /* minimal = mute for DACs */ + unsigned int suppress_vmaster:1; /* don't create vmaster kctls */ /* other internal flags */ unsigned int no_analog:1; /* digital I/O only */ -- cgit v1.2.3 From 9e615f63cbafa2147fd302ab495d7b785306bc26 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 31 Mar 2017 15:05:59 -0700 Subject: ASoC: add hi6210-i2s DT bindings Adds DT bindings documentation for the hi6210-i2s driver. Signed-off-by: John Stultz Signed-off-by: Mark Brown --- .../bindings/sound/hisilicon,hi6210-i2s.txt | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt new file mode 100644 index 000000000000..680bb03577ef --- /dev/null +++ b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt @@ -0,0 +1,34 @@ +* Hisilicon 6210 i2s controller + +Required properties: + +- compatible: should be one of the following: + - "hisilicon,hi6210-i2s" +- reg: physical base address of the i2s controller unit and length of + memory mapped region. +- interrupts: should contain the i2s interrupt. +- clocks: a list of phandle + clock-specifier pairs, one for each entry + in clock-names. +- clock-names: should contain following: + - "dacodec" + - "i2s-base" +- dmas: DMA specifiers for tx dma. See the DMA client binding, + Documentation/devicetree/bindings/dma/dma.txt +- dma-names: should be "tx" and "rx" +- hisilicon,sysctrl-syscon: phandle to sysctrl syscon +- #sound-dai-cells: Should be set to 1 (for multi-dai) + +Example for the hi6210 i2s controller: + +i2s0: i2s@f7118000{ + compatible = "hisilicon,hi6210-i2s"; + reg = <0x0 0xf7118000 0x0 0x8000>; /* i2s unit */ + interrupts = ; /* 155 "DigACodec_intr"-32 */ + clocks = <&sys_ctrl HI6220_DACODEC_PCLK>, + <&sys_ctrl HI6220_BBPPLL0_DIV>; + clock-names = "dacodec", "i2s-base"; + dmas = <&dma0 15 &dma0 14>; + dma-names = "rx", "tx"; + hisilicon,sysctrl-syscon = <&sys_ctrl>; + #sound-dai-cells = <1>; +}; -- cgit v1.2.3 From 570c70a60f53ca737ead4e5966c446bf0d39fac9 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 5 Apr 2017 11:32:34 -0300 Subject: ASoC: sgtl5000: Allow LRCLK pad drive strength to be changed Introduce the "lrclk-strength" property to allow LRCLK pad drive strength to be changed via device tree. When running a stress playback loop test on a mx6dl wandboard channel swap can be noticed on about 10% of the times. While debugging this issue I noticed that when probing the SGTL5000 LRCLK pin with the scope the swap did not happen. After removing the probe the swap started to happen again. After changing the LRCLK pad drive strength to the maximum value the issue is gone. Same fix works on a mx6dl Colibri board as well. Signed-off-by: Fabio Estevam Tested-by: Max Krummenacher Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/sgtl5000.txt | 9 +++++++++ sound/soc/codecs/sgtl5000.c | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt index 5666da7b8605..7a73a9d62015 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -26,6 +26,15 @@ Optional properties: If this node is not mentioned or the value is unknown, then the value is set to 1.25V. +- lrclk-strength: the LRCLK pad strength. Possible values are: +0, 1, 2 and 3 as per the table below: + +VDDIO 1.8V 2.5V 3.3V +0 = Disable +1 = 1.66 mA 2.87 mA 4.02 mA +2 = 3.33 mA 5.74 mA 8.03 mA +3 = 4.99 mA 8.61 mA 12.05 mA + Example: codec: sgtl5000@0a { diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 1589325855bc..5a2702edeb77 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -99,6 +99,13 @@ enum sgtl5000_micbias_resistor { SGTL5000_MICBIAS_8K = 8, }; +enum { + I2S_LRCLK_STRENGTH_DISABLE, + I2S_LRCLK_STRENGTH_LOW, + I2S_LRCLK_STRENGTH_MEDIUM, + I2S_LRCLK_STRENGTH_HIGH, +}; + /* sgtl5000 private structure in codec */ struct sgtl5000_priv { int sysclk; /* sysclk rate */ @@ -111,6 +118,7 @@ struct sgtl5000_priv { int revision; u8 micbias_resistor; u8 micbias_voltage; + u8 lrclk_strength; }; /* @@ -1089,6 +1097,7 @@ static int sgtl5000_enable_regulators(struct i2c_client *client) static int sgtl5000_probe(struct snd_soc_codec *codec) { int ret; + u16 reg; struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); /* power up sgtl5000 */ @@ -1118,7 +1127,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) SGTL5000_DAC_MUTE_RIGHT | SGTL5000_DAC_MUTE_LEFT); - snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f); + reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | 0x5f); + snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, reg); snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL, SGTL5000_HP_ZCD_EN | @@ -1347,6 +1357,13 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, } } + sgtl5000->lrclk_strength = I2S_LRCLK_STRENGTH_LOW; + if (!of_property_read_u32(np, "lrclk-strength", &value)) { + if (value > I2S_LRCLK_STRENGTH_HIGH) + value = I2S_LRCLK_STRENGTH_LOW; + sgtl5000->lrclk_strength = value; + } + /* Ensure sgtl5000 will start with sane register values */ sgtl5000_fill_defaults(client); -- cgit v1.2.3 From 86666c083a4eacb798754f2485bbecfedc9fc7d7 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 10 Apr 2017 12:35:11 -0700 Subject: ASoC: Improve hi6210-i2s DT bindings This patch improves the previously submitted hi6210-i2s DT binding, adding extra details to how the multi-dai index value maps to the potential interfaces. (Currently just index 0 -> the S2 interface, as there is only one supported, but in the future other interfaces may be enabled.) Signed-off-by: John Stultz Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt index 680bb03577ef..7a296784eb37 100644 --- a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt +++ b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.txt @@ -17,6 +17,10 @@ Required properties: - dma-names: should be "tx" and "rx" - hisilicon,sysctrl-syscon: phandle to sysctrl syscon - #sound-dai-cells: Should be set to 1 (for multi-dai) + - The dai cell indexes reference the following interfaces: + 0: S2 interface + (Currently that is the only one available, but more may be + supported in the future) Example for the hi6210 i2s controller: @@ -32,3 +36,7 @@ i2s0: i2s@f7118000{ hisilicon,sysctrl-syscon = <&sys_ctrl>; #sound-dai-cells = <1>; }; + +Then when referencing the i2s controller: + sound-dai = <&i2s0 0>; /* index 0 => S2 interface */ + -- cgit v1.2.3 From ddea87ea79927ba0b94e697a505f90fe06c29f84 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 11 Apr 2017 15:49:34 -0300 Subject: ASoC: tas2552: Describe the possible I2C addresses According to the TAS2552 datasheet the possible I2C addresses are: 0x40 - when ADDR pin is 0 0x41 - when ADDR pin is 1 List the possible values for better clarification. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tas2552.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt index 54c1c87c4ad8..ea8abf08b094 100644 --- a/Documentation/devicetree/bindings/sound/tas2552.txt +++ b/Documentation/devicetree/bindings/sound/tas2552.txt @@ -5,7 +5,8 @@ The tas2552 serial control bus communicates through I2C protocols Required properties: - compatible - One of: "ti,tas2552" - TAS2552 - - reg - I2C slave address + - reg - I2C slave address: it can be 0x40 if ADDR pin is 0 + or 0x41 if ADDR pin is 1. - supply-*: Required supply regulators are: "vbat" battery voltage "iovdd" I/O Voltage -- cgit v1.2.3 From 7d8d2c9482474804a5c2b33ea9218d33dbcb48ba Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 11 Apr 2017 15:49:35 -0300 Subject: ASoC: tas2552: Fix typos in the binding doc Fix the following typos: "receive its reference clock" and "selected". Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/tas2552.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt index ea8abf08b094..2d71eb05c1d3 100644 --- a/Documentation/devicetree/bindings/sound/tas2552.txt +++ b/Documentation/devicetree/bindings/sound/tas2552.txt @@ -15,11 +15,11 @@ Required properties: Optional properties: - enable-gpio - gpio pin to enable/disable the device -tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the +tas2552 can receive its reference clock via MCLK, BCLK, IVCLKIN pin or use the internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK. For system integration the dt-bindings/sound/tas2552.h header file provides -defined values to selct and configure the PLL and PDM reference clocks. +defined values to select and configure the PLL and PDM reference clocks. Example: -- cgit v1.2.3 From 23aaf4ced39de2353e2204c8397418ebb771143a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 12 Apr 2017 10:32:00 -0300 Subject: ASoC: fsl ssi doc: Move optional properties to the correct section Properties 'fsl,ssi-asynchronous', 'fsl,playback-dma' and 'fsl,capture-dma' are optional, so move them under the optional section of the document. While at it mention that 'fsl,playback-dma' and 'fsl,capture-dma' only apply to Power Architecture. Signed-off-by: Fabio Estevam Acked-by: Timur Tabi Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/fsl,ssi.txt | 34 ++++++++++++---------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index 5b76be45d18b..d415888e1316 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -20,24 +20,8 @@ Required properties: have. - interrupt-parent: The phandle for the interrupt controller that services interrupts for this device. -- fsl,playback-dma: Phandle to a node for the DMA channel to use for - playback of audio. This is typically dictated by SOC - design. See the notes below. -- fsl,capture-dma: Phandle to a node for the DMA channel to use for - capture (recording) of audio. This is typically dictated - by SOC design. See the notes below. - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. This number is the maximum allowed value for SFCSR[TFWM0]. -- fsl,ssi-asynchronous: - If specified, the SSI is to be programmed in asynchronous - mode. In this mode, pins SRCK, STCK, SRFS, and STFS must - all be connected to valid signals. In synchronous mode, - SRCK and SRFS are ignored. Asynchronous mode allows - playback and capture to use different sample sizes and - sample rates. Some drivers may require that SRCK and STCK - be connected together, and SRFS and STFS be connected - together. This would still allow different sample sizes, - but not different sample rates. - clocks: "ipg" - Required clock for the SSI unit "baud" - Required clock for SSI master mode. Otherwise this clock is not used @@ -61,6 +45,24 @@ Optional properties: - fsl,mode: The operating mode for the AC97 interface only. "ac97-slave" - AC97 mode, SSI is clock slave "ac97-master" - AC97 mode, SSI is clock master +- fsl,ssi-asynchronous: + If specified, the SSI is to be programmed in asynchronous + mode. In this mode, pins SRCK, STCK, SRFS, and STFS must + all be connected to valid signals. In synchronous mode, + SRCK and SRFS are ignored. Asynchronous mode allows + playback and capture to use different sample sizes and + sample rates. Some drivers may require that SRCK and STCK + be connected together, and SRFS and STFS be connected + together. This would still allow different sample sizes, + but not different sample rates. +- fsl,playback-dma: Phandle to a node for the DMA channel to use for + playback of audio. This is typically dictated by SOC + design. See the notes below. + Only used on Power Architecture. +- fsl,capture-dma: Phandle to a node for the DMA channel to use for + capture (recording) of audio. This is typically dictated + by SOC design. See the notes below. + Only used on Power Architecture. Child 'codec' node required properties: - compatible: Compatible list, contains the name of the codec -- cgit v1.2.3 From 06bdf385f66a53b335b324e28a43788b03e6f3e3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 13 Apr 2017 16:52:09 +0100 Subject: ASoC: cs35l35: Allow user to configure IMON SCALE On the chip the IMON signal is a full 24-bits however normally only some of the bits will be sent over the bus. The chip provides a field to select which bits of the IMON will be sent back, this is the only feedback signal that has this feature. Add an additional entry to the cirrus,imon device tree property to allow the IMON scale parameter to be passed. Signed-off-by: Charles Keepax Acked-by: Brian Austin Acked-by: Rob Herring Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/cs35l35.txt | 4 ++-- include/sound/cs35l35.h | 1 + sound/soc/codecs/cs35l35.c | 22 +++++++++++++++------- sound/soc/codecs/cs35l35.h | 3 +++ 4 files changed, 21 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt index 457d176dcee0..016b768bc722 100644 --- a/Documentation/devicetree/bindings/sound/cs35l35.txt +++ b/Documentation/devicetree/bindings/sound/cs35l35.txt @@ -118,8 +118,8 @@ Optional Monitor Signal Format sub-node: Sections 7.44 - 7.53 lists values for the depth, location, and frame for each monitoring signal. - - cirrus,imon : 3 8 bit values to set the depth, location, and frame - of the IMON monitor signal. + - cirrus,imon : 4 8 bit values to set the depth, location, frame and ADC + scale of the IMON monitor signal. - cirrus,vmon : 3 8 bit values to set the depth, location, and frame of the VMON monitor signal. diff --git a/include/sound/cs35l35.h b/include/sound/cs35l35.h index 88744bbd6728..29da899e17e4 100644 --- a/include/sound/cs35l35.h +++ b/include/sound/cs35l35.h @@ -57,6 +57,7 @@ struct monitor_cfg { u8 imon_dpth; u8 imon_loc; u8 imon_frm; + u8 imon_scale; u8 vmon_dpth; u8 vmon_loc; u8 vmon_frm; diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index dc6591adc96d..f8aef5869b03 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -918,6 +918,11 @@ static int cs35l35_codec_probe(struct snd_soc_codec *codec) CS35L35_MON_FRM_MASK, monitor_config->imon_frm << CS35L35_MON_FRM_SHIFT); + regmap_update_bits(cs35l35->regmap, + CS35L35_IMON_SCALE_CTL, + CS35L35_IMON_SCALE_MASK, + monitor_config->imon_scale << + CS35L35_IMON_SCALE_SHIFT); } if (monitor_config->vpmon_specs) { regmap_update_bits(cs35l35->regmap, @@ -1161,7 +1166,9 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, struct classh_cfg *classh_config = &pdata->classh_algo; struct monitor_cfg *monitor_config = &pdata->mon_cfg; unsigned int val32 = 0; - u8 monitor_array[3]; + u8 monitor_array[4]; + const int imon_array_size = ARRAY_SIZE(monitor_array); + const int mon_array_size = imon_array_size - 1; int ret = 0; if (!np) @@ -1302,15 +1309,16 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, monitor_config->is_present = signal_format ? true : false; if (monitor_config->is_present) { ret = of_property_read_u8_array(signal_format, "cirrus,imon", - monitor_array, ARRAY_SIZE(monitor_array)); + monitor_array, imon_array_size); if (!ret) { monitor_config->imon_specs = true; monitor_config->imon_dpth = monitor_array[0]; monitor_config->imon_loc = monitor_array[1]; monitor_config->imon_frm = monitor_array[2]; + monitor_config->imon_scale = monitor_array[3]; } ret = of_property_read_u8_array(signal_format, "cirrus,vmon", - monitor_array, ARRAY_SIZE(monitor_array)); + monitor_array, mon_array_size); if (!ret) { monitor_config->vmon_specs = true; monitor_config->vmon_dpth = monitor_array[0]; @@ -1318,7 +1326,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, monitor_config->vmon_frm = monitor_array[2]; } ret = of_property_read_u8_array(signal_format, "cirrus,vpmon", - monitor_array, ARRAY_SIZE(monitor_array)); + monitor_array, mon_array_size); if (!ret) { monitor_config->vpmon_specs = true; monitor_config->vpmon_dpth = monitor_array[0]; @@ -1326,7 +1334,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, monitor_config->vpmon_frm = monitor_array[2]; } ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon", - monitor_array, ARRAY_SIZE(monitor_array)); + monitor_array, mon_array_size); if (!ret) { monitor_config->vbstmon_specs = true; monitor_config->vbstmon_dpth = monitor_array[0]; @@ -1334,7 +1342,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, monitor_config->vbstmon_frm = monitor_array[2]; } ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat", - monitor_array, ARRAY_SIZE(monitor_array)); + monitor_array, mon_array_size); if (!ret) { monitor_config->vpbrstat_specs = true; monitor_config->vpbrstat_dpth = monitor_array[0]; @@ -1342,7 +1350,7 @@ static int cs35l35_handle_of_data(struct i2c_client *i2c_client, monitor_config->vpbrstat_frm = monitor_array[2]; } ret = of_property_read_u8_array(signal_format, "cirrus,zerofill", - monitor_array, ARRAY_SIZE(monitor_array)); + monitor_array, mon_array_size); if (!ret) { monitor_config->zerofill_specs = true; monitor_config->zerofill_dpth = monitor_array[0]; diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h index 54e9ac536b20..5a6e43a87c4d 100644 --- a/sound/soc/codecs/cs35l35.h +++ b/sound/soc/codecs/cs35l35.h @@ -148,6 +148,9 @@ #define CS35L35_MON_FRM_MASK 0x80 #define CS35L35_MON_FRM_SHIFT 7 +#define CS35L35_IMON_SCALE_MASK 0xF8 +#define CS35L35_IMON_SCALE_SHIFT 3 + #define CS35L35_MS_MASK 0x80 #define CS35L35_MS_SHIFT 7 #define CS35L35_SPMODE_MASK 0x40 -- cgit v1.2.3 From 92c9f05ebc7290e638c7b1b85288918c4fc65d4e Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 21 Apr 2017 19:19:49 +0200 Subject: ASoC: Add Odroid sound DT bindings documentation This patch adds DT binding documentation for Odroid XU3/4 sound subsystem. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/samsung,odroid.txt | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/samsung,odroid.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.txt b/Documentation/devicetree/bindings/sound/samsung,odroid.txt new file mode 100644 index 000000000000..c1ac70cb0afb --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,odroid.txt @@ -0,0 +1,57 @@ +Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec + +Required properties: + + - compatible - "samsung,odroidxu3-audio" - for Odroid XU3 board, + "samsung,odroidxu4-audio" - for Odroid XU4 board + - model - the user-visible name of this sound complex + - 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S + controller + - 'codec' subnode with a 'sound-dai' property containing list of phandles + to the CODEC nodes, first entry must be corresponding to the MAX98090 + CODEC and the second entry must be the phandle of the HDMI IP block node + - clocks - should contain entries matching clock names in the clock-names + property + - clock-names - should contain following entries: + - "epll" - indicating the EPLL output clock + - "i2s_rclk" - indicating the RCLK (root) clock of the I2S0 controller + - samsung,audio-widgets - this property specifies off-codec audio elements + like headphones or speakers, for details see widgets.txt + - samsung,audio-routing - a list of the connections between audio + components; each entry is a pair of strings, the first being the + connection's sink, the second being the connection's source; + valid names for sources and sinks are the MAX98090's pins (as + documented in its binding), and the jacks on the board + + For Odroid X2: + "Headphone Jack", "Mic Jack", "DMIC" + + For Odroid U3, XU3: + "Headphone Jack", "Speakers" + + For Odroid XU4: + no entries + +Example: + +sound { + compatible = "samsung,odroidxu3-audio"; + samsung,cpu-dai = <&i2s0>; + samsung,codec-dai = <&max98090>; + model = "Odroid-XU3"; + samsung,audio-routing = + "Headphone Jack", "HPL", + "Headphone Jack", "HPR", + "IN1", "Mic Jack", + "Mic Jack", "MICBIAS"; + + clocks = <&clock CLK_FOUT_EPLL>, <&i2s0 CLK_I2S_RCLK_SRC>; + clock-names = "epll", "sclk_i2s"; + + cpu { + sound-dai = <&i2s0 0>; + }; + codec { + sound-dai = <&hdmi>, <&max98090>; + }; +}; -- cgit v1.2.3 From dfeabded04962ed2de9dca489de228801df25de6 Mon Sep 17 00:00:00 2001 From: John Hsu Date: Thu, 20 Apr 2017 17:25:11 +0800 Subject: ASoC: nau8824: new driver Add driver for NAU88L24. Signed-off-by: John Hsu Signed-off-by: John Hsu Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/nau8824.txt | 88 + sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/nau8824.c | 1837 ++++++++++++++++++++ sound/soc/codecs/nau8824.h | 466 +++++ 5 files changed, 2398 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nau8824.txt create mode 100644 sound/soc/codecs/nau8824.c create mode 100644 sound/soc/codecs/nau8824.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/nau8824.txt b/Documentation/devicetree/bindings/sound/nau8824.txt new file mode 100644 index 000000000000..e0058b97e49a --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nau8824.txt @@ -0,0 +1,88 @@ +Nuvoton NAU8824 audio codec + +This device supports I2C only. + +Required properties: + - compatible : Must be "nuvoton,nau8824" + + - reg : the I2C address of the device. This is either 0x1a (CSB=0) or 0x1b (CSB=1). + +Optional properties: + - nuvoton,jkdet-polarity: JKDET pin polarity. 0 - active high, 1 - active low. + + - nuvoton,vref-impedance: VREF Impedance selection + 0 - Open + 1 - 25 kOhm + 2 - 125 kOhm + 3 - 2.5 kOhm + + - nuvoton,micbias-voltage: Micbias voltage level. + 0 - VDDA + 1 - VDDA + 2 - VDDA * 1.1 + 3 - VDDA * 1.2 + 4 - VDDA * 1.3 + 5 - VDDA * 1.4 + 6 - VDDA * 1.53 + 7 - VDDA * 1.53 + + - nuvoton,sar-threshold-num: Number of buttons supported + - nuvoton,sar-threshold: Impedance threshold for each button. Array that contains up to 8 buttons configuration. SAR value is calculated as + SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R) + where MICBIAS is configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by 'nuvoton,sar-voltage', R - button impedance. + Refer datasheet section 10.2 for more information about threshold calculation. + + - nuvoton,sar-hysteresis: Button impedance measurement hysteresis. + + - nuvoton,sar-voltage: Reference voltage for button impedance measurement. + 0 - VDDA + 1 - VDDA + 2 - VDDA * 1.1 + 3 - VDDA * 1.2 + 4 - VDDA * 1.3 + 5 - VDDA * 1.4 + 6 - VDDA * 1.53 + 7 - VDDA * 1.53 + + - nuvoton,sar-compare-time: SAR compare time + 0 - 500 ns + 1 - 1 us + 2 - 2 us + 3 - 4 us + + - nuvoton,sar-sampling-time: SAR sampling time + 0 - 2 us + 1 - 4 us + 2 - 8 us + 3 - 16 us + + - nuvoton,short-key-debounce: Button short key press debounce time. + 0 - 30 ms + 1 - 50 ms + 2 - 100 ms + + - nuvoton,jack-eject-debounce: Jack ejection debounce time. + 0 - 0 ms + 1 - 1 ms + 2 - 10 ms + + +Example: + + headset: nau8824@1a { + compatible = "nuvoton,nau8824"; + reg = <0x1a>; + interrupt-parent = <&gpio>; + interrupts = ; + nuvoton,vref-impedance = <2>; + nuvoton,micbias-voltage = <6>; + // Setup 4 buttons impedance according to Android specification + nuvoton,sar-threshold-num = <4>; + nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>; + nuvoton,sar-hysteresis = <0>; + nuvoton,sar-voltage = <6>; + nuvoton,sar-compare-time = <1>; + nuvoton,sar-sampling-time = <1>; + nuvoton,short-key-debounce = <0>; + nuvoton,jack-eject-debounce = <1>; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e49e9da7f1f6..fc6162191da6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -97,6 +97,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ML26124 if I2C select SND_SOC_NAU8540 if I2C select SND_SOC_NAU8810 if I2C + select SND_SOC_NAU8824 if I2C select SND_SOC_NAU8825 if I2C select SND_SOC_HDMI_CODEC select SND_SOC_PCM1681 if I2C @@ -1116,6 +1117,10 @@ config SND_SOC_NAU8810 tristate "Nuvoton Technology Corporation NAU88C10 CODEC" depends on I2C +config SND_SOC_NAU8824 + tristate "Nuvoton Technology Corporation NAU88L24 CODEC" + depends on I2C + config SND_SOC_NAU8825 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 1796cb987e71..c1929e1f1b65 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -92,6 +92,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o +snd-soc-nau8824-objs := nau8824.o snd-soc-nau8825-objs := nau8825.o snd-soc-hdmi-codec-objs := hdmi-codec.o snd-soc-pcm1681-objs := pcm1681.o @@ -321,6 +322,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o +obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c new file mode 100644 index 000000000000..18552ed43924 --- /dev/null +++ b/sound/soc/codecs/nau8824.c @@ -0,0 +1,1837 @@ +/* + * NAU88L24 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "nau8824.h" + + +static int nau8824_config_sysclk(struct nau8824 *nau8824, + int clk_id, unsigned int freq); +static bool nau8824_is_jack_inserted(struct nau8824 *nau8824); + +/* the ADC threshold of headset */ +#define DMIC_CLK 3072000 + +/* the ADC threshold of headset */ +#define HEADSET_SARADC_THD 0x80 + +/* the parameter threshold of FLL */ +#define NAU_FREF_MAX 13500000 +#define NAU_FVCO_MAX 124000000 +#define NAU_FVCO_MIN 90000000 + +/* scaling for mclk from sysclk_src output */ +static const struct nau8824_fll_attr mclk_src_scaling[] = { + { 1, 0x0 }, + { 2, 0x2 }, + { 4, 0x3 }, + { 8, 0x4 }, + { 16, 0x5 }, + { 32, 0x6 }, + { 3, 0x7 }, + { 6, 0xa }, + { 12, 0xb }, + { 24, 0xc }, +}; + +/* ratio for input clk freq */ +static const struct nau8824_fll_attr fll_ratio[] = { + { 512000, 0x01 }, + { 256000, 0x02 }, + { 128000, 0x04 }, + { 64000, 0x08 }, + { 32000, 0x10 }, + { 8000, 0x20 }, + { 4000, 0x40 }, +}; + +static const struct nau8824_fll_attr fll_pre_scalar[] = { + { 1, 0x0 }, + { 2, 0x1 }, + { 4, 0x2 }, + { 8, 0x3 }, +}; + +/* the maximum frequency of CLK_ADC and CLK_DAC */ +#define CLK_DA_AD_MAX 6144000 + +/* over sampling rate */ +static const struct nau8824_osr_attr osr_dac_sel[] = { + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 256, 0 }, /* OSR 256, SRC 1 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 0, 0 }, + { 32, 3 }, /* OSR 32, SRC 1/8 */ +}; + +static const struct nau8824_osr_attr osr_adc_sel[] = { + { 32, 3 }, /* OSR 32, SRC 1/8 */ + { 64, 2 }, /* OSR 64, SRC 1/4 */ + { 128, 1 }, /* OSR 128, SRC 1/2 */ + { 256, 0 }, /* OSR 256, SRC 1 */ +}; + +static const struct reg_default nau8824_reg_defaults[] = { + { NAU8824_REG_ENA_CTRL, 0x0000 }, + { NAU8824_REG_CLK_GATING_ENA, 0x0000 }, + { NAU8824_REG_CLK_DIVIDER, 0x0000 }, + { NAU8824_REG_FLL1, 0x0000 }, + { NAU8824_REG_FLL2, 0x3126 }, + { NAU8824_REG_FLL3, 0x0008 }, + { NAU8824_REG_FLL4, 0x0010 }, + { NAU8824_REG_FLL5, 0xC000 }, + { NAU8824_REG_FLL6, 0x6000 }, + { NAU8824_REG_FLL_VCO_RSV, 0xF13C }, + { NAU8824_REG_JACK_DET_CTRL, 0x0000 }, + { NAU8824_REG_INTERRUPT_SETTING_1, 0x0000 }, + { NAU8824_REG_IRQ, 0x0000 }, + { NAU8824_REG_CLEAR_INT_REG, 0x0000 }, + { NAU8824_REG_INTERRUPT_SETTING, 0x1000 }, + { NAU8824_REG_SAR_ADC, 0x0015 }, + { NAU8824_REG_VDET_COEFFICIENT, 0x0110 }, + { NAU8824_REG_VDET_THRESHOLD_1, 0x0000 }, + { NAU8824_REG_VDET_THRESHOLD_2, 0x0000 }, + { NAU8824_REG_VDET_THRESHOLD_3, 0x0000 }, + { NAU8824_REG_VDET_THRESHOLD_4, 0x0000 }, + { NAU8824_REG_GPIO_SEL, 0x0000 }, + { NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 0x000B }, + { NAU8824_REG_PORT0_I2S_PCM_CTRL_2, 0x0010 }, + { NAU8824_REG_PORT0_LEFT_TIME_SLOT, 0x0000 }, + { NAU8824_REG_PORT0_RIGHT_TIME_SLOT, 0x0000 }, + { NAU8824_REG_TDM_CTRL, 0x0000 }, + { NAU8824_REG_ADC_HPF_FILTER, 0x0000 }, + { NAU8824_REG_ADC_FILTER_CTRL, 0x0002 }, + { NAU8824_REG_DAC_FILTER_CTRL_1, 0x0000 }, + { NAU8824_REG_DAC_FILTER_CTRL_2, 0x0000 }, + { NAU8824_REG_NOTCH_FILTER_1, 0x0000 }, + { NAU8824_REG_NOTCH_FILTER_2, 0x0000 }, + { NAU8824_REG_EQ1_LOW, 0x112C }, + { NAU8824_REG_EQ2_EQ3, 0x2C2C }, + { NAU8824_REG_EQ4_EQ5, 0x2C2C }, + { NAU8824_REG_ADC_CH0_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_CH1_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_CH2_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_CH3_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_DAC_MUTE_CTRL, 0x0000 }, + { NAU8824_REG_DAC_CH0_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_DAC_CH1_DGAIN_CTRL, 0x0100 }, + { NAU8824_REG_ADC_TO_DAC_ST, 0x0000 }, + { NAU8824_REG_DRC_KNEE_IP12_ADC_CH01, 0x1486 }, + { NAU8824_REG_DRC_KNEE_IP34_ADC_CH01, 0x0F12 }, + { NAU8824_REG_DRC_SLOPE_ADC_CH01, 0x25FF }, + { NAU8824_REG_DRC_ATKDCY_ADC_CH01, 0x3457 }, + { NAU8824_REG_DRC_KNEE_IP12_ADC_CH23, 0x1486 }, + { NAU8824_REG_DRC_KNEE_IP34_ADC_CH23, 0x0F12 }, + { NAU8824_REG_DRC_SLOPE_ADC_CH23, 0x25FF }, + { NAU8824_REG_DRC_ATKDCY_ADC_CH23, 0x3457 }, + { NAU8824_REG_DRC_GAINL_ADC0, 0x0200 }, + { NAU8824_REG_DRC_GAINL_ADC1, 0x0200 }, + { NAU8824_REG_DRC_GAINL_ADC2, 0x0200 }, + { NAU8824_REG_DRC_GAINL_ADC3, 0x0200 }, + { NAU8824_REG_DRC_KNEE_IP12_DAC, 0x1486 }, + { NAU8824_REG_DRC_KNEE_IP34_DAC, 0x0F12 }, + { NAU8824_REG_DRC_SLOPE_DAC, 0x25F9 }, + { NAU8824_REG_DRC_ATKDCY_DAC, 0x3457 }, + { NAU8824_REG_DRC_GAIN_DAC_CH0, 0x0200 }, + { NAU8824_REG_DRC_GAIN_DAC_CH1, 0x0200 }, + { NAU8824_REG_MODE, 0x0000 }, + { NAU8824_REG_MODE1, 0x0000 }, + { NAU8824_REG_MODE2, 0x0000 }, + { NAU8824_REG_CLASSG, 0x0000 }, + { NAU8824_REG_OTP_EFUSE, 0x0000 }, + { NAU8824_REG_OTPDOUT_1, 0x0000 }, + { NAU8824_REG_OTPDOUT_2, 0x0000 }, + { NAU8824_REG_MISC_CTRL, 0x0000 }, + { NAU8824_REG_I2C_TIMEOUT, 0xEFFF }, + { NAU8824_REG_TEST_MODE, 0x0000 }, + { NAU8824_REG_I2C_DEVICE_ID, 0x1AF1 }, + { NAU8824_REG_SAR_ADC_DATA_OUT, 0x00FF }, + { NAU8824_REG_BIAS_ADJ, 0x0000 }, + { NAU8824_REG_PGA_GAIN, 0x0000 }, + { NAU8824_REG_TRIM_SETTINGS, 0x0000 }, + { NAU8824_REG_ANALOG_CONTROL_1, 0x0000 }, + { NAU8824_REG_ANALOG_CONTROL_2, 0x0000 }, + { NAU8824_REG_ENABLE_LO, 0x0000 }, + { NAU8824_REG_GAIN_LO, 0x0000 }, + { NAU8824_REG_CLASSD_GAIN_1, 0x0000 }, + { NAU8824_REG_CLASSD_GAIN_2, 0x0000 }, + { NAU8824_REG_ANALOG_ADC_1, 0x0011 }, + { NAU8824_REG_ANALOG_ADC_2, 0x0020 }, + { NAU8824_REG_RDAC, 0x0008 }, + { NAU8824_REG_MIC_BIAS, 0x0006 }, + { NAU8824_REG_HS_VOLUME_CONTROL, 0x0000 }, + { NAU8824_REG_BOOST, 0x0000 }, + { NAU8824_REG_FEPGA, 0x0000 }, + { NAU8824_REG_FEPGA_II, 0x0000 }, + { NAU8824_REG_FEPGA_SE, 0x0000 }, + { NAU8824_REG_FEPGA_ATTENUATION, 0x0000 }, + { NAU8824_REG_ATT_PORT0, 0x0000 }, + { NAU8824_REG_ATT_PORT1, 0x0000 }, + { NAU8824_REG_POWER_UP_CONTROL, 0x0000 }, + { NAU8824_REG_CHARGE_PUMP_CONTROL, 0x0300 }, + { NAU8824_REG_CHARGE_PUMP_INPUT, 0x0013 }, +}; + +static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout) +{ + int ret; + + if (timeout) { + ret = down_timeout(&nau8824->jd_sem, timeout); + if (ret < 0) + dev_warn(nau8824->dev, "Acquire semaphone timeout\n"); + } else { + ret = down_interruptible(&nau8824->jd_sem); + if (ret < 0) + dev_warn(nau8824->dev, "Acquire semaphone fail\n"); + } + + return ret; +} + +static inline void nau8824_sema_release(struct nau8824 *nau8824) +{ + up(&nau8824->jd_sem); +} + +static bool nau8824_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8824_REG_ENA_CTRL ... NAU8824_REG_FLL_VCO_RSV: + case NAU8824_REG_JACK_DET_CTRL: + case NAU8824_REG_INTERRUPT_SETTING_1: + case NAU8824_REG_IRQ: + case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4: + case NAU8824_REG_GPIO_SEL: + case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL: + case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5: + case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST: + case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 ... NAU8824_REG_DRC_GAINL_ADC3: + case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_GAIN_DAC_CH1: + case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE: + case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2: + case NAU8824_REG_I2C_TIMEOUT: + case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT: + case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2: + case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1: + case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_INPUT: + return true; + default: + return false; + } + +} + +static bool nau8824_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8824_REG_RESET ... NAU8824_REG_FLL_VCO_RSV: + case NAU8824_REG_JACK_DET_CTRL: + case NAU8824_REG_INTERRUPT_SETTING_1: + case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4: + case NAU8824_REG_GPIO_SEL: + case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL: + case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5: + case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST: + case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01: + case NAU8824_REG_DRC_KNEE_IP34_ADC_CH01: + case NAU8824_REG_DRC_SLOPE_ADC_CH01: + case NAU8824_REG_DRC_ATKDCY_ADC_CH01: + case NAU8824_REG_DRC_KNEE_IP12_ADC_CH23: + case NAU8824_REG_DRC_KNEE_IP34_ADC_CH23: + case NAU8824_REG_DRC_SLOPE_ADC_CH23: + case NAU8824_REG_DRC_ATKDCY_ADC_CH23: + case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_ATKDCY_DAC: + case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE: + case NAU8824_REG_I2C_TIMEOUT: + case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2: + case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1: + case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_CONTROL: + return true; + default: + return false; + } +} + +static bool nau8824_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8824_REG_RESET: + case NAU8824_REG_IRQ ... NAU8824_REG_CLEAR_INT_REG: + case NAU8824_REG_DRC_GAINL_ADC0 ... NAU8824_REG_DRC_GAINL_ADC3: + case NAU8824_REG_DRC_GAIN_DAC_CH0 ... NAU8824_REG_DRC_GAIN_DAC_CH1: + case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2: + case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT: + case NAU8824_REG_CHARGE_PUMP_INPUT: + return true; + default: + return false; + } +} + +static const char * const nau8824_companding[] = { + "Off", "NC", "u-law", "A-law" }; + +static const struct soc_enum nau8824_companding_adc_enum = + SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 12, + ARRAY_SIZE(nau8824_companding), nau8824_companding); + +static const struct soc_enum nau8824_companding_dac_enum = + SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 14, + ARRAY_SIZE(nau8824_companding), nau8824_companding); + +static const char * const nau8824_adc_decimation[] = { + "32", "64", "128", "256" }; + +static const struct soc_enum nau8824_adc_decimation_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_FILTER_CTRL, 0, + ARRAY_SIZE(nau8824_adc_decimation), nau8824_adc_decimation); + +static const char * const nau8824_dac_oversampl[] = { + "64", "256", "128", "", "32" }; + +static const struct soc_enum nau8824_dac_oversampl_enum = + SOC_ENUM_SINGLE(NAU8824_REG_DAC_FILTER_CTRL_1, 0, + ARRAY_SIZE(nau8824_dac_oversampl), nau8824_dac_oversampl); + +static const char * const nau8824_input_channel[] = { + "Input CH0", "Input CH1", "Input CH2", "Input CH3" }; + +static const struct soc_enum nau8824_adc_ch0_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH0_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const struct soc_enum nau8824_adc_ch1_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH1_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const struct soc_enum nau8824_adc_ch2_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH2_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const struct soc_enum nau8824_adc_ch3_enum = + SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH3_DGAIN_CTRL, 9, + ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel); + +static const char * const nau8824_tdm_slot[] = { + "Slot 0", "Slot 1", "Slot 2", "Slot 3" }; + +static const struct soc_enum nau8824_dac_left_sel_enum = + SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 6, + ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot); + +static const struct soc_enum nau8824_dac_right_sel_enum = + SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 4, + ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot); + +static const DECLARE_TLV_DB_MINMAX_MUTE(spk_vol_tlv, 0, 2400); +static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -3000, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 200, 0); +static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, -12800, 50, 0); + +static const struct snd_kcontrol_new nau8824_snd_controls[] = { + SOC_ENUM("ADC Companding", nau8824_companding_adc_enum), + SOC_ENUM("DAC Companding", nau8824_companding_dac_enum), + + SOC_ENUM("ADC Decimation Rate", nau8824_adc_decimation_enum), + SOC_ENUM("DAC Oversampling Rate", nau8824_dac_oversampl_enum), + + SOC_SINGLE_TLV("Speaker Right from DACR Volume", + NAU8824_REG_CLASSD_GAIN_1, 8, 0x1f, 0, spk_vol_tlv), + SOC_SINGLE_TLV("Speaker Left from DACL Volume", + NAU8824_REG_CLASSD_GAIN_2, 0, 0x1f, 0, spk_vol_tlv), + SOC_SINGLE_TLV("Speaker Left from DACR Volume", + NAU8824_REG_CLASSD_GAIN_1, 0, 0x1f, 0, spk_vol_tlv), + SOC_SINGLE_TLV("Speaker Right from DACL Volume", + NAU8824_REG_CLASSD_GAIN_2, 8, 0x1f, 0, spk_vol_tlv), + + SOC_SINGLE_TLV("Headphone Right from DACR Volume", + NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv), + SOC_SINGLE_TLV("Headphone Left from DACL Volume", + NAU8824_REG_ATT_PORT0, 0, 0x1f, 0, hp_vol_tlv), + SOC_SINGLE_TLV("Headphone Right from DACL Volume", + NAU8824_REG_ATT_PORT1, 8, 0x1f, 0, hp_vol_tlv), + SOC_SINGLE_TLV("Headphone Left from DACR Volume", + NAU8824_REG_ATT_PORT1, 0, 0x1f, 0, hp_vol_tlv), + + SOC_SINGLE_TLV("Mic1 Volume", NAU8824_REG_FEPGA_II, + NAU8824_FEPGA_GAINL_SFT, 0x12, 0, mic_vol_tlv), + SOC_SINGLE_TLV("Mic2 Volume", NAU8824_REG_FEPGA_II, + NAU8824_FEPGA_GAINR_SFT, 0x12, 0, mic_vol_tlv), + + SOC_SINGLE_TLV("DMIC1 Volume", NAU8824_REG_ADC_CH0_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + SOC_SINGLE_TLV("DMIC2 Volume", NAU8824_REG_ADC_CH1_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + SOC_SINGLE_TLV("DMIC3 Volume", NAU8824_REG_ADC_CH2_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + SOC_SINGLE_TLV("DMIC4 Volume", NAU8824_REG_ADC_CH3_DGAIN_CTRL, + 0, 0x164, 0, dmic_vol_tlv), + + SOC_ENUM("ADC CH0 Select", nau8824_adc_ch0_enum), + SOC_ENUM("ADC CH1 Select", nau8824_adc_ch1_enum), + SOC_ENUM("ADC CH2 Select", nau8824_adc_ch2_enum), + SOC_ENUM("ADC CH3 Select", nau8824_adc_ch3_enum), + + SOC_SINGLE("ADC CH0 TX Switch", NAU8824_REG_TDM_CTRL, 0, 1, 0), + SOC_SINGLE("ADC CH1 TX Switch", NAU8824_REG_TDM_CTRL, 1, 1, 0), + SOC_SINGLE("ADC CH2 TX Switch", NAU8824_REG_TDM_CTRL, 2, 1, 0), + SOC_SINGLE("ADC CH3 TX Switch", NAU8824_REG_TDM_CTRL, 3, 1, 0), + + SOC_ENUM("DACL Channel Source", nau8824_dac_left_sel_enum), + SOC_ENUM("DACR Channel Source", nau8824_dac_right_sel_enum), + + SOC_SINGLE("DACL LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 0, 1, 0), + SOC_SINGLE("DACR LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 1, 1, 0), +}; + +static int nau8824_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 nau8824 *nau8824 = 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(nau8824->regmap, NAU8824_REG_ENABLE_LO, + NAU8824_TEST_DAC_EN, 0); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO, + NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8824_spk_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 nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_ANALOG_CONTROL_2, + NAU8824_CLASSD_CLAMP_DIS, NAU8824_CLASSD_CLAMP_DIS); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_ANALOG_CONTROL_2, + NAU8824_CLASSD_CLAMP_DIS, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nau8824_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 nau8824 *nau8824 = 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(nau8824->regmap, + NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_JAMNODCLOW, NAU8824_JAMNODCLOW); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_JAMNODCLOW, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int system_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + /* Set clock source to disable or internal clock before the + * playback or capture end. Codec needs clock for Jack + * detection and button press if jack inserted; otherwise, + * the clock should be closed. + */ + if (nau8824_is_jack_inserted(nau8824)) { + nau8824_config_sysclk(nau8824, + NAU8824_CLK_INTERNAL, 0); + } else { + nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); + } + } + return 0; +} + +static int dmic_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + int src; + + /* The DMIC clock is gotten from system clock (256fs) divided by + * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or + * less than 3.072 MHz. + */ + for (src = 0; src < 5; src++) { + if ((0x1 << (8 - src)) * nau8824->fs <= DMIC_CLK) + break; + } + dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256); + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT)); + + return 0; +} + +static const struct snd_kcontrol_new nau8824_adc_ch0_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH0_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_ch1_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH1_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_ch2_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH2_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_ch3_dmic = + SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL, + NAU8824_ADC_CH3_DMIC_SFT, 1, 0); + +static const struct snd_kcontrol_new nau8824_adc_left_mixer[] = { + SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODEL_MIC1_SFT, 1, 0), + SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODEL_HSMIC_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new nau8824_adc_right_mixer[] = { + SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODER_MIC2_SFT, 1, 0), + SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODER_HSMIC_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new nau8824_hp_left_mixer[] = { + SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACR_HPL_EN_SFT, 1, 0), + SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACL_HPL_EN_SFT, 1, 0), +}; + +static const struct snd_kcontrol_new nau8824_hp_right_mixer[] = { + SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACL_HPR_EN_SFT, 1, 0), + SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO, + NAU8824_DACR_HPR_EN_SFT, 1, 0), +}; + +static const char * const nau8824_dac_src[] = { "DACL", "DACR" }; + +static SOC_ENUM_SINGLE_DECL( + nau8824_dacl_enum, NAU8824_REG_DAC_CH0_DGAIN_CTRL, + NAU8824_DAC_CH0_SEL_SFT, nau8824_dac_src); + +static SOC_ENUM_SINGLE_DECL( + nau8824_dacr_enum, NAU8824_REG_DAC_CH1_DGAIN_CTRL, + NAU8824_DAC_CH1_SEL_SFT, nau8824_dac_src); + +static const struct snd_kcontrol_new nau8824_dacl_mux = + SOC_DAPM_ENUM("DACL Source", nau8824_dacl_enum); + +static const struct snd_kcontrol_new nau8824_dacr_mux = + SOC_DAPM_ENUM("DACR Source", nau8824_dacr_enum); + + +static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0, + system_clock_control, SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("HSMIC1"), + SND_SOC_DAPM_INPUT("HSMIC2"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + + SND_SOC_DAPM_SUPPLY("SAR", NAU8824_REG_SAR_ADC, + NAU8824_SAR_ADC_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8824_REG_MIC_BIAS, + NAU8824_MICBIAS_POWERUP_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC12 Power", NAU8824_REG_BIAS_ADJ, + NAU8824_DMIC1_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC34 Power", NAU8824_REG_BIAS_ADJ, + NAU8824_DMIC2_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0, + dmic_clock_control, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SWITCH("DMIC1 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch0_dmic), + SND_SOC_DAPM_SWITCH("DMIC2 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch1_dmic), + SND_SOC_DAPM_SWITCH("DMIC3 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch2_dmic), + SND_SOC_DAPM_SWITCH("DMIC4 Enable", SND_SOC_NOPM, + 0, 0, &nau8824_adc_ch3_dmic), + + SND_SOC_DAPM_MIXER("Left ADC", NAU8824_REG_POWER_UP_CONTROL, + 12, 0, nau8824_adc_left_mixer, + ARRAY_SIZE(nau8824_adc_left_mixer)), + SND_SOC_DAPM_MIXER("Right ADC", NAU8824_REG_POWER_UP_CONTROL, + 13, 0, nau8824_adc_right_mixer, + ARRAY_SIZE(nau8824_adc_right_mixer)), + + SND_SOC_DAPM_ADC("ADCL", NULL, NAU8824_REG_ANALOG_ADC_2, + NAU8824_ADCL_EN_SFT, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2, + NAU8824_ADCR_EN_SFT, 0), + + SND_SOC_DAPM_AIF_OUT("AIFTX", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFRX", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACL", NULL, NAU8824_REG_RDAC, + NAU8824_DACL_EN_SFT, 0), + SND_SOC_DAPM_SUPPLY("DACL Clock", NAU8824_REG_RDAC, + NAU8824_DACL_CLK_SFT, 0, NULL, 0), + SND_SOC_DAPM_DAC("DACR", NULL, NAU8824_REG_RDAC, + NAU8824_DACR_EN_SFT, 0), + SND_SOC_DAPM_SUPPLY("DACR Clock", NAU8824_REG_RDAC, + NAU8824_DACR_CLK_SFT, 0, NULL, 0), + + SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacl_mux), + SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacr_mux), + + SND_SOC_DAPM_PGA_S("Output DACL", 0, NAU8824_REG_CHARGE_PUMP_CONTROL, + 8, 1, nau8824_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_S("Output DACR", 0, NAU8824_REG_CHARGE_PUMP_CONTROL, + 9, 1, nau8824_output_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_S("ClassD", 0, NAU8824_REG_CLASSD_GAIN_1, + NAU8824_CLASSD_EN_SFT, 0, nau8824_spk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("Left Headphone", NAU8824_REG_CLASSG, + NAU8824_CLASSG_LDAC_EN_SFT, 0, nau8824_hp_left_mixer, + ARRAY_SIZE(nau8824_hp_left_mixer)), + SND_SOC_DAPM_MIXER("Right Headphone", NAU8824_REG_CLASSG, + NAU8824_CLASSG_RDAC_EN_SFT, 0, nau8824_hp_right_mixer, + ARRAY_SIZE(nau8824_hp_right_mixer)), + SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_CHARGE_PUMP_EN_SFT, 0, nau8824_pump_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA("Output Driver L", + NAU8824_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Output Driver R", + NAU8824_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Main Driver L", + NAU8824_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Main Driver R", + NAU8824_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Boost Driver", NAU8824_REG_BOOST, + NAU8824_HP_BOOST_DIS_SFT, 1, NULL, 0), + SND_SOC_DAPM_PGA("Class G", NAU8824_REG_CLASSG, + NAU8824_CLASSG_EN_SFT, 0, NULL, 0), + + SND_SOC_DAPM_OUTPUT("SPKOUTL"), + SND_SOC_DAPM_OUTPUT("SPKOUTR"), + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), +}; + +static const struct snd_soc_dapm_route nau8824_dapm_routes[] = { + {"DMIC1 Enable", "Switch", "DMIC1"}, + {"DMIC2 Enable", "Switch", "DMIC2"}, + {"DMIC3 Enable", "Switch", "DMIC3"}, + {"DMIC4 Enable", "Switch", "DMIC4"}, + + {"DMIC1", NULL, "DMIC12 Power"}, + {"DMIC2", NULL, "DMIC12 Power"}, + {"DMIC3", NULL, "DMIC34 Power"}, + {"DMIC4", NULL, "DMIC34 Power"}, + {"DMIC12 Power", NULL, "DMIC Clock"}, + {"DMIC34 Power", NULL, "DMIC Clock"}, + + {"Left ADC", "MIC Switch", "MIC1"}, + {"Left ADC", "HSMIC Switch", "HSMIC1"}, + {"Right ADC", "MIC Switch", "MIC2"}, + {"Right ADC", "HSMIC Switch", "HSMIC2"}, + + {"ADCL", NULL, "Left ADC"}, + {"ADCR", NULL, "Right ADC"}, + + {"AIFTX", NULL, "MICBIAS"}, + {"AIFTX", NULL, "ADCL"}, + {"AIFTX", NULL, "ADCR"}, + {"AIFTX", NULL, "DMIC1 Enable"}, + {"AIFTX", NULL, "DMIC2 Enable"}, + {"AIFTX", NULL, "DMIC3 Enable"}, + {"AIFTX", NULL, "DMIC4 Enable"}, + + {"AIFTX", NULL, "System Clock"}, + {"AIFRX", NULL, "System Clock"}, + + {"DACL", NULL, "AIFRX"}, + {"DACL", NULL, "DACL Clock"}, + {"DACR", NULL, "AIFRX"}, + {"DACR", NULL, "DACR Clock"}, + + {"DACL Mux", "DACL", "DACL"}, + {"DACL Mux", "DACR", "DACR"}, + {"DACR Mux", "DACL", "DACL"}, + {"DACR Mux", "DACR", "DACR"}, + + {"Output DACL", NULL, "DACL Mux"}, + {"Output DACR", NULL, "DACR Mux"}, + + {"ClassD", NULL, "Output DACL"}, + {"ClassD", NULL, "Output DACR"}, + + {"Left Headphone", "DAC Left Switch", "Output DACL"}, + {"Left Headphone", "DAC Right Switch", "Output DACR"}, + {"Right Headphone", "DAC Left Switch", "Output DACL"}, + {"Right Headphone", "DAC Right Switch", "Output DACR"}, + + {"Charge Pump", NULL, "Left Headphone"}, + {"Charge Pump", NULL, "Right Headphone"}, + {"Output Driver L", NULL, "Charge Pump"}, + {"Output Driver R", NULL, "Charge Pump"}, + {"Main Driver L", NULL, "Output Driver L"}, + {"Main Driver R", NULL, "Output Driver R"}, + {"Class G", NULL, "Main Driver L"}, + {"Class G", NULL, "Main Driver R"}, + {"HP Boost Driver", NULL, "Class G"}, + + {"SPKOUTL", NULL, "ClassD"}, + {"SPKOUTR", NULL, "ClassD"}, + {"HPOL", NULL, "HP Boost Driver"}, + {"HPOR", NULL, "HP Boost Driver"}, +}; + +static bool nau8824_is_jack_inserted(struct nau8824 *nau8824) +{ + struct snd_soc_jack *jack = nau8824->jack; + bool insert = FALSE; + + if (nau8824->irq && jack) + insert = jack->status & SND_JACK_HEADPHONE; + + return insert; +} + +static void nau8824_int_status_clear_all(struct regmap *regmap) +{ + int active_irq, clear_irq, i; + + /* Reset the intrruption status from rightmost bit if the corres- + * ponding irq event occurs. + */ + regmap_read(regmap, NAU8824_REG_IRQ, &active_irq); + for (i = 0; i < NAU8824_REG_DATA_LEN; i++) { + clear_irq = (0x1 << i); + if (active_irq & clear_irq) + regmap_write(regmap, + NAU8824_REG_CLEAR_INT_REG, clear_irq); + } +} + +static void nau8824_eject_jack(struct nau8824 *nau8824) +{ + struct snd_soc_dapm_context *dapm = nau8824->dapm; + struct regmap *regmap = nau8824->regmap; + + /* Clear all interruption status */ + nau8824_int_status_clear_all(regmap); + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + snd_soc_dapm_sync(dapm); + + /* Enable the insertion interruption, disable the ejection + * interruption, and then bypass de-bounce circuit. + */ + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS | + NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, + NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS | + NAU8824_IRQ_EJECT_DIS); + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN, + NAU8824_IRQ_INSERT_EN); + regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE); + + /* Close clock for jack type detection at manual mode */ + nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); +} + +static void nau8824_jdet_work(struct work_struct *work) +{ + struct nau8824 *nau8824 = container_of( + work, struct nau8824, jdet_work); + struct snd_soc_dapm_context *dapm = nau8824->dapm; + struct regmap *regmap = nau8824->regmap; + int adc_value, event = 0, event_mask = 0; + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(dapm, "SAR"); + snd_soc_dapm_sync(dapm); + + msleep(100); + + regmap_read(regmap, NAU8824_REG_SAR_ADC_DATA_OUT, &adc_value); + adc_value = adc_value & NAU8824_SAR_ADC_DATA_MASK; + dev_dbg(nau8824->dev, "SAR ADC data 0x%02x\n", adc_value); + if (adc_value < HEADSET_SARADC_THD) { + event |= SND_JACK_HEADPHONE; + + snd_soc_dapm_disable_pin(dapm, "SAR"); + snd_soc_dapm_disable_pin(dapm, "MICBIAS"); + snd_soc_dapm_sync(dapm); + } else { + event |= SND_JACK_HEADSET; + } + event_mask |= SND_JACK_HEADSET; + snd_soc_jack_report(nau8824->jack, event, event_mask); + + nau8824_sema_release(nau8824); +} + +static void nau8824_setup_auto_irq(struct nau8824 *nau8824) +{ + struct regmap *regmap = nau8824->regmap; + + /* Enable jack ejection, short key press and release interruption. */ + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN, + NAU8824_IRQ_EJECT_EN); + regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_KEY_RELEASE_DIS | + NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0); + /* Enable internal VCO needed for interruptions */ + nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0); + regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, 0); +} + +static int nau8824_button_decode(int value) +{ + int buttons = 0; + + /* The chip supports up to 8 buttons, but ALSA defines + * only 6 buttons. + */ + if (value & BIT(0)) + buttons |= SND_JACK_BTN_0; + if (value & BIT(1)) + buttons |= SND_JACK_BTN_1; + if (value & BIT(2)) + buttons |= SND_JACK_BTN_2; + if (value & BIT(3)) + buttons |= SND_JACK_BTN_3; + if (value & BIT(4)) + buttons |= SND_JACK_BTN_4; + if (value & BIT(5)) + buttons |= SND_JACK_BTN_5; + + return buttons; +} + +#define NAU8824_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3) + +static irqreturn_t nau8824_interrupt(int irq, void *data) +{ + struct nau8824 *nau8824 = (struct nau8824 *)data; + struct regmap *regmap = nau8824->regmap; + int active_irq, clear_irq = 0, event = 0, event_mask = 0; + + if (regmap_read(regmap, NAU8824_REG_IRQ, &active_irq)) { + dev_err(nau8824->dev, "failed to read irq status\n"); + return IRQ_NONE; + } + dev_dbg(nau8824->dev, "IRQ %x\n", active_irq); + + if (active_irq & NAU8824_JACK_EJECTION_DETECTED) { + nau8824_eject_jack(nau8824); + event_mask |= SND_JACK_HEADSET; + clear_irq = NAU8824_JACK_EJECTION_DETECTED; + /* release semaphore held after resume, + * and cancel jack detection + */ + nau8824_sema_release(nau8824); + cancel_work_sync(&nau8824->jdet_work); + } else if (active_irq & NAU8824_KEY_SHORT_PRESS_IRQ) { + int key_status, button_pressed; + + regmap_read(regmap, NAU8824_REG_CLEAR_INT_REG, + &key_status); + + /* lower 8 bits of the register are for pressed keys */ + button_pressed = nau8824_button_decode(key_status); + + event |= button_pressed; + dev_dbg(nau8824->dev, "button %x pressed\n", event); + event_mask |= NAU8824_BUTTONS; + clear_irq = NAU8824_KEY_SHORT_PRESS_IRQ; + } else if (active_irq & NAU8824_KEY_RELEASE_IRQ) { + event_mask = NAU8824_BUTTONS; + clear_irq = NAU8824_KEY_RELEASE_IRQ; + } else if (active_irq & NAU8824_JACK_INSERTION_DETECTED) { + /* Turn off insertion interruption at manual mode */ + regmap_update_bits(regmap, + NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_INSERT_DIS, + NAU8824_IRQ_INSERT_DIS); + regmap_update_bits(regmap, + NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_INSERT_EN, 0); + /* detect microphone and jack type */ + cancel_work_sync(&nau8824->jdet_work); + schedule_work(&nau8824->jdet_work); + + /* Enable interruption for jack type detection at audo + * mode which can detect microphone and jack type. + */ + nau8824_setup_auto_irq(nau8824); + } + + if (!clear_irq) + clear_irq = active_irq; + /* clears the rightmost interruption */ + regmap_write(regmap, NAU8824_REG_CLEAR_INT_REG, clear_irq); + + if (event_mask) + snd_soc_jack_report(nau8824->jack, event, event_mask); + + return IRQ_HANDLED; +} + +static int nau8824_clock_check(struct nau8824 *nau8824, + int stream, int rate, int osr) +{ + int osrate; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (osr >= ARRAY_SIZE(osr_dac_sel)) + return -EINVAL; + osrate = osr_dac_sel[osr].osr; + } else { + if (osr >= ARRAY_SIZE(osr_adc_sel)) + return -EINVAL; + osrate = osr_adc_sel[osr].osr; + } + + if (!osrate || rate * osr > CLK_DA_AD_MAX) { + dev_err(nau8824->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n"); + return -EINVAL; + } + + return 0; +} + +static int nau8824_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 nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div; + + nau8824_sema_acquire(nau8824, HZ); + + /* CLK_DAC or CLK_ADC = OSR * FS + * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR) + * multiplied by the audio sample rate (Fs). Note that the OSR and Fs + * values must be selected such that the maximum frequency is less + * than 6.144 MHz. + */ + nau8824->fs = params_rate(params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_read(nau8824->regmap, + NAU8824_REG_DAC_FILTER_CTRL_1, &osr); + osr &= NAU8824_DAC_OVERSAMPLE_MASK; + if (nau8824_clock_check(nau8824, substream->stream, + nau8824->fs, osr)) + return -EINVAL; + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_DAC_SRC_MASK, + osr_dac_sel[osr].clk_src << NAU8824_CLK_DAC_SRC_SFT); + } else { + regmap_read(nau8824->regmap, + NAU8824_REG_ADC_FILTER_CTRL, &osr); + osr &= NAU8824_ADC_SYNC_DOWN_MASK; + if (nau8824_clock_check(nau8824, substream->stream, + nau8824->fs, osr)) + return -EINVAL; + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_ADC_SRC_MASK, + osr_adc_sel[osr].clk_src << NAU8824_CLK_ADC_SRC_SFT); + } + + /* make BCLK and LRC divde configuration if the codec as master. */ + regmap_read(nau8824->regmap, + NAU8824_REG_PORT0_I2S_PCM_CTRL_2, &ctrl_val); + if (ctrl_val & NAU8824_I2S_MS_MASTER) { + /* get the bclk and fs ratio */ + bclk_fs = snd_soc_params_to_bclk(params) / nau8824->fs; + if (bclk_fs <= 32) + bclk_div = 0x3; + else if (bclk_fs <= 64) + bclk_div = 0x2; + else if (bclk_fs <= 128) + bclk_div = 0x1; + else if (bclk_fs <= 256) + bclk_div = 0; + else + return -EINVAL; + regmap_update_bits(nau8824->regmap, + NAU8824_REG_PORT0_I2S_PCM_CTRL_2, + NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK, + (bclk_div << NAU8824_I2S_LRC_DIV_SFT) | bclk_div); + } + + switch (params_width(params)) { + case 16: + val_len |= NAU8824_I2S_DL_16; + break; + case 20: + val_len |= NAU8824_I2S_DL_20; + break; + case 24: + val_len |= NAU8824_I2S_DL_24; + break; + case 32: + val_len |= NAU8824_I2S_DL_32; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1, + NAU8824_I2S_DL_MASK, val_len); + + nau8824_sema_release(nau8824); + + return 0; +} + +static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + unsigned int ctrl1_val = 0, ctrl2_val = 0; + + nau8824_sema_acquire(nau8824, HZ); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= NAU8824_I2S_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= NAU8824_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= NAU8824_I2S_DF_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= NAU8824_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1_val |= NAU8824_I2S_DF_RIGTH; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= NAU8824_I2S_DF_PCM_AB; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl1_val |= NAU8824_I2S_DF_PCM_AB; + ctrl1_val |= NAU8824_I2S_PCMB_EN; + break; + default: + return -EINVAL; + } + + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1, + NAU8824_I2S_DF_MASK | NAU8824_I2S_BP_MASK | + NAU8824_I2S_PCMB_EN, ctrl1_val); + regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_2, + NAU8824_I2S_MS_MASK, ctrl2_val); + + nau8824_sema_release(nau8824); + + return 0; +} + +/** + * nau8824_calc_fll_param - Calculate FLL parameters. + * @fll_in: external clock provided to codec. + * @fs: sampling rate. + * @fll_param: Pointer to structure of FLL parameters. + * + * Calculate FLL parameters to configure codec. + * + * Returns 0 for success or negative error code. + */ +static int nau8824_calc_fll_param(unsigned int fll_in, + unsigned int fs, struct nau8824_fll *fll_param) +{ + u64 fvco, fvco_max; + unsigned int fref, i, fvco_sel; + + /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing + * freq_in by 1, 2, 4, or 8 using FLL pre-scalar. + * FREF = freq_in / NAU8824_FLL_REF_DIV_MASK + */ + for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) { + fref = fll_in / fll_pre_scalar[i].param; + if (fref <= NAU_FREF_MAX) + break; + } + if (i == ARRAY_SIZE(fll_pre_scalar)) + return -EINVAL; + fll_param->clk_ref_div = fll_pre_scalar[i].val; + + /* Choose the FLL ratio based on FREF */ + for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) { + if (fref >= fll_ratio[i].param) + break; + } + if (i == ARRAY_SIZE(fll_ratio)) + return -EINVAL; + fll_param->ratio = fll_ratio[i].val; + + /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs. + * FDCO must be within the 90MHz - 124MHz or the FFL cannot be + * guaranteed across the full range of operation. + * FDCO = freq_out * 2 * mclk_src_scaling + */ + fvco_max = 0; + fvco_sel = ARRAY_SIZE(mclk_src_scaling); + for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { + fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && + fvco_max < fvco) { + fvco_max = fvco; + fvco_sel = i; + } + } + if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel) + return -EINVAL; + fll_param->mclk_src = mclk_src_scaling[fvco_sel].val; + + /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional + * input based on FDCO, FREF and FLL ratio. + */ + fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> 16) & 0x3FF; + fll_param->fll_frac = fvco & 0xFFFF; + return 0; +} + +static void nau8824_fll_apply(struct regmap *regmap, + struct nau8824_fll *fll_param) +{ + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK | NAU8824_CLK_MCLK_SRC_MASK, + NAU8824_CLK_SRC_MCLK | fll_param->mclk_src); + regmap_update_bits(regmap, NAU8824_REG_FLL1, + NAU8824_FLL_RATIO_MASK, fll_param->ratio); + /* FLL 16-bit fractional input */ + regmap_write(regmap, NAU8824_REG_FLL2, fll_param->fll_frac); + /* FLL 10-bit integer input */ + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_INTEGER_MASK, fll_param->fll_int); + /* FLL pre-scaler */ + regmap_update_bits(regmap, NAU8824_REG_FLL4, + NAU8824_FLL_REF_DIV_MASK, + fll_param->clk_ref_div << NAU8824_FLL_REF_DIV_SFT); + /* select divided VCO input */ + regmap_update_bits(regmap, NAU8824_REG_FLL5, + NAU8824_FLL_CLK_SW_MASK, NAU8824_FLL_CLK_SW_REF); + /* Disable free-running mode */ + regmap_update_bits(regmap, + NAU8824_REG_FLL6, NAU8824_DCO_EN, 0); + if (fll_param->fll_frac) { + regmap_update_bits(regmap, NAU8824_REG_FLL5, + NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN | + NAU8824_FLL_FTR_SW_MASK, + NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN | + NAU8824_FLL_FTR_SW_FILTER); + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_SDM_EN, NAU8824_SDM_EN); + } else { + regmap_update_bits(regmap, NAU8824_REG_FLL5, + NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN | + NAU8824_FLL_FTR_SW_MASK, NAU8824_FLL_FTR_SW_ACCU); + regmap_update_bits(regmap, + NAU8824_REG_FLL6, NAU8824_SDM_EN, 0); + } +} + +/* freq_out must be 256*Fs in order to achieve the best performance */ +static int nau8824_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + struct nau8824_fll fll_param; + int ret, fs; + + fs = freq_out / 256; + ret = nau8824_calc_fll_param(freq_in, fs, &fll_param); + if (ret < 0) { + dev_err(nau8824->dev, "Unsupported input clock %d\n", freq_in); + return ret; + } + dev_dbg(nau8824->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n", + fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac, + fll_param.fll_int, fll_param.clk_ref_div); + + nau8824_fll_apply(nau8824->regmap, &fll_param); + mdelay(2); + regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO); + + return 0; +} + +static int nau8824_config_sysclk(struct nau8824 *nau8824, + int clk_id, unsigned int freq) +{ + struct regmap *regmap = nau8824->regmap; + + switch (clk_id) { + case NAU8824_CLK_DIS: + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_DCO_EN, 0); + break; + + case NAU8824_CLK_MCLK: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK); + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_DCO_EN, 0); + nau8824_sema_release(nau8824); + break; + + case NAU8824_CLK_INTERNAL: + regmap_update_bits(regmap, NAU8824_REG_FLL6, + NAU8824_DCO_EN, NAU8824_DCO_EN); + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO); + break; + + case NAU8824_CLK_FLL_MCLK: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_MCLK); + nau8824_sema_release(nau8824); + break; + + case NAU8824_CLK_FLL_BLK: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_BLK); + nau8824_sema_release(nau8824); + break; + + case NAU8824_CLK_FLL_FS: + nau8824_sema_acquire(nau8824, HZ); + regmap_update_bits(regmap, NAU8824_REG_FLL3, + NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_FS); + nau8824_sema_release(nau8824); + break; + + default: + dev_err(nau8824->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + + dev_dbg(nau8824->dev, "Sysclk is %dHz and clock id is %d\n", freq, + clk_id); + + return 0; +} + +static int nau8824_set_sysclk(struct snd_soc_codec *codec, + int clk_id, int source, unsigned int freq, int dir) +{ + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + + return nau8824_config_sysclk(nau8824, clk_id, freq); +} + +static void nau8824_resume_setup(struct nau8824 *nau8824) +{ + nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0); + if (nau8824->irq) { + /* Clear all interruption status */ + nau8824_int_status_clear_all(nau8824->regmap); + /* Enable jack detection at sleep mode, insertion detection, + * and ejection detection. + */ + regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING, + NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, 0); + } +} + +static int nau8824_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + /* Setup codec configuration after resume */ + nau8824_resume_setup(nau8824); + } + break; + + case SND_SOC_BIAS_OFF: + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0); + break; + } + + return 0; +} + +static int nau8824_codec_probe(struct snd_soc_codec *codec) +{ + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + nau8824->dapm = dapm; + + return 0; +} + +static int __maybe_unused nau8824_suspend(struct snd_soc_codec *codec) +{ + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + + if (nau8824->irq) { + disable_irq(nau8824->irq); + snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + } + regcache_cache_only(nau8824->regmap, true); + regcache_mark_dirty(nau8824->regmap); + + return 0; +} + +static int __maybe_unused nau8824_resume(struct snd_soc_codec *codec) +{ + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + + regcache_cache_only(nau8824->regmap, false); + regcache_sync(nau8824->regmap); + if (nau8824->irq) { + /* Hold semaphore to postpone playback happening + * until jack detection done. + */ + nau8824_sema_acquire(nau8824, 0); + enable_irq(nau8824->irq); + } + + return 0; +} + +static struct snd_soc_codec_driver nau8824_codec_driver = { + .probe = nau8824_codec_probe, + .set_sysclk = nau8824_set_sysclk, + .set_pll = nau8824_set_pll, + .set_bias_level = nau8824_set_bias_level, + .suspend = nau8824_suspend, + .resume = nau8824_resume, + .suspend_bias_off = true, + + .component_driver = { + .controls = nau8824_snd_controls, + .num_controls = ARRAY_SIZE(nau8824_snd_controls), + .dapm_widgets = nau8824_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8824_dapm_widgets), + .dapm_routes = nau8824_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8824_dapm_routes), + }, +}; + +static const struct snd_soc_dai_ops nau8824_dai_ops = { + .hw_params = nau8824_hw_params, + .set_fmt = nau8824_set_fmt, +}; + +#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000 +#define NAU8824_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver nau8824_dai = { + .name = NAU8824_CODEC_DAI, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8824_RATES, + .formats = NAU8824_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8824_RATES, + .formats = NAU8824_FORMATS, + }, + .ops = &nau8824_dai_ops, +}; + +static const struct regmap_config nau8824_regmap_config = { + .val_bits = NAU8824_REG_ADDR_LEN, + .reg_bits = NAU8824_REG_DATA_LEN, + + .max_register = NAU8824_REG_MAX, + .readable_reg = nau8824_readable_reg, + .writeable_reg = nau8824_writeable_reg, + .volatile_reg = nau8824_volatile_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8824_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8824_reg_defaults), +}; + +/** + * nau8824_enable_jack_detect - Specify a jack for event reporting + * + * @component: component to register the jack with + * @jack: jack to use to report headset and button events on + * + * After this function has been called the headset insert/remove and button + * events will be routed to the given jack. Jack can be null to stop + * reporting. + */ +int nau8824_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec); + int ret; + + nau8824->jack = jack; + /* Initiate jack detection work queue */ + INIT_WORK(&nau8824->jdet_work, nau8824_jdet_work); + ret = devm_request_threaded_irq(nau8824->dev, nau8824->irq, NULL, + nau8824_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "nau8824", nau8824); + if (ret) { + dev_err(nau8824->dev, "Cannot request irq %d (%d)\n", + nau8824->irq, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(nau8824_enable_jack_detect); + +static void nau8824_reset_chip(struct regmap *regmap) +{ + regmap_write(regmap, NAU8824_REG_RESET, 0x00); + regmap_write(regmap, NAU8824_REG_RESET, 0x00); +} + +static void nau8824_setup_buttons(struct nau8824 *nau8824) +{ + struct regmap *regmap = nau8824->regmap; + + regmap_update_bits(regmap, NAU8824_REG_SAR_ADC, + NAU8824_SAR_TRACKING_GAIN_MASK, + nau8824->sar_voltage << NAU8824_SAR_TRACKING_GAIN_SFT); + regmap_update_bits(regmap, NAU8824_REG_SAR_ADC, + NAU8824_SAR_COMPARE_TIME_MASK, + nau8824->sar_compare_time << NAU8824_SAR_COMPARE_TIME_SFT); + regmap_update_bits(regmap, NAU8824_REG_SAR_ADC, + NAU8824_SAR_SAMPLING_TIME_MASK, + nau8824->sar_sampling_time << NAU8824_SAR_SAMPLING_TIME_SFT); + + regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT, + NAU8824_LEVELS_NR_MASK, + (nau8824->sar_threshold_num - 1) << NAU8824_LEVELS_NR_SFT); + regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT, + NAU8824_HYSTERESIS_MASK, + nau8824->sar_hysteresis << NAU8824_HYSTERESIS_SFT); + regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT, + NAU8824_SHORTKEY_DEBOUNCE_MASK, + nau8824->key_debounce << NAU8824_SHORTKEY_DEBOUNCE_SFT); + + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_1, + (nau8824->sar_threshold[0] << 8) | nau8824->sar_threshold[1]); + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_2, + (nau8824->sar_threshold[2] << 8) | nau8824->sar_threshold[3]); + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_3, + (nau8824->sar_threshold[4] << 8) | nau8824->sar_threshold[5]); + regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_4, + (nau8824->sar_threshold[6] << 8) | nau8824->sar_threshold[7]); +} + +static void nau8824_init_regs(struct nau8824 *nau8824) +{ + struct regmap *regmap = nau8824->regmap; + + /* Enable Bias/VMID/VMID Tieoff */ + regmap_update_bits(regmap, NAU8824_REG_BIAS_ADJ, + NAU8824_VMID | NAU8824_VMID_SEL_MASK, NAU8824_VMID | + (nau8824->vref_impedance << NAU8824_VMID_SEL_SFT)); + regmap_update_bits(regmap, NAU8824_REG_BOOST, + NAU8824_GLOBAL_BIAS_EN, NAU8824_GLOBAL_BIAS_EN); + mdelay(2); + regmap_update_bits(regmap, NAU8824_REG_MIC_BIAS, + NAU8824_MICBIAS_VOLTAGE_MASK, nau8824->micbias_voltage); + /* Disable Boost Driver, Automatic Short circuit protection enable */ + regmap_update_bits(regmap, NAU8824_REG_BOOST, + NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS | + NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN, + NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS | + NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN); + /* Scaling for ADC and DAC clock */ + regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER, + NAU8824_CLK_ADC_SRC_MASK | NAU8824_CLK_DAC_SRC_MASK, + (0x1 << NAU8824_CLK_ADC_SRC_SFT) | + (0x1 << NAU8824_CLK_DAC_SRC_SFT)); + regmap_update_bits(regmap, NAU8824_REG_DAC_MUTE_CTRL, + NAU8824_DAC_ZC_EN, NAU8824_DAC_ZC_EN); + regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL, + NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN | + NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN | + NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN, + NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN | + NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN | + NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN); + regmap_update_bits(regmap, NAU8824_REG_CLK_GATING_ENA, + NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN | + NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN | + NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN | + NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN, + NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN | + NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN | + NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN | + NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN); + /* Class G timer 64ms */ + regmap_update_bits(regmap, NAU8824_REG_CLASSG, + NAU8824_CLASSG_TIMER_MASK, + 0x20 << NAU8824_CLASSG_TIMER_SFT); + regmap_update_bits(regmap, NAU8824_REG_TRIM_SETTINGS, + NAU8824_DRV_CURR_INC, NAU8824_DRV_CURR_INC); + /* Disable DACR/L power */ + regmap_update_bits(regmap, NAU8824_REG_CHARGE_PUMP_CONTROL, + NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN | + NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL, + NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN | + NAU8824_POWER_DOWN_DACR | NAU8824_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(regmap, NAU8824_REG_ENABLE_LO, + NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN); + /* Config L/R channel */ + regmap_update_bits(regmap, NAU8824_REG_DAC_CH0_DGAIN_CTRL, + NAU8824_DAC_CH0_SEL_MASK, NAU8824_DAC_CH0_SEL_I2S0); + regmap_update_bits(regmap, NAU8824_REG_DAC_CH1_DGAIN_CTRL, + NAU8824_DAC_CH1_SEL_MASK, NAU8824_DAC_CH1_SEL_I2S1); + regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO, + NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN, + NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN); + /* Default oversampling/decimations settings are unusable + * (audible hiss). Set it to something better. + */ + regmap_update_bits(regmap, NAU8824_REG_ADC_FILTER_CTRL, + NAU8824_ADC_SYNC_DOWN_MASK, NAU8824_ADC_SYNC_DOWN_64); + regmap_update_bits(regmap, NAU8824_REG_DAC_FILTER_CTRL_1, + NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_MASK, + NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_64); + /* Class D gain 9db for 1R and 2L */ + regmap_update_bits(regmap, NAU8824_REG_CLASSD_GAIN_1, + NAU8824_CLASSD_GAIN_1R_MASK, + (0xa << NAU8824_CLASSD_GAIN_1R_SFT)); + regmap_update_bits(regmap, NAU8824_REG_CLASSD_GAIN_2, + NAU8824_CLASSD_GAIN_2L_MASK, 0xa); + /* DAC clock delay 2ns, VREF */ + regmap_update_bits(regmap, NAU8824_REG_RDAC, + NAU8824_RDAC_CLK_DELAY_MASK | NAU8824_RDAC_VREF_MASK, + (0x2 << NAU8824_RDAC_CLK_DELAY_SFT) | + (0x3 << NAU8824_RDAC_VREF_SFT)); + /* PGA input mode selection */ + regmap_update_bits(regmap, NAU8824_REG_FEPGA, + NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN, + NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN); + /* Digital microphone control */ + regmap_update_bits(regmap, NAU8824_REG_ANALOG_CONTROL_1, + NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST, + NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST); + regmap_update_bits(regmap, NAU8824_REG_JACK_DET_CTRL, + NAU8824_JACK_LOGIC, + /* jkdet_polarity - 1 is for active-low */ + nau8824->jkdet_polarity ? 0 : NAU8824_JACK_LOGIC); + regmap_update_bits(regmap, + NAU8824_REG_JACK_DET_CTRL, NAU8824_JACK_EJECT_DT_MASK, + (nau8824->jack_eject_debounce << NAU8824_JACK_EJECT_DT_SFT)); + if (nau8824->sar_threshold_num) + nau8824_setup_buttons(nau8824); +} + +static int nau8824_setup_irq(struct nau8824 *nau8824) +{ + /* Disable interruption before codec initiation done */ + regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL, + NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE); + regmap_update_bits(nau8824->regmap, + NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff); + regmap_update_bits(nau8824->regmap, NAU8824_REG_INTERRUPT_SETTING_1, + NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0); + + return 0; +} + +static void nau8824_print_device_properties(struct nau8824 *nau8824) +{ + struct device *dev = nau8824->dev; + int i; + + dev_dbg(dev, "jkdet-polarity: %d\n", nau8824->jkdet_polarity); + dev_dbg(dev, "micbias-voltage: %d\n", nau8824->micbias_voltage); + dev_dbg(dev, "vref-impedance: %d\n", nau8824->vref_impedance); + + dev_dbg(dev, "sar-threshold-num: %d\n", nau8824->sar_threshold_num); + for (i = 0; i < nau8824->sar_threshold_num; i++) + dev_dbg(dev, "sar-threshold[%d]=%x\n", i, + nau8824->sar_threshold[i]); + + dev_dbg(dev, "sar-hysteresis: %d\n", nau8824->sar_hysteresis); + dev_dbg(dev, "sar-voltage: %d\n", nau8824->sar_voltage); + dev_dbg(dev, "sar-compare-time: %d\n", nau8824->sar_compare_time); + dev_dbg(dev, "sar-sampling-time: %d\n", nau8824->sar_sampling_time); + dev_dbg(dev, "short-key-debounce: %d\n", nau8824->key_debounce); + dev_dbg(dev, "jack-eject-debounce: %d\n", + nau8824->jack_eject_debounce); +} + +static int nau8824_read_device_properties(struct device *dev, + struct nau8824 *nau8824) { + int ret; + + ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity", + &nau8824->jkdet_polarity); + if (ret) + nau8824->jkdet_polarity = 1; + ret = device_property_read_u32(dev, "nuvoton,micbias-voltage", + &nau8824->micbias_voltage); + if (ret) + nau8824->micbias_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,vref-impedance", + &nau8824->vref_impedance); + if (ret) + nau8824->vref_impedance = 2; + ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num", + &nau8824->sar_threshold_num); + if (ret) + nau8824->sar_threshold_num = 4; + ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold", + nau8824->sar_threshold, nau8824->sar_threshold_num); + if (ret) { + nau8824->sar_threshold[0] = 0x0a; + nau8824->sar_threshold[1] = 0x14; + nau8824->sar_threshold[2] = 0x26; + nau8824->sar_threshold[3] = 0x73; + } + ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis", + &nau8824->sar_hysteresis); + if (ret) + nau8824->sar_hysteresis = 0; + ret = device_property_read_u32(dev, "nuvoton,sar-voltage", + &nau8824->sar_voltage); + if (ret) + nau8824->sar_voltage = 6; + ret = device_property_read_u32(dev, "nuvoton,sar-compare-time", + &nau8824->sar_compare_time); + if (ret) + nau8824->sar_compare_time = 1; + ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time", + &nau8824->sar_sampling_time); + if (ret) + nau8824->sar_sampling_time = 1; + ret = device_property_read_u32(dev, "nuvoton,short-key-debounce", + &nau8824->key_debounce); + if (ret) + nau8824->key_debounce = 0; + ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce", + &nau8824->jack_eject_debounce); + if (ret) + nau8824->jack_eject_debounce = 1; + + return 0; +} + +static int nau8824_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct nau8824 *nau8824 = dev_get_platdata(dev); + int ret, value; + + if (!nau8824) { + nau8824 = devm_kzalloc(dev, sizeof(*nau8824), GFP_KERNEL); + if (!nau8824) + return -ENOMEM; + ret = nau8824_read_device_properties(dev, nau8824); + if (ret) + return ret; + } + i2c_set_clientdata(i2c, nau8824); + + nau8824->regmap = devm_regmap_init_i2c(i2c, &nau8824_regmap_config); + if (IS_ERR(nau8824->regmap)) + return PTR_ERR(nau8824->regmap); + nau8824->dev = dev; + nau8824->irq = i2c->irq; + sema_init(&nau8824->jd_sem, 1); + + nau8824_print_device_properties(nau8824); + + ret = regmap_read(nau8824->regmap, NAU8824_REG_I2C_DEVICE_ID, &value); + if (ret < 0) { + dev_err(dev, "Failed to read device id from the NAU8824: %d\n", + ret); + return ret; + } + nau8824_reset_chip(nau8824->regmap); + nau8824_init_regs(nau8824); + + if (i2c->irq) + nau8824_setup_irq(nau8824); + + return snd_soc_register_codec(dev, + &nau8824_codec_driver, &nau8824_dai, 1); +} + + +static int nau8824_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id nau8824_i2c_ids[] = { + { "nau8824", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids); + +#ifdef CONFIG_OF +static const struct of_device_id nau8824_of_ids[] = { + { .compatible = "nuvoton,nau8824", }, + {} +}; +MODULE_DEVICE_TABLE(of, nau8824_of_ids); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id nau8824_acpi_match[] = { + { "10508824", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, nau8824_acpi_match); +#endif + +static struct i2c_driver nau8824_i2c_driver = { + .driver = { + .name = "nau8824", + .of_match_table = of_match_ptr(nau8824_of_ids), + .acpi_match_table = ACPI_PTR(nau8824_acpi_match), + }, + .probe = nau8824_i2c_probe, + .remove = nau8824_i2c_remove, + .id_table = nau8824_i2c_ids, +}; +module_i2c_driver(nau8824_i2c_driver); + + +MODULE_DESCRIPTION("ASoC NAU88L24 driver"); +MODULE_AUTHOR("John Hsu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h new file mode 100644 index 000000000000..87ac9a382aed --- /dev/null +++ b/sound/soc/codecs/nau8824.h @@ -0,0 +1,466 @@ +/* + * NAU88L24 ALSA SoC audio driver + * + * Copyright 2016 Nuvoton Technology Corp. + * Author: John Hsu + * + * 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 __NAU8824_H__ +#define __NAU8824_H__ + +#define NAU8824_REG_RESET 0x00 +#define NAU8824_REG_ENA_CTRL 0x01 +#define NAU8824_REG_CLK_GATING_ENA 0x02 +#define NAU8824_REG_CLK_DIVIDER 0x03 +#define NAU8824_REG_FLL1 0x04 +#define NAU8824_REG_FLL2 0x05 +#define NAU8824_REG_FLL3 0x06 +#define NAU8824_REG_FLL4 0x07 +#define NAU8824_REG_FLL5 0x08 +#define NAU8824_REG_FLL6 0x09 +#define NAU8824_REG_FLL_VCO_RSV 0x0A +#define NAU8824_REG_JACK_DET_CTRL 0x0D +#define NAU8824_REG_INTERRUPT_SETTING_1 0x0F +#define NAU8824_REG_IRQ 0x10 +#define NAU8824_REG_CLEAR_INT_REG 0x11 +#define NAU8824_REG_INTERRUPT_SETTING 0x12 +#define NAU8824_REG_SAR_ADC 0x13 +#define NAU8824_REG_VDET_COEFFICIENT 0x14 +#define NAU8824_REG_VDET_THRESHOLD_1 0x15 +#define NAU8824_REG_VDET_THRESHOLD_2 0x16 +#define NAU8824_REG_VDET_THRESHOLD_3 0x17 +#define NAU8824_REG_VDET_THRESHOLD_4 0x18 +#define NAU8824_REG_GPIO_SEL 0x1A +#define NAU8824_REG_PORT0_I2S_PCM_CTRL_1 0x1C +#define NAU8824_REG_PORT0_I2S_PCM_CTRL_2 0x1D +#define NAU8824_REG_PORT0_LEFT_TIME_SLOT 0x1E +#define NAU8824_REG_PORT0_RIGHT_TIME_SLOT 0x1F +#define NAU8824_REG_TDM_CTRL 0x20 +#define NAU8824_REG_ADC_HPF_FILTER 0x23 +#define NAU8824_REG_ADC_FILTER_CTRL 0x24 +#define NAU8824_REG_DAC_FILTER_CTRL_1 0x25 +#define NAU8824_REG_DAC_FILTER_CTRL_2 0x26 +#define NAU8824_REG_NOTCH_FILTER_1 0x27 +#define NAU8824_REG_NOTCH_FILTER_2 0x28 +#define NAU8824_REG_EQ1_LOW 0x29 +#define NAU8824_REG_EQ2_EQ3 0x2A +#define NAU8824_REG_EQ4_EQ5 0x2B +#define NAU8824_REG_ADC_CH0_DGAIN_CTRL 0x2D +#define NAU8824_REG_ADC_CH1_DGAIN_CTRL 0x2E +#define NAU8824_REG_ADC_CH2_DGAIN_CTRL 0x2F +#define NAU8824_REG_ADC_CH3_DGAIN_CTRL 0x30 +#define NAU8824_REG_DAC_MUTE_CTRL 0x31 +#define NAU8824_REG_DAC_CH0_DGAIN_CTRL 0x32 +#define NAU8824_REG_DAC_CH1_DGAIN_CTRL 0x33 +#define NAU8824_REG_ADC_TO_DAC_ST 0x34 +#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 0x38 +#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH01 0x39 +#define NAU8824_REG_DRC_SLOPE_ADC_CH01 0x3A +#define NAU8824_REG_DRC_ATKDCY_ADC_CH01 0x3B +#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH23 0x3C +#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH23 0x3D +#define NAU8824_REG_DRC_SLOPE_ADC_CH23 0x3E +#define NAU8824_REG_DRC_ATKDCY_ADC_CH23 0x3F +#define NAU8824_REG_DRC_GAINL_ADC0 0x40 +#define NAU8824_REG_DRC_GAINL_ADC1 0x41 +#define NAU8824_REG_DRC_GAINL_ADC2 0x42 +#define NAU8824_REG_DRC_GAINL_ADC3 0x43 +#define NAU8824_REG_DRC_KNEE_IP12_DAC 0x45 +#define NAU8824_REG_DRC_KNEE_IP34_DAC 0x46 +#define NAU8824_REG_DRC_SLOPE_DAC 0x47 +#define NAU8824_REG_DRC_ATKDCY_DAC 0x48 +#define NAU8824_REG_DRC_GAIN_DAC_CH0 0x49 +#define NAU8824_REG_DRC_GAIN_DAC_CH1 0x4A +#define NAU8824_REG_MODE 0x4C +#define NAU8824_REG_MODE1 0x4D +#define NAU8824_REG_MODE2 0x4E +#define NAU8824_REG_CLASSG 0x50 +#define NAU8824_REG_OTP_EFUSE 0x51 +#define NAU8824_REG_OTPDOUT_1 0x53 +#define NAU8824_REG_OTPDOUT_2 0x54 +#define NAU8824_REG_MISC_CTRL 0x55 +#define NAU8824_REG_I2C_TIMEOUT 0x56 +#define NAU8824_REG_TEST_MODE 0x57 +#define NAU8824_REG_I2C_DEVICE_ID 0x58 +#define NAU8824_REG_SAR_ADC_DATA_OUT 0x59 +#define NAU8824_REG_BIAS_ADJ 0x66 +#define NAU8824_REG_PGA_GAIN 0x67 +#define NAU8824_REG_TRIM_SETTINGS 0x68 +#define NAU8824_REG_ANALOG_CONTROL_1 0x69 +#define NAU8824_REG_ANALOG_CONTROL_2 0x6A +#define NAU8824_REG_ENABLE_LO 0x6B +#define NAU8824_REG_GAIN_LO 0x6C +#define NAU8824_REG_CLASSD_GAIN_1 0x6D +#define NAU8824_REG_CLASSD_GAIN_2 0x6E +#define NAU8824_REG_ANALOG_ADC_1 0x71 +#define NAU8824_REG_ANALOG_ADC_2 0x72 +#define NAU8824_REG_RDAC 0x73 +#define NAU8824_REG_MIC_BIAS 0x74 +#define NAU8824_REG_HS_VOLUME_CONTROL 0x75 +#define NAU8824_REG_BOOST 0x76 +#define NAU8824_REG_FEPGA 0x77 +#define NAU8824_REG_FEPGA_II 0x78 +#define NAU8824_REG_FEPGA_SE 0x79 +#define NAU8824_REG_FEPGA_ATTENUATION 0x7A +#define NAU8824_REG_ATT_PORT0 0x7B +#define NAU8824_REG_ATT_PORT1 0x7C +#define NAU8824_REG_POWER_UP_CONTROL 0x7F +#define NAU8824_REG_CHARGE_PUMP_CONTROL 0x80 +#define NAU8824_REG_CHARGE_PUMP_INPUT 0x81 +#define NAU8824_REG_MAX NAU8824_REG_CHARGE_PUMP_INPUT +/* 16-bit control register address, and 16-bits control register data */ +#define NAU8824_REG_ADDR_LEN 16 +#define NAU8824_REG_DATA_LEN 16 + + +/* ENA_CTRL (0x1) */ +#define NAU8824_DMIC_LCH_EDGE_CH23 (0x1 << 12) +#define NAU8824_DMIC_LCH_EDGE_CH01 (0x1 << 11) +#define NAU8824_JD_SLEEP_MODE (0x1 << 10) +#define NAU8824_ADC_CH3_DMIC_SFT 9 +#define NAU8824_ADC_CH3_DMIC_EN (0x1 << NAU8824_ADC_CH3_DMIC_SFT) +#define NAU8824_ADC_CH2_DMIC_SFT 8 +#define NAU8824_ADC_CH2_DMIC_EN (0x1 << NAU8824_ADC_CH2_DMIC_SFT) +#define NAU8824_ADC_CH1_DMIC_SFT 7 +#define NAU8824_ADC_CH1_DMIC_EN (0x1 << NAU8824_ADC_CH1_DMIC_SFT) +#define NAU8824_ADC_CH0_DMIC_SFT 6 +#define NAU8824_ADC_CH0_DMIC_EN (0x1 << NAU8824_ADC_CH0_DMIC_SFT) +#define NAU8824_DAC_CH1_EN (0x1 << 5) +#define NAU8824_DAC_CH0_EN (0x1 << 4) +#define NAU8824_ADC_CH3_EN (0x1 << 3) +#define NAU8824_ADC_CH2_EN (0x1 << 2) +#define NAU8824_ADC_CH1_EN (0x1 << 1) +#define NAU8824_ADC_CH0_EN 0x1 + +/* CLK_GATING_ENA (0x02) */ +#define NAU8824_CLK_ADC_CH23_EN (0x1 << 15) +#define NAU8824_CLK_ADC_CH01_EN (0x1 << 14) +#define NAU8824_CLK_DAC_CH1_EN (0x1 << 13) +#define NAU8824_CLK_DAC_CH0_EN (0x1 << 12) +#define NAU8824_CLK_I2S_EN (0x1 << 7) +#define NAU8824_CLK_GAIN_EN (0x1 << 5) +#define NAU8824_CLK_SAR_EN (0x1 << 3) +#define NAU8824_CLK_DMIC_CH23_EN (0x1 << 1) + +/* CLK_DIVIDER (0x3) */ +#define NAU8824_CLK_SRC_SFT 15 +#define NAU8824_CLK_SRC_MASK (1 << NAU8824_CLK_SRC_SFT) +#define NAU8824_CLK_SRC_VCO (1 << NAU8824_CLK_SRC_SFT) +#define NAU8824_CLK_SRC_MCLK (0 << NAU8824_CLK_SRC_SFT) +#define NAU8824_CLK_MCLK_SRC_MASK (0xf << 0) +#define NAU8824_CLK_DMIC_SRC_SFT 10 +#define NAU8824_CLK_DMIC_SRC_MASK (0x7 << NAU8824_CLK_DMIC_SRC_SFT) +#define NAU8824_CLK_ADC_SRC_SFT 6 +#define NAU8824_CLK_ADC_SRC_MASK (0x3 << NAU8824_CLK_ADC_SRC_SFT) +#define NAU8824_CLK_DAC_SRC_SFT 4 +#define NAU8824_CLK_DAC_SRC_MASK (0x3 << NAU8824_CLK_DAC_SRC_SFT) + +/* FLL1 (0x04) */ +#define NAU8824_FLL_RATIO_MASK (0x7f << 0) + +/* FLL3 (0x06) */ +#define NAU8824_FLL_INTEGER_MASK (0x3ff << 0) +#define NAU8824_FLL_CLK_SRC_SFT 10 +#define NAU8824_FLL_CLK_SRC_MASK (0x3 << NAU8824_FLL_CLK_SRC_SFT) +#define NAU8824_FLL_CLK_SRC_MCLK (0 << NAU8824_FLL_CLK_SRC_SFT) +#define NAU8824_FLL_CLK_SRC_BLK (0x2 << NAU8824_FLL_CLK_SRC_SFT) +#define NAU8824_FLL_CLK_SRC_FS (0x3 << NAU8824_FLL_CLK_SRC_SFT) + +/* FLL4 (0x07) */ +#define NAU8824_FLL_REF_DIV_SFT 10 +#define NAU8824_FLL_REF_DIV_MASK (0x3 << NAU8824_FLL_REF_DIV_SFT) + +/* FLL5 (0x08) */ +#define NAU8824_FLL_PDB_DAC_EN (0x1 << 15) +#define NAU8824_FLL_LOOP_FTR_EN (0x1 << 14) +#define NAU8824_FLL_CLK_SW_MASK (0x1 << 13) +#define NAU8824_FLL_CLK_SW_N2 (0x1 << 13) +#define NAU8824_FLL_CLK_SW_REF (0x0 << 13) +#define NAU8824_FLL_FTR_SW_MASK (0x1 << 12) +#define NAU8824_FLL_FTR_SW_ACCU (0x1 << 12) +#define NAU8824_FLL_FTR_SW_FILTER (0x0 << 12) + +/* FLL6 (0x9) */ +#define NAU8824_DCO_EN (0x1 << 15) +#define NAU8824_SDM_EN (0x1 << 14) + +/* IRQ (0x10) */ +#define NAU8824_SHORT_CIRCUIT_IRQ (0x1 << 7) +#define NAU8824_IMPEDANCE_MEAS_IRQ (0x1 << 6) +#define NAU8824_KEY_RELEASE_IRQ (0x1 << 5) +#define NAU8824_KEY_LONG_PRESS_IRQ (0x1 << 4) +#define NAU8824_KEY_SHORT_PRESS_IRQ (0x1 << 3) +#define NAU8824_JACK_EJECTION_DETECTED (0x1 << 1) +#define NAU8824_JACK_INSERTION_DETECTED 0x1 + +/* JACK_DET_CTRL (0x0D) */ +#define NAU8824_JACK_EJECT_DT_SFT 2 +#define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT) +#define NAU8824_JACK_LOGIC 0x1 + + +/* INTERRUPT_SETTING_1 (0x0F) */ +#define NAU8824_IRQ_EJECT_EN (0x1 << 9) +#define NAU8824_IRQ_INSERT_EN (0x1 << 8) + +/* INTERRUPT_SETTING (0x12) */ +#define NAU8824_IRQ_KEY_RELEASE_DIS (0x1 << 5) +#define NAU8824_IRQ_KEY_SHORT_PRESS_DIS (0x1 << 3) +#define NAU8824_IRQ_EJECT_DIS (0x1 << 1) +#define NAU8824_IRQ_INSERT_DIS 0x1 + +/* SAR_ADC (0x13) */ +#define NAU8824_SAR_ADC_EN_SFT 12 +#define NAU8824_SAR_TRACKING_GAIN_SFT 8 +#define NAU8824_SAR_TRACKING_GAIN_MASK (0x7 << NAU8824_SAR_TRACKING_GAIN_SFT) +#define NAU8824_SAR_COMPARE_TIME_SFT 2 +#define NAU8824_SAR_COMPARE_TIME_MASK (3 << 2) +#define NAU8824_SAR_SAMPLING_TIME_SFT 0 +#define NAU8824_SAR_SAMPLING_TIME_MASK (3 << 0) + +/* VDET_COEFFICIENT (0x14) */ +#define NAU8824_SHORTKEY_DEBOUNCE_SFT 12 +#define NAU8824_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8824_SHORTKEY_DEBOUNCE_SFT) +#define NAU8824_LEVELS_NR_SFT 8 +#define NAU8824_LEVELS_NR_MASK (0x7 << 8) +#define NAU8824_HYSTERESIS_SFT 0 +#define NAU8824_HYSTERESIS_MASK 0xf + +/* PORT0_I2S_PCM_CTRL_1 (0x1C) */ +#define NAU8824_I2S_BP_SFT 7 +#define NAU8824_I2S_BP_MASK (1 << NAU8824_I2S_BP_SFT) +#define NAU8824_I2S_BP_INV (1 << NAU8824_I2S_BP_SFT) +#define NAU8824_I2S_PCMB_SFT 6 +#define NAU8824_I2S_PCMB_EN (1 << NAU8824_I2S_PCMB_SFT) +#define NAU8824_I2S_DL_SFT 2 +#define NAU8824_I2S_DL_MASK (0x3 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_16 (0 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_20 (1 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_24 (2 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DL_32 (3 << NAU8824_I2S_DL_SFT) +#define NAU8824_I2S_DF_MASK 0x3 +#define NAU8824_I2S_DF_RIGTH 0 +#define NAU8824_I2S_DF_LEFT 1 +#define NAU8824_I2S_DF_I2S 2 +#define NAU8824_I2S_DF_PCM_AB 3 + + +/* PORT0_I2S_PCM_CTRL_2 (0x1D) */ +#define NAU8824_I2S_LRC_DIV_SFT 12 +#define NAU8824_I2S_LRC_DIV_MASK (0x3 << NAU8824_I2S_LRC_DIV_SFT) +#define NAU8824_I2S_MS_SFT 3 +#define NAU8824_I2S_MS_MASK (1 << NAU8824_I2S_MS_SFT) +#define NAU8824_I2S_MS_MASTER (1 << NAU8824_I2S_MS_SFT) +#define NAU8824_I2S_MS_SLAVE (0 << NAU8824_I2S_MS_SFT) +#define NAU8824_I2S_BLK_DIV_MASK 0x7 + +/* ADC_FILTER_CTRL (0x24) */ +#define NAU8824_ADC_SYNC_DOWN_MASK 0x3 +#define NAU8824_ADC_SYNC_DOWN_32 0 +#define NAU8824_ADC_SYNC_DOWN_64 1 +#define NAU8824_ADC_SYNC_DOWN_128 2 +#define NAU8824_ADC_SYNC_DOWN_256 3 + +/* DAC_FILTER_CTRL_1 (0x25) */ +#define NAU8824_DAC_CICCLP_OFF (0x1 << 7) +#define NAU8824_DAC_OVERSAMPLE_MASK 0x7 +#define NAU8824_DAC_OVERSAMPLE_64 0 +#define NAU8824_DAC_OVERSAMPLE_256 1 +#define NAU8824_DAC_OVERSAMPLE_128 2 +#define NAU8824_DAC_OVERSAMPLE_32 4 + +/* DAC_MUTE_CTRL (0x31) */ +#define NAU8824_DAC_CH01_MIX 0x3 +#define NAU8824_DAC_ZC_EN (0x1 << 11) + +/* DAC_CH0_DGAIN_CTRL (0x32) */ +#define NAU8824_DAC_CH0_SEL_SFT 9 +#define NAU8824_DAC_CH0_SEL_MASK (0x1 << NAU8824_DAC_CH0_SEL_SFT) +#define NAU8824_DAC_CH0_SEL_I2S0 (0x0 << NAU8824_DAC_CH0_SEL_SFT) +#define NAU8824_DAC_CH0_SEL_I2S1 (0x1 << NAU8824_DAC_CH0_SEL_SFT) +#define NAU8824_DAC_CH0_VOL_MASK 0x1ff + +/* DAC_CH1_DGAIN_CTRL (0x33) */ +#define NAU8824_DAC_CH1_SEL_SFT 9 +#define NAU8824_DAC_CH1_SEL_MASK (0x1 << NAU8824_DAC_CH1_SEL_SFT) +#define NAU8824_DAC_CH1_SEL_I2S0 (0x0 << NAU8824_DAC_CH1_SEL_SFT) +#define NAU8824_DAC_CH1_SEL_I2S1 (0x1 << NAU8824_DAC_CH1_SEL_SFT) +#define NAU8824_DAC_CH1_VOL_MASK 0x1ff + +/* CLASSG (0x50) */ +#define NAU8824_CLASSG_TIMER_SFT 8 +#define NAU8824_CLASSG_TIMER_MASK (0x3f << NAU8824_CLASSG_TIMER_SFT) +#define NAU8824_CLASSG_LDAC_EN_SFT 2 +#define NAU8824_CLASSG_RDAC_EN_SFT 1 +#define NAU8824_CLASSG_EN_SFT 0 + +/* SAR_ADC_DATA_OUT (0x59) */ +#define NAU8824_SAR_ADC_DATA_MASK 0xff + +/* BIAS_ADJ (0x66) */ +#define NAU8824_VMID (1 << 6) +#define NAU8824_VMID_SEL_SFT 4 +#define NAU8824_VMID_SEL_MASK (3 << NAU8824_VMID_SEL_SFT) +#define NAU8824_DMIC2_EN_SFT 3 +#define NAU8824_DMIC1_EN_SFT 2 + +/* TRIM_SETTINGS (0x68) */ +#define NAU8824_DRV_CURR_INC (1 << 15) + +/* ANALOG_CONTROL_1 (0x69) */ +#define NAU8824_DMIC_CLK_DRV_STRG (1 << 3) +#define NAU8824_DMIC_CLK_SLEW_FAST (0x7) + +/* ANALOG_CONTROL_2 (0x6A) */ +#define NAU8824_CLASSD_CLAMP_DIS_SFT 3 +#define NAU8824_CLASSD_CLAMP_DIS (0x1 << NAU8824_CLASSD_CLAMP_DIS_SFT) + +/* ENABLE_LO (0x6B) */ +#define NAU8824_TEST_DAC_SFT 14 +#define NAU8824_TEST_DAC_EN (0x3 << NAU8824_TEST_DAC_SFT) +#define NAU8824_DACL_HPR_EN_SFT 3 +#define NAU8824_DACL_HPR_EN (0x1 << NAU8824_DACL_HPR_EN_SFT) +#define NAU8824_DACR_HPR_EN_SFT 2 +#define NAU8824_DACR_HPR_EN (0x1 << NAU8824_DACR_HPR_EN_SFT) +#define NAU8824_DACR_HPL_EN_SFT 1 +#define NAU8824_DACR_HPL_EN (0x1 << NAU8824_DACR_HPL_EN_SFT) +#define NAU8824_DACL_HPL_EN_SFT 0 +#define NAU8824_DACL_HPL_EN 0x1 + +/* CLASSD_GAIN_1 (0x6D) */ +#define NAU8824_CLASSD_GAIN_1R_SFT 8 +#define NAU8824_CLASSD_GAIN_1R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT) +#define NAU8824_CLASSD_EN_SFT 7 +#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT) +#define NAU8824_CLASSD_GAIN_1L_MASK 0x1f + +/* CLASSD_GAIN_2 (0x6E) */ +#define NAU8824_CLASSD_GAIN_2R_SFT 8 +#define NAU8824_CLASSD_GAIN_2R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT) +#define NAU8824_CLASSD_EN_SFT 7 +#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT) +#define NAU8824_CLASSD_GAIN_2L_MASK 0x1f + +/* ANALOG_ADC_2 (0x72) */ +#define NAU8824_ADCR_EN_SFT 7 +#define NAU8824_ADCL_EN_SFT 6 + +/* RDAC (0x73) */ +#define NAU8824_DACR_EN_SFT 13 +#define NAU8824_DACL_EN_SFT 12 +#define NAU8824_DACR_CLK_SFT 9 +#define NAU8824_DACL_CLK_SFT 8 +#define NAU8824_RDAC_CLK_DELAY_SFT 4 +#define NAU8824_RDAC_CLK_DELAY_MASK (0x7 << NAU8824_RDAC_CLK_DELAY_SFT) +#define NAU8824_RDAC_VREF_SFT 2 +#define NAU8824_RDAC_VREF_MASK (0x3 << NAU8824_RDAC_VREF_SFT) + +/* MIC_BIAS (0x74) */ +#define NAU8824_MICBIAS_JKSLV (1 << 14) +#define NAU8824_MICBIAS_JKR2 (1 << 12) +#define NAU8824_MICBIAS_POWERUP_SFT 8 +#define NAU8824_MICBIAS_VOLTAGE_SFT 0 +#define NAU8824_MICBIAS_VOLTAGE_MASK 0x7 + +/* BOOST (0x76) */ +#define NAU8824_PRECHARGE_DIS (0x1 << 13) +#define NAU8824_GLOBAL_BIAS_EN (0x1 << 12) +#define NAU8824_HP_BOOST_DIS_SFT 9 +#define NAU8824_HP_BOOST_DIS (0x1 << NAU8824_HP_BOOST_DIS_SFT) +#define NAU8824_HP_BOOST_G_DIS_SFT 8 +#define NAU8824_HP_BOOST_G_DIS (0x1 << NAU8824_HP_BOOST_G_DIS_SFT) +#define NAU8824_SHORT_SHUTDOWN_DIG_EN (1 << 7) +#define NAU8824_SHORT_SHUTDOWN_EN (1 << 6) + +/* FEPGA (0x77) */ +#define NAU8824_FEPGA_MODER_SHORT_SFT 7 +#define NAU8824_FEPGA_MODER_SHORT_EN (0x1 << NAU8824_FEPGA_MODER_SHORT_SFT) +#define NAU8824_FEPGA_MODER_MIC2_SFT 5 +#define NAU8824_FEPGA_MODER_MIC2_EN (0x1 << NAU8824_FEPGA_MODER_MIC2_SFT) +#define NAU8824_FEPGA_MODER_HSMIC_SFT 4 +#define NAU8824_FEPGA_MODER_HSMIC_EN (0x1 << NAU8824_FEPGA_MODER_HSMIC_SFT) +#define NAU8824_FEPGA_MODEL_SHORT_SFT 3 +#define NAU8824_FEPGA_MODEL_SHORT_EN (0x1 << NAU8824_FEPGA_MODEL_SHORT_SFT) +#define NAU8824_FEPGA_MODEL_MIC1_SFT 1 +#define NAU8824_FEPGA_MODEL_MIC1_EN (0x1 << NAU8824_FEPGA_MODEL_MIC1_SFT) +#define NAU8824_FEPGA_MODEL_HSMIC_SFT 0 +#define NAU8824_FEPGA_MODEL_HSMIC_EN (0x1 << NAU8824_FEPGA_MODEL_HSMIC_SFT) + +/* FEPGA_II (0x78) */ +#define NAU8824_FEPGA_GAINR_SFT 5 +#define NAU8824_FEPGA_GAINR_MASK (0x1f << NAU8824_FEPGA_GAINR_SFT) +#define NAU8824_FEPGA_GAINL_SFT 0 +#define NAU8824_FEPGA_GAINL_MASK 0x1f + +/* CHARGE_PUMP_CONTROL (0x80) */ +#define NAU8824_JAMNODCLOW (0x1 << 15) +#define NAU8824_SPKR_PULL_DOWN (0x1 << 13) +#define NAU8824_SPKL_PULL_DOWN (0x1 << 12) +#define NAU8824_POWER_DOWN_DACR (0x1 << 9) +#define NAU8824_POWER_DOWN_DACL (0x1 << 8) +#define NAU8824_CHARGE_PUMP_EN_SFT 5 +#define NAU8824_CHARGE_PUMP_EN (0x1 << NAU8824_CHARGE_PUMP_EN_SFT) + + +#define NAU8824_CODEC_DAI "nau8824-hifi" + +/* System Clock Source */ +enum { + NAU8824_CLK_DIS, + NAU8824_CLK_MCLK, + NAU8824_CLK_INTERNAL, + NAU8824_CLK_FLL_MCLK, + NAU8824_CLK_FLL_BLK, + NAU8824_CLK_FLL_FS, +}; + +struct nau8824 { + struct device *dev; + struct regmap *regmap; + struct snd_soc_dapm_context *dapm; + struct snd_soc_jack *jack; + struct work_struct jdet_work; + struct semaphore jd_sem; + int fs; + int irq; + int micbias_voltage; + int vref_impedance; + int jkdet_polarity; + int sar_threshold_num; + int sar_threshold[8]; + int sar_hysteresis; + int sar_voltage; + int sar_compare_time; + int sar_sampling_time; + int key_debounce; + int jack_eject_debounce; +}; + +struct nau8824_fll { + int mclk_src; + int ratio; + int fll_frac; + int fll_int; + int clk_ref_div; +}; + +struct nau8824_fll_attr { + unsigned int param; + unsigned int val; +}; + +struct nau8824_osr_attr { + unsigned int osr; + unsigned int clk_src; +}; + + +int nau8824_enable_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + +#endif /* _NAU8824_H */ + -- cgit v1.2.3 From b45c4f117b002a902e8b9f52eaf542e3e82c36a8 Mon Sep 17 00:00:00 2001 From: olivier moysan Date: Mon, 10 Apr 2017 17:19:55 +0200 Subject: ASoC: stm32: add bindings for SAI This patch adds documentation of device tree bindings for the STM32 SAI ASoC driver. Signed-off-by: olivier moysan Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/st,stm32-sai.txt | 89 ++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/st,stm32-sai.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt new file mode 100644 index 000000000000..c59a3d779e06 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt @@ -0,0 +1,89 @@ +STMicroelectronics STM32 Serial Audio Interface (SAI). + +The SAI interface (Serial Audio Interface) offers a wide set of audio protocols +as I2S standards, LSB or MSB-justified, PCM/DSP, TDM, and AC'97. +The SAI contains two independent audio sub-blocks. Each sub-block has +its own clock generator and I/O lines controller. + +Required properties: + - compatible: Should be "st,stm32f4-sai" + - reg: Base address and size of SAI common register set. + - clocks: Must contain phandle and clock specifier pairs for each entry + in clock-names. + - clock-names: Must contain "x8k" and "x11k" + "x8k": SAI parent clock for sampling rates multiple of 8kHz. + "x11k": SAI parent clock for sampling rates multiple of 11.025kHz. + - interrupts: cpu DAI interrupt line shared by SAI sub-blocks + +Optional properties: + - resets: Reference to a reset controller asserting the SAI + +SAI subnodes: +Two subnodes corresponding to SAI sub-block instances A et B can be defined. +Subnode can be omitted for unsused sub-block. + +SAI subnodes required properties: + - compatible: Should be "st,stm32-sai-sub-a" or "st,stm32-sai-sub-b" + for SAI sub-block A or B respectively. + - reg: Base address and size of SAI sub-block register set. + - clocks: Must contain one phandle and clock specifier pair + for sai_ck which feeds the internal clock generator. + - clock-names: Must contain "sai_ck". + - dmas: see Documentation/devicetree/bindings/dma/stm32-dma.txt + - dma-names: identifier string for each DMA request line + "tx": if sai sub-block is configured as playback DAI + "rx": if sai sub-block is configured as capture DAI + - pinctrl-names: should contain only value "default" + - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt + +Example: +sound_card { + compatible = "audio-graph-card"; + dais = <&sai1b_port>; +}; + +sai1: sai1@40015800 { + compatible = "st,stm32f4-sai"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + reg = <0x40015800 0x4>; + clocks = <&rcc 1 CLK_SAIQ_PDIV>, <&rcc 1 CLK_I2SQ_PDIV>; + clock-names = "x8k", "x11k"; + interrupts = <87>; + + sai1b: audio-controller@40015824 { + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x40015824 0x1C>; + clocks = <&rcc 1 CLK_SAI2>; + clock-names = "sai_ck"; + dmas = <&dma2 5 0 0x400 0x0>; + dma-names = "tx"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai1b>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + sai1b_port: port@0 { + reg = <0>; + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + audio-graph-card,format = "i2s"; + audio-graph-card,bitclock-master = <&codec_endpoint>; + audio-graph-card,frame-master = <&codec_endpoint>; + }; + }; + }; + }; +}; + +audio-codec { + codec_port: port { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; +}; -- cgit v1.2.3