Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v6 3/8] PWM: add pwm-stm32 DT bindings
From: Benjamin Gaignard @ 2016-12-13 16:28 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, devicetree@vger.kernel.org, Lars-Peter Clausen,
	Alexandre Torgue, Linux PWM List, linux-iio@vger.kernel.org,
	Linus Walleij, Arnaud POULIQUEN, linux-kernel@vger.kernel.org,
	Thierry Reding, linux-arm-kernel@lists.infradead.org,
	Peter Meerwald, Hartmut Knaack, Gerald Baeza, Fabrice Gasnier,
	Lee Jones, linaro-kernel@lists.linaro.org, Jonathan Cameron,
	Benjamin Gaignard
In-Reply-To: <CAL_JsqL8pJSFb8LbABAYJOQ0URaMpyupbFryk_mS2ToN1kStdA@mail.gmail.com>

2016-12-13 16:57 GMT+01:00 Rob Herring <robh@kernel.org>:
> On Tue, Dec 13, 2016 at 5:11 AM, Lee Jones <lee.jones@linaro.org> wrote:
>> On Mon, 12 Dec 2016, Rob Herring wrote:
>>
>>> On Fri, Dec 09, 2016 at 03:15:14PM +0100, Benjamin Gaignard wrote:
>>> > Define bindings for pwm-stm32
>>> >
>>> > version 6:
>>> > - change st,breakinput parameter format to make it usuable on stm32f7 too.
>>> >
>>> > version 2:
>>> > - use parameters instead of compatible of handle the hardware configuration
>>> >
>>> > Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
>>> > ---
>>> >  .../devicetree/bindings/pwm/pwm-stm32.txt          | 33 ++++++++++++++++++++++
>>> >  1 file changed, 33 insertions(+)
>>> >  create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>>> >
>>> > diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>>> > new file mode 100644
>>> > index 0000000..866f222
>>> > --- /dev/null
>>> > +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>>> > @@ -0,0 +1,33 @@
>>> > +STMicroelectronics STM32 Timers PWM bindings
>>> > +
>>> > +Must be a sub-node of an STM32 Timers device tree node.
>>> > +See ../mfd/stm32-timers.txt for details about the parent node.
>>> > +
>>> > +Required parameters:
>>> > +- compatible:              Must be "st,stm32-pwm".
>>> > +- pinctrl-names:   Set to "default".
>>> > +- pinctrl-0:               List of phandles pointing to pin configuration nodes for PWM module.
>>> > +                   For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
>>> > +
>>> > +Optional parameters:
>>> > +- st,breakinput:   Arrays of three u32 <index level filter> to describe break input configurations.
>>> > +                   "index" indicates on which break input the configuration should be applied.
>>> > +                   "level" gives the active level (0=low or 1=high) for this configuration.
>>> > +                   "filter" gives the filtering value to be applied.
>>> > +
>>> > +Example:
>>> > +   timers@40010000 {
>>>
>>> timer@...
>>
>> No, it should be timers.
>
> Read the spec. "timer" is a generic node name. "timers" is not. How
> many is not relevant.

"timer" is already used in stm32 DT for clocksource node... It is also why
I use "timers" for this.

>
>> The 's' is intentional, since this parent (MFD) device houses 3
>> different types of timers.  The "timer" node is a child of this one.



-- 
Benjamin Gaignard

Graphic Study Group

Linaro.org │ Open source software for ARM SoCs

Follow Linaro: Facebook | Twitter | Blog

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH v2 2/2] ASoC: cs35l35: Add device tree documentation for CS35L35
From: Li Xu @ 2016-12-13 16:26 UTC (permalink / raw)
  To: alsa-devel, devicetree
  Cc: mark.rutland, brian.austin, tiwai, robh+dt, lgirdwood, broonie,
	Paul.Handrigan, Li Xu
In-Reply-To: <1481646404-19012-1-git-send-email-li.xu@cirrus.com>

Add device tree documentation for Cirrus Logic CS35L35
speaker amplifier

Signed-off-by: Li Xu <li.xu@cirrus.com>
---
 .../devicetree/bindings/sound/cs35l35.txt          | 172 +++++++++++++++++++++
 1 file changed, 172 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/cs35l35.txt

diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt
new file mode 100644
index 0000000..8b13f67
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cs35l35.txt
@@ -0,0 +1,172 @@
+CS35L35 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:
+  - cirrus,reset-gpios : Active low 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 Converter control word. Step Size of 100mV
+  0x00 = (Default) VP
+  0x01 = 2600mV
+  0x41 = 9000mV
+
+  - cirrus,boost-ipk-milliamp : Boost-converter peak current limit.
+  Configures the peak current by monitoring the current through the boost FET.
+  Step size: 112mA
+  0x00 = 1680mA
+  0x19 = 4480mA
+
+  - 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: audio-codec@20 {
+	compatible = "cirrus,cs35l35";
+	reg = <0x20>;
+	VA-supply = <&dummy_vreg>;
+	VP-supply = <&dummy_vreg>;
+	reset-gpios = <&axi_gpio 54 1>;
+	interrupt-parent = <&gpio8>;
+	interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+	cirrus,boost-ctl = <0x41>;
+
+	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>;
+	};
+
+};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v2 1/2] ASoC: cs35l35: Add support for Cirrus CS35L35 Amplifier
From: Li Xu @ 2016-12-13 16:26 UTC (permalink / raw)
  To: alsa-devel, devicetree
  Cc: mark.rutland, brian.austin, tiwai, robh+dt, lgirdwood, broonie,
	Paul.Handrigan, Li Xu

Add driver support for Cirrus Logic CS35L35 boosted
speaker amplifier

Signed-off-by: Li Xu <li.xu@cirrus.com>
---
 include/sound/cs35l35.h    |  104 ++++
 sound/soc/codecs/Kconfig   |    5 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/cs35l35.c | 1357 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/cs35l35.h |  285 ++++++++++
 5 files changed, 1753 insertions(+)
 create mode 100644 include/sound/cs35l35.h
 create mode 100644 sound/soc/codecs/cs35l35.c
 create mode 100644 sound/soc/codecs/cs35l35.h

diff --git a/include/sound/cs35l35.h b/include/sound/cs35l35.h
new file mode 100644
index 0000000..005a813
--- /dev/null
+++ b/include/sound/cs35l35.h
@@ -0,0 +1,104 @@
+/*
+ * linux/sound/cs35l35.h -- Platform data for CS35l35
+ *
+ * Copyright (c) 2016 Cirrus Logic Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CS35L35_H
+#define __CS35L35_H
+
+struct classh_cfg {
+	/*
+	 * Class H Algorithm Control Variables
+	 * You can either have it done
+	 * automatically or you can adjust
+	 * these variables for tuning
+	 *
+	 * if you do not enable the internal algorithm
+	 * you will get a set of mixer controls for
+	 * Class H tuning
+	 *
+	 * Section 4.3 of the datasheet
+	 */
+	/* Internal ClassH Algorithm  */
+	bool classh_bst_override;
+	bool classh_algo_enable;
+	int classh_bst_max_limit;
+	int classh_mem_depth;
+	int classh_release_rate;
+	int classh_headroom;
+	int classh_wk_fet_disable;
+	int classh_wk_fet_delay;
+	int classh_wk_fet_thld;
+	int classh_vpch_auto;
+	int classh_vpch_rate;
+	int classh_vpch_man;
+};
+
+struct monitor_cfg {
+	/*
+	 * Signal Monitor Data
+	 * highly configurable signal monitoring
+	 * data positioning and different types of
+	 * monitoring data.
+	 *
+	 * Section 4.8.2 - 4.8.4 of the datasheet
+	 */
+	bool is_present;
+	bool imon_specs;
+	bool vmon_specs;
+	bool vpmon_specs;
+	bool vbstmon_specs;
+	bool vpbrstat_specs;
+	bool zerofill_specs;
+	u8 imon_dpth;
+	u8 imon_loc;
+	u8 imon_frm;
+	u8 vmon_dpth;
+	u8 vmon_loc;
+	u8 vmon_frm;
+	u8 vpmon_dpth;
+	u8 vpmon_loc;
+	u8 vpmon_frm;
+	u8 vbstmon_dpth;
+	u8 vbstmon_loc;
+	u8 vbstmon_frm;
+	u8 vpbrstat_dpth;
+	u8 vpbrstat_loc;
+	u8 vpbrstat_frm;
+	u8 zerofill_dpth;
+	u8 zerofill_loc;
+	u8 zerofill_frm;
+};
+
+struct cs35l35_platform_data {
+
+	/* Stereo (2 Device) */
+	bool stereo;
+	/* serial port drive strength */
+	int sp_drv_str;
+	/* Boost Power Down with FET */
+	bool bst_pdn_fet_on;
+	/* Boost Voltage : used if ClassH Algo Enabled */
+	int bst_vctl;
+	/* Boost Converter Peak Current CTRL */
+	int bst_ipk;
+	/* Amp Gain Zero Cross */
+	bool gain_zc;
+	/* Audio Input Location */
+	int aud_channel;
+	/* Advisory Input Location */
+	int adv_channel;
+	/* Shared Boost for stereo */
+	bool shared_bst;
+	/* ClassH Algorithm */
+	struct classh_cfg classh_algo;
+	/* Monitor Config */
+	struct monitor_cfg mon_cfg;
+};
+
+#endif /* __CS35L35_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9e1718a..3fd0a08 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -49,6 +49,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS35L32 if I2C
 	select SND_SOC_CS35L33 if I2C
 	select SND_SOC_CS35L34 if I2C
+	select SND_SOC_CS35L35 if I2C
 	select SND_SOC_CS42L42 if I2C
 	select SND_SOC_CS42L51_I2C if I2C
 	select SND_SOC_CS42L52 if I2C && INPUT
@@ -407,6 +408,10 @@ config SND_SOC_CS35L34
 	tristate "Cirrus Logic CS35L34 CODEC"
 	depends on I2C
 
+config SND_SOC_CS35L35
+	tristate "Cirrus Logic CS35L35 CODEC"
+	depends on I2C
+
 config SND_SOC_CS42L42
 	tristate "Cirrus Logic CS42L42 CODEC"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7e1dad7..a5622f6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -39,6 +39,7 @@ snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs35l32-objs := cs35l32.o
 snd-soc-cs35l33-objs := cs35l33.o
 snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs35l35-objs := cs35l35.o
 snd-soc-cs42l42-objs := cs42l42.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -268,6 +269,7 @@ obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS35L32)	+= snd-soc-cs35l32.o
 obj-$(CONFIG_SND_SOC_CS35L33)	+= snd-soc-cs35l33.o
 obj-$(CONFIG_SND_SOC_CS35L34)	+= snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35)	+= snd-soc-cs35l35.o
 obj-$(CONFIG_SND_SOC_CS42L42)	+= snd-soc-cs42l42.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)	+= snd-soc-cs42l51-i2c.o
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 0000000..ea84430
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1357 @@
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *         Li Xu <li.xu@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+
+static const struct reg_default cs35l35_reg[] = {
+	{CS35L35_PWRCTL1,		0x01},
+	{CS35L35_PWRCTL2,		0x11},
+	{CS35L35_PWRCTL3,		0x00},
+	{CS35L35_CLK_CTL1,		0x04},
+	{CS35L35_CLK_CTL2,		0x10},
+	{CS35L35_CLK_CTL3,		0xCF},
+	{CS35L35_SP_FMT_CTL1,		0x20},
+	{CS35L35_SP_FMT_CTL2,		0x00},
+	{CS35L35_SP_FMT_CTL3,		0x02},
+	{CS35L35_MAG_COMP_CTL,		0x00},
+	{CS35L35_AMP_INP_DRV_CTL,	0x01},
+	{CS35L35_AMP_DIG_VOL_CTL,	0x12},
+	{CS35L35_AMP_DIG_VOL,		0x00},
+	{CS35L35_ADV_DIG_VOL,		0x00},
+	{CS35L35_PROTECT_CTL,		0x06},
+	{CS35L35_AMP_GAIN_AUD_CTL,	0x13},
+	{CS35L35_AMP_GAIN_PDM_CTL,	0x00},
+	{CS35L35_AMP_GAIN_ADV_CTL,	0x00},
+	{CS35L35_GPI_CTL,		0x00},
+	{CS35L35_BST_CVTR_V_CTL,	0x00},
+	{CS35L35_BST_PEAK_I,		0x07},
+	{CS35L35_BST_RAMP_CTL,		0x85},
+	{CS35L35_BST_CONV_COEF_1,	0x20},
+	{CS35L35_BST_CONV_COEF_2,	0x20},
+	{CS35L35_BST_CONV_SLOPE_COMP,	0x47},
+	{CS35L35_BST_CONV_SW_FREQ,	0x04},
+	{CS35L35_CLASS_H_CTL,		0x0B},
+	{CS35L35_CLASS_H_HEADRM_CTL,	0x0B},
+	{CS35L35_CLASS_H_RELEASE_RATE,	0x08},
+	{CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+	{CS35L35_CLASS_H_VP_CTL,	0xC5},
+	{CS35L35_VPBR_CTL,		0x0A},
+	{CS35L35_VPBR_VOL_CTL,		0x09},
+	{CS35L35_VPBR_TIMING_CTL,	0x6A},
+	{CS35L35_VPBR_MODE_VOL_CTL,	0x00},
+	{CS35L35_SPKR_MON_CTL,		0xC0},
+	{CS35L35_IMON_SCALE_CTL,	0x30},
+	{CS35L35_AUDIN_RXLOC_CTL,	0x00},
+	{CS35L35_ADVIN_RXLOC_CTL,	0x80},
+	{CS35L35_VMON_TXLOC_CTL,	0x00},
+	{CS35L35_IMON_TXLOC_CTL,	0x80},
+	{CS35L35_VPMON_TXLOC_CTL,	0x04},
+	{CS35L35_VBSTMON_TXLOC_CTL,	0x84},
+	{CS35L35_VPBR_STATUS_TXLOC_CTL,	0x04},
+	{CS35L35_ZERO_FILL_LOC_CTL,	0x00},
+	{CS35L35_AUDIN_DEPTH_CTL,	0x0F},
+	{CS35L35_SPKMON_DEPTH_CTL,	0x0F},
+	{CS35L35_SUPMON_DEPTH_CTL,	0x0F},
+	{CS35L35_ZEROFILL_DEPTH_CTL,	0x00},
+	{CS35L35_MULT_DEV_SYNCH1,	0x02},
+	{CS35L35_MULT_DEV_SYNCH2,	0x80},
+	{CS35L35_PROT_RELEASE_CTL,	0x00},
+	{CS35L35_DIAG_MODE_REG_LOCK,	0x00},
+	{CS35L35_DIAG_MODE_CTL_1,	0x40},
+	{CS35L35_DIAG_MODE_CTL_2,	0x00},
+	{CS35L35_INT_MASK_1,		0xFF},
+	{CS35L35_INT_MASK_2,		0xFF},
+	{CS35L35_INT_MASK_3,		0xFF},
+	{CS35L35_INT_MASK_4,		0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_DEVID_AB ... CS35L35_REV_ID:
+	case CS35L35_INT_STATUS_1:
+	case CS35L35_INT_STATUS_2:
+	case CS35L35_INT_STATUS_3:
+	case CS35L35_INT_STATUS_4:
+	case CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+	case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+	case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+	case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+	case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+	case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+	case CS35L35_CLASS_H_STATUS:
+	case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+	case CS35L35_VPBR_ATTEN_STATUS:
+	case CS35L35_SPKR_MON_CTL:
+	case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+	case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+	case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+	case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_INT_STATUS_1:
+	case CS35L35_INT_STATUS_2:
+	case CS35L35_INT_STATUS_3:
+	case CS35L35_INT_STATUS_4:
+	case CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+			CS35L35_MCLK_DIS_MASK, 0 << CS35L35_MCLK_DIS_SHIFT);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_DISCHG_FILT_MASK, 0 << CS35L35_DISCHG_FILT_SHIFT);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL_MASK, 0);
+	break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL_MASK, 1);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_DISCHG_FILT_MASK, 1 << CS35L35_DISCHG_FILT_SHIFT);
+
+		ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+							msecs_to_jiffies(100));
+		if (ret == 0) {
+			pr_err("TIMEOUT PDN_DONE did not complete in 100ms\n");
+			ret = -ETIMEDOUT;
+		}
+
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+			CS35L35_MCLK_DIS_MASK, 1 << CS35L35_MCLK_DIS_SHIFT);
+	break;
+	default:
+		pr_err("Invalid event = 0x%x\n", event);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int cs35l35_main_amp_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	unsigned int reg[4];
+	int i;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (cs35l35->pdata.bst_pdn_fet_on)
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETON_SHIFT);
+		else
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+			regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+				CS35L35_AMP_MUTE_MASK, 0 << CS35L35_AMP_MUTE_SHIFT);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5100);
+		/* If PDM mode we must use VP
+		 * for Voltage control
+		 */
+		if (cs35l35->pdm_mode)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+				0 << CS35L35_BST_CTL_SHIFT);
+		for (i = 0; i < 2; i++)
+			regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+				&reg, ARRAY_SIZE(reg));
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+			CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+		if (cs35l35->pdata.bst_pdn_fet_on)
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+		else
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(5000, 5100);
+		/* If PDM mode we should switch back to pdata value
+		 * for Voltage control when we go down
+		 */
+		if (cs35l35->pdm_mode)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+				cs35l35->pdata.bst_vctl << CS35L35_BST_CTL_SHIFT);
+
+		break;
+	default:
+		pr_err("Invalid event = 0x%x\n", event);
+	}
+	return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+	SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+		      0, 0x34, 0xE4, dig_vol_tlv),
+	SOC_SINGLE_TLV("AMP Audio Gain", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+			amp_gain_tlv),
+	SOC_SINGLE_TLV("AMP PDM Gain", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+			amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+	SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+		      0, 0x34, 0xE4, dig_vol_tlv),
+	SOC_SINGLE_TLV("AMP Advisory Gain", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+			amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+				cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+	SND_SOC_DAPM_OUTPUT("SPK"),
+
+	SND_SOC_DAPM_INPUT("VP"),
+	SND_SOC_DAPM_INPUT("VBST"),
+	SND_SOC_DAPM_INPUT("ISENSE"),
+	SND_SOC_DAPM_INPUT("VSENSE"),
+
+	SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+	SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+	SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+	SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+	SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+	SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+		cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+	{"VPMON ADC", NULL, "VP"},
+	{"VBSTMON ADC", NULL, "VBST"},
+	{"IMON ADC", NULL, "ISENSE"},
+	{"VMON ADC", NULL, "VSENSE"},
+	{"SDOUT", NULL, "IMON ADC"},
+	{"SDOUT", NULL, "VMON ADC"},
+	{"SDOUT", NULL, "VBSTMON ADC"},
+	{"SDOUT", NULL, "VPMON ADC"},
+	{"AMP Capture", NULL, "SDOUT"},
+
+	{"SDIN", NULL, "AMP Playback"},
+	{"CLASS H", NULL, "SDIN"},
+	{"Main AMP", NULL, "CLASS H"},
+	{"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				    CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+		cs35l35->slave_mode = false;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				    CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+		cs35l35->slave_mode = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		cs35l35->i2s_mode = true;
+		cs35l35->pdm_mode = false;
+		break;
+	case SND_SOC_DAIFMT_PDM:
+		cs35l35->pdm_mode = true;
+		cs35l35->i2s_mode = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct cs35l35_sysclk_config {
+	int sysclk;
+	int srate;
+	u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+	/* SYSCLK, Sample Rate, Serial Port Cfg */
+	{5644800, 44100, 0x00},
+	{5644800, 88200, 0x40},
+	{6144000, 48000, 0x10},
+	{6144000, 96000, 0x50},
+	{11289600, 44100, 0x01},
+	{11289600, 88200, 0x41},
+	{11289600, 176400, 0x81},
+	{12000000, 44100, 0x03},
+	{12000000, 48000, 0x13},
+	{12000000, 88200, 0x43},
+	{12000000, 96000, 0x53},
+	{12000000, 176400, 0x83},
+	{12000000, 192000, 0x93},
+	{12288000, 48000, 0x11},
+	{12288000, 96000, 0x51},
+	{12288000, 192000, 0x91},
+	{13000000, 44100, 0x07},
+	{13000000, 48000, 0x17},
+	{13000000, 88200, 0x47},
+	{13000000, 96000, 0x57},
+	{13000000, 176400, 0x87},
+	{13000000, 192000, 0x97},
+	{22579200, 44100, 0x02},
+	{22579200, 88200, 0x42},
+	{22579200, 176400, 0x82},
+	{24000000, 44100, 0x0B},
+	{24000000, 48000, 0x1B},
+	{24000000, 88200, 0x4B},
+	{24000000, 96000, 0x5B},
+	{24000000, 176400, 0x8B},
+	{24000000, 192000, 0x9B},
+	{24576000, 48000, 0x12},
+	{24576000, 96000, 0x52},
+	{24576000, 192000, 0x92},
+	{26000000, 44100, 0x0F},
+	{26000000, 48000, 0x1F},
+	{26000000, 88200, 0x4F},
+	{26000000, 96000, 0x5F},
+	{26000000, 176400, 0x8F},
+	{26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+		if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+			cs35l35_clk_ctl[i].srate == srate)
+			return cs35l35_clk_ctl[i].clk_cfg;
+	}
+	return -EINVAL;
+}
+
+static int cs35l35_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+	int srate = params_rate(params);
+	int ret = 0;
+	u8 sp_sclks;
+	int audin_format;
+	int errata_chk;
+
+	int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+	if (clk_ctl < 0) {
+		dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
+			cs35l35->sysclk, srate);
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+			  CS35L35_CLK_CTL2_MASK, clk_ctl);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set port config %d\n", ret);
+		return ret;
+	}
+
+	/* Rev A0 Errata
+	 *
+	 * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+	 * the Class H algorithm does not enable weak-drive operation for
+	 * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+	 *
+	 */
+	errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
+
+	if (classh->classh_wk_fet_disable == 0x00 &&
+		(errata_chk == 0x01 || errata_chk == 0x03)) {
+		ret = regmap_update_bits(cs35l35->regmap,
+			CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+			0 << CS35L35_CH_WKFET_DEL_SHIFT);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to set fet config %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+/*
+ * You can pull more Monitor data from the SDOUT pin than going to SDIN
+ * Just make sure your SCLK is fast enough to fill the frame
+ */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		switch (params_width(params)) {
+		case 8:
+			audin_format = CS35L35_SDIN_DEPTH_8;
+			break;
+		case 16:
+			audin_format = CS35L35_SDIN_DEPTH_16;
+			break;
+		case 24:
+			audin_format = CS35L35_SDIN_DEPTH_24;
+			break;
+		default:
+			dev_err(codec->dev, "Unsupported Width %d\n",
+				params_width(params));
+		}
+		regmap_update_bits(cs35l35->regmap,
+			CS35L35_AUDIN_DEPTH_CTL, CS35L35_AUDIN_DEPTH_MASK,
+			audin_format << CS35L35_AUDIN_DEPTH_SHIFT);
+		if (cs35l35->pdata.stereo) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_AUDIN_DEPTH_CTL, CS35L35_ADVIN_DEPTH_MASK,
+				audin_format << CS35L35_ADVIN_DEPTH_SHIFT);
+		}
+	}
+/* We have to take the SCLK to derive num sclks
+ * to configure the CLOCK_CTL3 register correctly
+ */
+	if ((cs35l35->sclk / srate) % 4) {
+		dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
+					cs35l35->sclk, srate);
+		return -EINVAL;
+	}
+	sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+	if (cs35l35->i2s_mode) {
+		/* Only certain ratios are supported in I2S Slave Mode */
+		if (cs35l35->slave_mode) {
+			switch (sp_sclks) {
+			case CS35L35_SP_SCLKS_32FS:
+			case CS35L35_SP_SCLKS_48FS:
+			case CS35L35_SP_SCLKS_64FS:
+			break;
+			default:
+				dev_err(codec->dev, "ratio not supported\n");
+				return -EINVAL;
+			};
+		} else {
+			/* Only certain ratios supported in I2S MASTER Mode */
+			switch (sp_sclks) {
+			case CS35L35_SP_SCLKS_32FS:
+			case CS35L35_SP_SCLKS_64FS:
+			break;
+			default:
+				dev_err(codec->dev, "ratio not supported\n");
+				return -EINVAL;
+			};
+		}
+		ret = regmap_update_bits(cs35l35->regmap,
+			CS35L35_CLK_CTL3, CS35L35_SP_SCLKS_MASK,
+			sp_sclks << CS35L35_SP_SCLKS_SHIFT);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
+			return ret;
+		}
+	}
+	if (cs35l35->pdm_mode) {
+		regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+			CS35L35_PDM_MODE_MASK, 1 << CS35L35_PDM_MODE_SHIFT);
+	} else {
+		regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+			CS35L35_PDM_MODE_MASK, 0 << CS35L35_PDM_MODE_SHIFT);
+	}
+	return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+	44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+	.count  = ARRAY_SIZE(cs35l35_src_rates),
+	.list   = cs35l35_src_rates,
+};
+
+static int cs35l35_pcm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints);
+	return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+	44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+	.count  = ARRAY_SIZE(cs35l35_pdm_rates),
+	.list   = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&cs35l35_pdm_constraints);
+	return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+				int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	/* Need the SCLK Frequency */
+	cs35l35->sclk = freq;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+	.startup = cs35l35_pcm_startup,
+	.set_fmt = cs35l35_set_dai_fmt,
+	.hw_params = cs35l35_pcm_hw_params,
+	.set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+	.startup = cs35l35_pdm_startup,
+	.set_fmt = cs35l35_set_dai_fmt,
+	.hw_params = cs35l35_pcm_hw_params,
+	.set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+	{
+		.name = "cs35l35-pcm",
+		.id = 0,
+		.playback = {
+			.stream_name = "AMP Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AMP Capture",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.ops = &cs35l35_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "cs35l35-pdm",
+		.id = 1,
+		.playback = {
+			.stream_name = "PDM Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.ops = &cs35l35_pdm_ops,
+	},
+};
+
+static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
+				int clk_id, int source, unsigned int freq,
+				int dir)
+{
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	int clksrc;
+	int ret = 0;
+
+	switch (clk_id) {
+	case 0:
+		clksrc = CS35L35_CLK_SOURCE_MCLK;
+		break;
+	case 1:
+		clksrc = CS35L35_CLK_SOURCE_SCLK;
+		break;
+	case 2:
+		clksrc = CS35L35_CLK_SOURCE_PDM;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid CLK Source\n");
+		return -EINVAL;
+	};
+
+	switch (freq) {
+	case 5644800:
+	case 6144000:
+	case 11289600:
+	case 12000000:
+	case 12288000:
+	case 13000000:
+	case 22579200:
+	case 24000000:
+	case 24576000:
+	case 26000000:
+		cs35l35->sysclk = freq;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid CLK Frequency\n");
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+		CS35L35_CLK_SOURCE_MASK, clksrc << CS35L35_CLK_SOURCE_SHIFT);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set sysclk %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int cs35l35_codec_probe(struct snd_soc_codec *codec)
+{
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+	struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+	int ret;
+
+	/* Set Platform Data */
+	if (cs35l35->pdata.bst_vctl)
+		regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+			CS35L35_BST_CTL_MASK, cs35l35->pdata.bst_vctl);
+
+	if (cs35l35->pdata.bst_ipk)
+		regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+			CS35L35_BST_IPK_MASK,
+			cs35l35->pdata.bst_ipk << CS35L35_BST_IPK_SHIFT);
+
+	if (cs35l35->pdata.gain_zc)
+		regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+			CS35L35_AMP_GAIN_ZC_MASK,
+			cs35l35->pdata.gain_zc << CS35L35_AMP_GAIN_ZC_SHIFT);
+
+	if (cs35l35->pdata.aud_channel)
+		regmap_update_bits(cs35l35->regmap,
+			CS35L35_AUDIN_RXLOC_CTL,
+			CS35L35_AUD_IN_LR_MASK,
+			cs35l35->pdata.aud_channel << CS35L35_AUD_IN_LR_SHIFT);
+
+	if (cs35l35->pdata.stereo) {
+		regmap_update_bits(cs35l35->regmap,
+			CS35L35_ADVIN_RXLOC_CTL, CS35L35_ADV_IN_LR_MASK,
+			cs35l35->pdata.adv_channel << CS35L35_ADV_IN_LR_SHIFT);
+		if (cs35l35->pdata.shared_bst)
+			regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+				CS35L35_CH_STEREO_MASK, 1 << CS35L35_CH_STEREO_SHIFT);
+		ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
+					ARRAY_SIZE(cs35l35_adv_controls));
+		if (ret)
+			return ret;
+	}
+
+	if (cs35l35->pdata.sp_drv_str)
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+			CS35L35_SP_DRV_MASK,
+			cs35l35->pdata.sp_drv_str << CS35L35_SP_DRV_SHIFT);
+
+	if (classh->classh_algo_enable) {
+		if (classh->classh_bst_override)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_CTL, CS35L35_CH_BST_OVR_MASK,
+				classh->classh_bst_override << CS35L35_CH_BST_OVR_SHIFT);
+		if (classh->classh_bst_max_limit)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_CTL, CS35L35_CH_BST_LIM_MASK,
+				classh->classh_bst_max_limit << CS35L35_CH_BST_LIM_SHIFT);
+		if (classh->classh_mem_depth)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_CTL, CS35L35_CH_MEM_DEPTH_MASK,
+				classh->classh_mem_depth << CS35L35_CH_MEM_DEPTH_SHIFT);
+		if (classh->classh_headroom)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_HEADRM_CTL, CS35L35_CH_HDRM_CTL_MASK,
+				classh->classh_headroom << CS35L35_CH_HDRM_CTL_SHIFT);
+		if (classh->classh_release_rate)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_RELEASE_RATE, CS35L35_CH_REL_RATE_MASK,
+				classh->classh_release_rate << CS35L35_CH_REL_RATE_SHIFT);
+		if (classh->classh_wk_fet_disable)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DIS_MASK,
+				classh->classh_wk_fet_disable << CS35L35_CH_WKFET_DIS_SHIFT);
+		if (classh->classh_wk_fet_delay)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+				classh->classh_wk_fet_delay << CS35L35_CH_WKFET_DEL_SHIFT);
+		if (classh->classh_wk_fet_thld)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_THLD_MASK,
+				classh->classh_wk_fet_thld << CS35L35_CH_WKFET_THLD_SHIFT);
+		if (classh->classh_vpch_auto)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_AUTO_MASK,
+				classh->classh_vpch_auto << CS35L35_CH_VP_AUTO_SHIFT);
+		if (classh->classh_vpch_rate)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_RATE_MASK,
+				classh->classh_vpch_rate << CS35L35_CH_VP_RATE_SHIFT);
+		if (classh->classh_vpch_man)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_MAN_MASK,
+				classh->classh_vpch_man << CS35L35_CH_VP_MAN_SHIFT);
+	}
+
+	if (monitor_config->is_present) {
+		if (monitor_config->vmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SPKMON_DEPTH_CTL, CS35L35_VMON_DEPTH_MASK,
+				monitor_config->vmon_dpth << CS35L35_VMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vmon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vmon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->imon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SPKMON_DEPTH_CTL, CS35L35_IMON_DEPTH_MASK,
+				monitor_config->imon_dpth << CS35L35_IMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_IMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->imon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_IMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->imon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vpmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPMON_DEPTH_MASK,
+				monitor_config->vpmon_dpth << CS35L35_VPMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vpmon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vpmon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vbstmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_VBSTMON_DEPTH_MASK,
+				monitor_config->vpmon_dpth << CS35L35_VBSTMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vbstmon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vbstmon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vpbrstat_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPBRSTAT_DEPTH_MASK,
+				monitor_config->vpbrstat_dpth << CS35L35_VPBRSTAT_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vpbrstat_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vpbrstat_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->zerofill_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_ZEROFILL_DEPTH_MASK,
+				monitor_config->zerofill_dpth << CS35L35_ZEROFILL_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->zerofill_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->zerofill_frm << CS35L35_MON_FRM_SHIFT);
+		}
+	}
+
+	return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
+	.probe = cs35l35_codec_probe,
+	.set_sysclk = cs35l35_codec_set_sysclk,
+	.component_driver = {
+		.controls = cs35l35_aud_controls,
+		.num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+		.dapm_widgets = cs35l35_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+
+		.dapm_routes = cs35l35_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+	},
+};
+
+static struct regmap_config cs35l35_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CS35L35_MAX_REGISTER,
+	.reg_defaults = cs35l35_reg,
+	.num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+	.volatile_reg = cs35l35_volatile_register,
+	.readable_reg = cs35l35_readable_register,
+	.precious_reg = cs35l35_precious_register,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+	struct cs35l35_private *cs35l35 = data;
+	unsigned int sticky1, sticky2, sticky3, sticky4;
+	unsigned int mask1, mask2, mask3, mask4, current1;
+
+	/* ack the irq by reading all status registers */
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+	/* Check to see if unmasked bits are active */
+	if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+			&& !(sticky4 & ~mask4))
+		return IRQ_NONE;
+
+	if (sticky2 & CS35L35_PDN_DONE)
+		complete(&cs35l35->pdn_done);
+
+	/* read the current values */
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &current1);
+
+	/* handle the interrupts */
+	if (sticky1 & CS35L35_CAL_ERR) {
+		pr_err("%s : Calibration Error\n", __func__);
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_CAL_ERR)) {
+			pr_debug("%s : Cal error release\n", __func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS,
+				CS35L35_CAL_ERR_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_AMP_SHORT) {
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_AMP_SHORT)) {
+			pr_debug("%s :Amp short error release\n", __func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS,
+				CS35L35_SHORT_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_OTW) {
+		pr_err("%s : Over temperature warning\n", __func__);
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_OTW)) {
+			pr_debug("%s : Over temperature warning release\n",
+				__func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS,
+				CS35L35_OTW_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_OTE) {
+		pr_crit("%s : Over temperature error\n", __func__);
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_OTE)) {
+			pr_debug("%s : Over temperature error release\n",
+				__func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS,
+				CS35L35_OTE_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+		}
+	}
+
+	if (sticky3 & CS35L35_BST_HIGH) {
+		pr_crit("%s : VBST error: powering off!\n", __func__);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+	}
+
+	if (sticky3 & CS35L35_LBST_SHORT) {
+		pr_crit("%s : LBST error: powering off!\n", __func__);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+	}
+
+	if (sticky2 & CS35L35_VPBR_ERR)
+		pr_err("%s : Error: Reactive Brownout\n", __func__);
+
+	if (sticky4 & CS35L35_VMON_OVFL)
+		pr_err("%s : Error: VMON overflow\n", __func__);
+
+	if (sticky4 & CS35L35_IMON_OVFL)
+		pr_err("%s : Error: IMON overflow\n", __func__);
+
+	return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+				struct cs35l35_platform_data *pdata)
+{
+	struct device_node *np = i2c_client->dev.of_node;
+	struct device_node *classh, *signal_format;
+	struct classh_cfg *classh_config = &pdata->classh_algo;
+	struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+	unsigned int val32 = 0;
+	u8 monitor_array[3];
+	int ret = 0;
+
+	if (!np)
+		return 0;
+
+	pdata->bst_pdn_fet_on = of_property_read_bool(np,
+					"cirrus,boost-pdn-fet-on");
+
+	if (of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32) >= 0)
+		pdata->bst_vctl = val32;
+
+	if (of_property_read_u32(np, "cirrus,boost-ipk-milliamp", &val32) >= 0)
+		pdata->bst_ipk = val32;
+
+	if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+		pdata->sp_drv_str = val32;
+
+	pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+	if (pdata->stereo) {
+		if (of_property_read_u32(np, "cirrus,audio-channel", &val32) >= 0)
+			pdata->aud_channel = val32;
+		if (of_property_read_u32(np, "cirrus,advisory-channel",
+					&val32) >= 0)
+			pdata->adv_channel = val32;
+		pdata->shared_bst = of_property_read_bool(np,
+						"cirrus,shared-boost");
+	}
+
+	pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+	classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+	classh_config->classh_algo_enable = classh ? true : false;
+
+	if (classh_config->classh_algo_enable) {
+		classh_config->classh_bst_override =
+			of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+		if (of_property_read_u32(classh, "cirrus,classh-bst-max-limit",
+					&val32) >= 0)
+			classh_config->classh_bst_max_limit = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-mem-depth",
+					&val32) >= 0)
+			classh_config->classh_mem_depth = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-release-rate",
+					&val32) >= 0)
+			classh_config->classh_release_rate = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-headroom",
+					&val32) >= 0)
+			classh_config->classh_headroom = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-wk-fet-disable",
+					&val32) >= 0)
+			classh_config->classh_wk_fet_disable = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+					&val32) >= 0)
+			classh_config->classh_wk_fet_delay = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+					&val32) >= 0)
+			classh_config->classh_wk_fet_thld = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+					&val32) >= 0)
+			classh_config->classh_vpch_auto = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+					&val32) >= 0)
+			classh_config->classh_vpch_rate = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-vpch-man",
+					&val32) >= 0)
+			classh_config->classh_vpch_man = val32;
+	}
+	of_node_put(classh);
+
+	/* frame depth location */
+	signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+	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));
+		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];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vmon_specs = true;
+			monitor_config->vmon_dpth = monitor_array[0];
+			monitor_config->vmon_loc = monitor_array[1];
+			monitor_config->vmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vpmon_specs = true;
+			monitor_config->vpmon_dpth = monitor_array[0];
+			monitor_config->vpmon_loc = monitor_array[1];
+			monitor_config->vpmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vbstmon_specs = true;
+			monitor_config->vbstmon_dpth = monitor_array[0];
+			monitor_config->vbstmon_loc = monitor_array[1];
+			monitor_config->vbstmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vpbrstat_specs = true;
+			monitor_config->vpbrstat_dpth = monitor_array[0];
+			monitor_config->vpbrstat_loc = monitor_array[1];
+			monitor_config->vpbrstat_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->zerofill_specs = true;
+			monitor_config->zerofill_dpth = monitor_array[0];
+			monitor_config->zerofill_loc = monitor_array[1];
+			monitor_config->zerofill_frm = monitor_array[2];
+		}
+	}
+	of_node_put(signal_format);
+
+	return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+	{ 0x7F, 0x99 },
+	{ 0x00, 0x99 },
+	{ 0x52, 0x22 },
+	{ 0x04, 0x14 },
+	{ 0x6D, 0x44 },
+	{ 0x24, 0x10 },
+	{ 0x58, 0xC4 },
+	{ 0x00, 0x98 },
+	{ 0x18, 0x08 },
+	{ 0x00, 0x00 },
+	{ 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
+			      const struct i2c_device_id *id)
+{
+	struct cs35l35_private *cs35l35;
+	struct cs35l35_platform_data *pdata =
+		dev_get_platdata(&i2c_client->dev);
+	int i;
+	int ret;
+	unsigned int devid = 0;
+	unsigned int reg;
+
+	cs35l35 = devm_kzalloc(&i2c_client->dev,
+			       sizeof(struct cs35l35_private),
+			       GFP_KERNEL);
+	if (!cs35l35) {
+		dev_err(&i2c_client->dev, "could not allocate codec\n");
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(i2c_client, cs35l35);
+	cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+	if (IS_ERR(cs35l35->regmap)) {
+		ret = PTR_ERR(cs35l35->regmap);
+		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+		cs35l35->supplies[i].supply = cs35l35_supplies[i];
+		cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+	ret = devm_regulator_bulk_get(&i2c_client->dev,
+			cs35l35->num_supplies,
+			cs35l35->supplies);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev,
+			"Failed to request core supplies: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (pdata) {
+		cs35l35->pdata = *pdata;
+	} else {
+		pdata = devm_kzalloc(&i2c_client->dev,
+				     sizeof(struct cs35l35_platform_data),
+				GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&i2c_client->dev,
+				"could not allocate pdata\n");
+			return -ENOMEM;
+		}
+		if (i2c_client->dev.of_node) {
+			ret = cs35l35_handle_of_data(i2c_client, pdata);
+			if (ret != 0)
+				return ret;
+
+		}
+		cs35l35->pdata = *pdata;
+	}
+
+	ret = regulator_bulk_enable(cs35l35->num_supplies,
+					cs35l35->supplies);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev,
+			"Failed to enable core supplies: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* returning NULL can be an option if in stereo mode */
+	cs35l35->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+		"reset", GPIOD_OUT_LOW);
+	if (IS_ERR(cs35l35->reset_gpio))
+		return PTR_ERR(cs35l35->reset_gpio);
+
+	if (cs35l35->reset_gpio)
+		gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+
+	init_completion(&cs35l35->pdn_done);
+
+	ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+				    ARRAY_SIZE(cs35l35_errata_patch));
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+			cs35l35_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+			"cs35l35", cs35l35);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+		goto err;
+	}
+	/* initialize codec */
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, &reg);
+
+	devid = (reg & 0xFF) << 12;
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, &reg);
+	devid |= (reg & 0xFF) << 4;
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, &reg);
+	devid |= (reg & 0xF0) >> 4;
+
+	if (devid != CS35L35_CHIP_ID) {
+		dev_err(&i2c_client->dev,
+			"CS35L35 Device ID (%X). Expected ID %X\n",
+			devid, CS35L35_CHIP_ID);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, &reg);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+		goto err;
+	}
+
+	dev_info(&i2c_client->dev,
+		 "Cirrus Logic CS35L35 (%x), Revision: %02X\n", devid,
+		ret & 0xFF);
+
+	/* Set the INT Masks for critical errors */
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1, CS35L35_INT1_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2, CS35L35_INT2_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3, CS35L35_INT3_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4, CS35L35_INT4_CRIT_MASK);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+		CS35L35_PWR2_PDN_MASK, CS35L35_PWR2_PDN_MASK);
+
+	if (cs35l35->pdata.bst_pdn_fet_on)
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+	else
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+		CS35L35_PWR3_PDN_MASK, CS35L35_PWR3_PDN_MASK);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+		CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+	ret =  snd_soc_register_codec(&i2c_client->dev,
+			&soc_codec_dev_cs35l35, cs35l35_dai,
+			ARRAY_SIZE(cs35l35_dai));
+	if (ret < 0) {
+		dev_err(&i2c_client->dev,
+			"%s: Register codec failed\n", __func__);
+		goto err;
+	}
+
+err:
+	regulator_bulk_disable(cs35l35->num_supplies,
+			       cs35l35->supplies);
+	return ret;
+}
+
+static int cs35l35_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+	{.compatible = "cirrus,cs35l35"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+	{"cs35l35", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+	.driver = {
+		.name = "cs35l35",
+		.of_match_table = cs35l35_of_match,
+	},
+	.id_table = cs35l35_id,
+	.probe = cs35l35_i2c_probe,
+	.remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_AUTHOR("Li Xu, Cirrus Logic Inc, <li.xu@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 0000000..767227e
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,285 @@
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *         Li Xu <li.xu@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG		0x01
+#define CS35L35_LASTREG			0x7E
+#define CS35L35_CHIP_ID			0x00035A35
+#define CS35L35_DEVID_AB		0x01	/* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD		0x02    /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E			0x03    /* Device ID E [RO] */
+#define CS35L35_FAB_ID			0x04	/* Fab ID [RO] */
+#define CS35L35_REV_ID			0x05	/* Revision ID [RO] */
+#define CS35L35_PWRCTL1			0x06    /* Power Ctl 1 */
+#define CS35L35_PWRCTL2			0x07    /* Power Ctl 2 */
+#define CS35L35_PWRCTL3			0x08	/* Power Ctl 3 */
+#define CS35L35_CLK_CTL1		0x0A	/* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2		0x0B	/* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3		0x0C	/* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1		0x0D	/* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2		0x0E	/* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3		0x0F	/* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL		0x13	/* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL		0x14	/* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL		0x15	/* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL		0x16	/* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL		0x17	/* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL		0x18	/* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL	0x19	/* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL	0x1A	/* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL	0x1B	/* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL			0x1C	/* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL		0x1D	/* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I		0x1E	/* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL		0x20	/* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1		0x21	/* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2		0x22	/* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP	0x23	/* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ	0x24	/* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL		0x30	/* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL	0x31	/* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE	0x32	/* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL	0x33	/* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL		0x34	/* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS		0x38	/* CLS H Status */
+#define CS35L35_VPBR_CTL		0x3A	/* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL		0x3B	/* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL		0x3C	/* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL	0x3D	/* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS	0x4B	/* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL		0x4E	/* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL		0x51	/* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL		0x52	/* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL		0x53	/* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL		0x54	/* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL		0x55	/* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL		0x56	/* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL	0x57	/* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL	0x58	/* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL	0x59	/* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL		0x5A	/* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL	0x5B	/* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL	0x5C	/* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL	0x5D	/* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1		0x62	/* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2		0x63	/* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL	0x64	/* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK	0x68	/* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1		0x69	/* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2		0x6A	/* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1		0x70	/* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2		0x71	/* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3		0x72	/* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4		0x73	/* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1		0x74	/* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2		0x75	/* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3		0x76	/* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4		0x77	/* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS		0x78	/* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS		0x7E	/* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER		0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST			0x80
+#define CS35L35_DISCHG_FLT		0x02
+#define CS35L35_PDN_ALL			0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON		0x80
+#define CS35L35_PDN_IMON		0x40
+#define CS35L35_PDN_CLASSH		0x20
+#define CS35L35_PDN_VPBR		0x10
+#define CS35L35_PDN_BST			0x04
+#define CS35L35_PDN_AMP			0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT		0x10
+#define CS35L35_PDN_VMON_OUT		0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK	0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT	0
+#define CS35L35_ADVIN_DEPTH_MASK	0x12
+#define CS35L35_ADVIN_DEPTH_SHIFT	2
+#define CS35L35_SDIN_DEPTH_8		0x01
+#define CS35L35_SDIN_DEPTH_16		0x02
+#define CS35L35_SDIN_DEPTH_24		0x03
+
+#define CS35L35_SDOUT_DEPTH_8		0x01
+#define CS35L35_SDOUT_DEPTH_12		0x02
+#define CS35L35_SDOUT_DEPTH_16		0x03
+
+#define CS35L35_AUD_IN_LR_MASK		0x80
+#define CS35L35_AUD_IN_LR_SHIFT		7
+#define CS35L35_ADV_IN_LR_MASK		0x80
+#define CS35L35_ADV_IN_LR_SHIFT		7
+#define CS35L35_AUD_IN_LOC_MASK		0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT	0
+#define CS35L35_ADV_IN_LOC_MASK		0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT	0
+
+#define CS35L35_IMON_DEPTH_MASK		0x03
+#define CS35L35_IMON_DEPTH_SHIFT	0
+#define CS35L35_VMON_DEPTH_MASK		0x0C
+#define CS35L35_VMON_DEPTH_SHIFT	2
+#define CS35L35_VBSTMON_DEPTH_MASK	0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT	0
+#define CS35L35_VPMON_DEPTH_MASK	0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT	2
+#define CS35L35_VPBRSTAT_DEPTH_MASK	0x18
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT	4
+#define CS35L35_ZEROFILL_DEPTH_MASK	0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT	0x00
+
+#define CS35L35_MON_TXLOC_MASK		0x3F
+#define CS35L35_MON_TXLOC_SHIFT		0
+#define CS35L35_MON_FRM_MASK		0x80
+#define CS35L35_MON_FRM_SHIFT		7
+
+#define CS35L35_MS_MASK			0x80
+#define CS35L35_MS_SHIFT		7
+#define CS35L35_SPMODE_MASK		0x40
+#define CS35L35_SP_DRV_MASK		0x10
+#define CS35L35_SP_DRV_SHIFT		4
+#define CS35L35_CLK_CTL2_MASK		0xFF
+#define CS35L35_PDM_MODE_MASK		0x40
+#define CS35L35_PDM_MODE_SHIFT		6
+#define CS35L35_CLK_SOURCE_MASK		0x03
+#define CS35L35_CLK_SOURCE_SHIFT	0
+#define CS35L35_CLK_SOURCE_MCLK		0
+#define CS35L35_CLK_SOURCE_SCLK		1
+#define CS35L35_CLK_SOURCE_PDM		2
+
+#define CS35L35_SP_SCLKS_MASK		0x0F
+#define CS35L35_SP_SCLKS_SHIFT		0x00
+#define CS35L35_SP_SCLKS_16FS		0x03
+#define CS35L35_SP_SCLKS_32FS		0x07
+#define CS35L35_SP_SCLKS_48FS		0x0B
+#define CS35L35_SP_SCLKS_64FS		0x0F
+#define CS35L35_SP_RATE_MASK		0xC0
+
+#define CS35L35_PDN_BST_MASK		0x06
+#define CS35L35_PDN_BST_FETON_SHIFT	1
+#define CS35L35_PDN_BST_FETOFF_SHIFT	2
+#define CS35L35_PWR2_PDN_MASK		0xE0
+#define CS35L35_PWR3_PDN_MASK		0x1E
+#define CS35L35_PDN_ALL_MASK		0x01
+#define CS35L35_DISCHG_FILT_MASK	0x02
+#define CS35L35_DISCHG_FILT_SHIFT	1
+#define CS35L35_MCLK_DIS_MASK		0x04
+#define CS35L35_MCLK_DIS_SHIFT		2
+
+#define CS35L35_BST_CTL_MASK		0x7F
+#define CS35L35_BST_CTL_SHIFT		0
+#define CS35L35_BST_IPK_MASK		0x1F
+#define CS35L35_BST_IPK_SHIFT		0
+#define CS35L35_AMP_MUTE_MASK		0x20
+#define CS35L35_AMP_MUTE_SHIFT		5
+#define CS35L35_AMP_GAIN_ZC_MASK	0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT	4
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK		0x40
+#define CS35L35_CH_STEREO_SHIFT		6
+#define CS35L35_CH_BST_OVR_MASK		0x04
+#define CS35L35_CH_BST_OVR_SHIFT	2
+#define CS35L35_CH_BST_LIM_MASK		0x08
+#define CS35L35_CH_BST_LIM_SHIFT	3
+#define CS35L35_CH_MEM_DEPTH_MASK	0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT	0
+#define CS35L35_CH_HDRM_CTL_MASK	0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT	0
+#define CS35L35_CH_REL_RATE_MASK	0xFF
+#define CS35L35_CH_REL_RATE_SHIFT	0
+#define CS35L35_CH_WKFET_DIS_MASK	0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT	7
+#define CS35L35_CH_WKFET_DEL_MASK	0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT	4
+#define CS35L35_CH_WKFET_THLD_MASK	0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT	0
+#define CS35L35_CH_VP_AUTO_MASK		0x80
+#define CS35L35_CH_VP_AUTO_SHIFT	7
+#define CS35L35_CH_VP_RATE_MASK		0x60
+#define CS35L35_CH_VP_RATE_SHIFT	5
+#define CS35L35_CH_VP_MAN_MASK		0x1F
+#define CS35L35_CH_VP_MAN_SHIFT		0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS		0x80
+#define CS35L35_SHORT_RLS		0x04
+#define CS35L35_OTW_RLS			0x02
+#define CS35L35_OTE_RLS			0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK		0x38
+#define CS35L35_INT2_CRIT_MASK		0xEF
+#define CS35L35_INT3_CRIT_MASK		0xEE
+#define CS35L35_INT4_CRIT_MASK		0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT	4
+#define CS35L35_M_PDN_DONE_MASK		0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR			0x80
+#define CS35L35_OTP_ERR			0x40
+#define CS35L35_LRCLK_ERR		0x20
+#define CS35L35_SPCLK_ERR		0x10
+#define CS35L35_MCLK_ERR		0x08
+#define CS35L35_AMP_SHORT		0x04
+#define CS35L35_OTW			0x02
+#define CS35L35_OTE			0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE		0x10
+#define CS35L35_VPBR_ERR		0x02
+#define CS35L35_VPBR_CLR		0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH		0x10
+#define CS35L35_BST_HIGH_FLAG		0x08
+#define CS35L35_BST_IPK_FLAG		0x04
+#define CS35L35_LBST_SHORT		0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL		0x08
+#define CS35L35_IMON_OVFL		0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct  cs35l35_private {
+	struct snd_soc_codec *codec;
+	struct cs35l35_platform_data pdata;
+	struct regmap *regmap;
+	struct regulator_bulk_data supplies[2];
+	int num_supplies;
+	int sysclk;
+	int sclk;
+	bool pdm_mode;
+	bool i2s_mode;
+	bool slave_mode;
+	/* GPIO for /RST */
+	struct gpio_desc *reset_gpio;
+	struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+	"VA",
+	"VP",
+};
+
+#endif
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH 3/6] ARM: dts: sun8i: add a cpu0 label to cpu@0 node on A23/33
From: Sudeep Holla @ 2016-12-13 16:09 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: devicetree, Quentin Schulz, Michael Turquette, Stephen Boyd,
	Russell King, linux-kernel, Hans de Goede, Chen-Yu Tsai,
	Sudeep Holla, Maxime Ripard, linux-clk, linux-arm-kernel,
	Jorik Jonker
In-Reply-To: <20161213152252.53749-4-icenowy@aosc.xyz>



On 13/12/16 15:22, Icenowy Zheng wrote:
> A "cpu0" label is needed on cpu@0 for cpufreq-dt to work.
> 

IIUC any label should be fine and I don't see anything in the driver
looking for such label name. All I see is it looks for cpu0 regulator
for *legacy* DTs

> Add such a label, in order to prepare for cpufreq support of A23/33.
> 

You need this as you add the same label in the following patches. The
commit message sounds like cpufreq-dt search for that label by name.

-- 
Regards,
Sudeep

^ permalink raw reply

* [PATCH 2/2] ASoC: cs35l35: Add device tree documentation for CS35L35
From: Li Xu @ 2016-12-13 16:08 UTC (permalink / raw)
  To: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k,
	brian.austin-jGc1dHjMKG3QT0dZR+AlfA,
	Paul.Handrigan-jGc1dHjMKG3QT0dZR+AlfA, Li Xu
In-Reply-To: <1481645331-15114-1-git-send-email-li.xu-jGc1dHjMKG3QT0dZR+AlfA@public.gmane.org>

Add device tree documentation for Cirrus Logic CS35L35
speaker amplifier

Signed-off-by: Li Xu <li.xu-jGc1dHjMKG3QT0dZR+AlfA@public.gmane.org>
---
 .../devicetree/bindings/sound/cs35l35.txt          | 172 +++++++++++++++++++++
 1 file changed, 172 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/cs35l35.txt

diff --git a/Documentation/devicetree/bindings/sound/cs35l35.txt b/Documentation/devicetree/bindings/sound/cs35l35.txt
new file mode 100644
index 0000000..8b13f67
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/cs35l35.txt
@@ -0,0 +1,172 @@
+CS35L35 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:
+  - cirrus,reset-gpios : Active low 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 Converter control word. Step Size of 100mV
+  0x00 = (Default) VP
+  0x01 = 2600mV
+  0x41 = 9000mV
+
+  - cirrus,boost-ipk-milliamp : Boost-converter peak current limit.
+  Configures the peak current by monitoring the current through the boost FET.
+  Step size: 112mA
+  0x00 = 1680mA
+  0x19 = 4480mA
+
+  - 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: audio-codec@20 {
+	compatible = "cirrus,cs35l35";
+	reg = <0x20>;
+	VA-supply = <&dummy_vreg>;
+	VP-supply = <&dummy_vreg>;
+	reset-gpios = <&axi_gpio 54 1>;
+	interrupt-parent = <&gpio8>;
+	interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+	cirrus,boost-ctl = <0x41>;
+
+	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>;
+	};
+
+};
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 1/2] ASoC: cs35l35: Add support for Cirrus CS35L35 Amplifier
From: Li Xu @ 2016-12-13 16:08 UTC (permalink / raw)
  To: alsa-devel, devicetree
  Cc: mark.rutland, brian.austin, tiwai, robh+dt, lgirdwood, broonie,
	Paul.Handrigan, Li Xu

Add driver support for Cirrus Logic CS35L35 boosted
speaker amplifier

Signed-off-by: Li Xu <li.xu@cirrus.com>
---
 include/sound/cs35l35.h    |  104 ++++
 sound/soc/codecs/Kconfig   |    5 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/cs35l35.c | 1355 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/cs35l35.h |  284 ++++++++++
 5 files changed, 1750 insertions(+)
 create mode 100644 include/sound/cs35l35.h
 create mode 100644 sound/soc/codecs/cs35l35.c
 create mode 100644 sound/soc/codecs/cs35l35.h

diff --git a/include/sound/cs35l35.h b/include/sound/cs35l35.h
new file mode 100644
index 0000000..005a813
--- /dev/null
+++ b/include/sound/cs35l35.h
@@ -0,0 +1,104 @@
+/*
+ * linux/sound/cs35l35.h -- Platform data for CS35l35
+ *
+ * Copyright (c) 2016 Cirrus Logic Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CS35L35_H
+#define __CS35L35_H
+
+struct classh_cfg {
+	/*
+	 * Class H Algorithm Control Variables
+	 * You can either have it done
+	 * automatically or you can adjust
+	 * these variables for tuning
+	 *
+	 * if you do not enable the internal algorithm
+	 * you will get a set of mixer controls for
+	 * Class H tuning
+	 *
+	 * Section 4.3 of the datasheet
+	 */
+	/* Internal ClassH Algorithm  */
+	bool classh_bst_override;
+	bool classh_algo_enable;
+	int classh_bst_max_limit;
+	int classh_mem_depth;
+	int classh_release_rate;
+	int classh_headroom;
+	int classh_wk_fet_disable;
+	int classh_wk_fet_delay;
+	int classh_wk_fet_thld;
+	int classh_vpch_auto;
+	int classh_vpch_rate;
+	int classh_vpch_man;
+};
+
+struct monitor_cfg {
+	/*
+	 * Signal Monitor Data
+	 * highly configurable signal monitoring
+	 * data positioning and different types of
+	 * monitoring data.
+	 *
+	 * Section 4.8.2 - 4.8.4 of the datasheet
+	 */
+	bool is_present;
+	bool imon_specs;
+	bool vmon_specs;
+	bool vpmon_specs;
+	bool vbstmon_specs;
+	bool vpbrstat_specs;
+	bool zerofill_specs;
+	u8 imon_dpth;
+	u8 imon_loc;
+	u8 imon_frm;
+	u8 vmon_dpth;
+	u8 vmon_loc;
+	u8 vmon_frm;
+	u8 vpmon_dpth;
+	u8 vpmon_loc;
+	u8 vpmon_frm;
+	u8 vbstmon_dpth;
+	u8 vbstmon_loc;
+	u8 vbstmon_frm;
+	u8 vpbrstat_dpth;
+	u8 vpbrstat_loc;
+	u8 vpbrstat_frm;
+	u8 zerofill_dpth;
+	u8 zerofill_loc;
+	u8 zerofill_frm;
+};
+
+struct cs35l35_platform_data {
+
+	/* Stereo (2 Device) */
+	bool stereo;
+	/* serial port drive strength */
+	int sp_drv_str;
+	/* Boost Power Down with FET */
+	bool bst_pdn_fet_on;
+	/* Boost Voltage : used if ClassH Algo Enabled */
+	int bst_vctl;
+	/* Boost Converter Peak Current CTRL */
+	int bst_ipk;
+	/* Amp Gain Zero Cross */
+	bool gain_zc;
+	/* Audio Input Location */
+	int aud_channel;
+	/* Advisory Input Location */
+	int adv_channel;
+	/* Shared Boost for stereo */
+	bool shared_bst;
+	/* ClassH Algorithm */
+	struct classh_cfg classh_algo;
+	/* Monitor Config */
+	struct monitor_cfg mon_cfg;
+};
+
+#endif /* __CS35L35_H */
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9e1718a..3fd0a08 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -49,6 +49,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS35L32 if I2C
 	select SND_SOC_CS35L33 if I2C
 	select SND_SOC_CS35L34 if I2C
+	select SND_SOC_CS35L35 if I2C
 	select SND_SOC_CS42L42 if I2C
 	select SND_SOC_CS42L51_I2C if I2C
 	select SND_SOC_CS42L52 if I2C && INPUT
@@ -407,6 +408,10 @@ config SND_SOC_CS35L34
 	tristate "Cirrus Logic CS35L34 CODEC"
 	depends on I2C
 
+config SND_SOC_CS35L35
+	tristate "Cirrus Logic CS35L35 CODEC"
+	depends on I2C
+
 config SND_SOC_CS42L42
 	tristate "Cirrus Logic CS42L42 CODEC"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 7e1dad7..a5622f6 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -39,6 +39,7 @@ snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs35l32-objs := cs35l32.o
 snd-soc-cs35l33-objs := cs35l33.o
 snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs35l35-objs := cs35l35.o
 snd-soc-cs42l42-objs := cs42l42.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -268,6 +269,7 @@ obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS35L32)	+= snd-soc-cs35l32.o
 obj-$(CONFIG_SND_SOC_CS35L33)	+= snd-soc-cs35l33.o
 obj-$(CONFIG_SND_SOC_CS35L34)	+= snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35)	+= snd-soc-cs35l35.o
 obj-$(CONFIG_SND_SOC_CS42L42)	+= snd-soc-cs42l42.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)	+= snd-soc-cs42l51-i2c.o
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 0000000..b05e900
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1355 @@
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+
+static const struct reg_default cs35l35_reg[] = {
+	{CS35L35_PWRCTL1,		0x01},
+	{CS35L35_PWRCTL2,		0x11},
+	{CS35L35_PWRCTL3,		0x00},
+	{CS35L35_CLK_CTL1,		0x04},
+	{CS35L35_CLK_CTL2,		0x10},
+	{CS35L35_CLK_CTL3,		0xCF},
+	{CS35L35_SP_FMT_CTL1,		0x20},
+	{CS35L35_SP_FMT_CTL2,		0x00},
+	{CS35L35_SP_FMT_CTL3,		0x02},
+	{CS35L35_MAG_COMP_CTL,		0x00},
+	{CS35L35_AMP_INP_DRV_CTL,	0x01},
+	{CS35L35_AMP_DIG_VOL_CTL,	0x12},
+	{CS35L35_AMP_DIG_VOL,		0x00},
+	{CS35L35_ADV_DIG_VOL,		0x00},
+	{CS35L35_PROTECT_CTL,		0x06},
+	{CS35L35_AMP_GAIN_AUD_CTL,	0x13},
+	{CS35L35_AMP_GAIN_PDM_CTL,	0x00},
+	{CS35L35_AMP_GAIN_ADV_CTL,	0x00},
+	{CS35L35_GPI_CTL,		0x00},
+	{CS35L35_BST_CVTR_V_CTL,	0x00},
+	{CS35L35_BST_PEAK_I,		0x07},
+	{CS35L35_BST_RAMP_CTL,		0x85},
+	{CS35L35_BST_CONV_COEF_1,	0x20},
+	{CS35L35_BST_CONV_COEF_2,	0x20},
+	{CS35L35_BST_CONV_SLOPE_COMP,	0x47},
+	{CS35L35_BST_CONV_SW_FREQ,	0x04},
+	{CS35L35_CLASS_H_CTL,		0x0B},
+	{CS35L35_CLASS_H_HEADRM_CTL,	0x0B},
+	{CS35L35_CLASS_H_RELEASE_RATE,	0x08},
+	{CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+	{CS35L35_CLASS_H_VP_CTL,	0xC5},
+	{CS35L35_VPBR_CTL,		0x0A},
+	{CS35L35_VPBR_VOL_CTL,		0x09},
+	{CS35L35_VPBR_TIMING_CTL,	0x6A},
+	{CS35L35_VPBR_MODE_VOL_CTL,	0x00},
+	{CS35L35_SPKR_MON_CTL,		0xC0},
+	{CS35L35_IMON_SCALE_CTL,	0x30},
+	{CS35L35_AUDIN_RXLOC_CTL,	0x00},
+	{CS35L35_ADVIN_RXLOC_CTL,	0x80},
+	{CS35L35_VMON_TXLOC_CTL,	0x00},
+	{CS35L35_IMON_TXLOC_CTL,	0x80},
+	{CS35L35_VPMON_TXLOC_CTL,	0x04},
+	{CS35L35_VBSTMON_TXLOC_CTL,	0x84},
+	{CS35L35_VPBR_STATUS_TXLOC_CTL,	0x04},
+	{CS35L35_ZERO_FILL_LOC_CTL,	0x00},
+	{CS35L35_AUDIN_DEPTH_CTL,	0x0F},
+	{CS35L35_SPKMON_DEPTH_CTL,	0x0F},
+	{CS35L35_SUPMON_DEPTH_CTL,	0x0F},
+	{CS35L35_ZEROFILL_DEPTH_CTL,	0x00},
+	{CS35L35_MULT_DEV_SYNCH1,	0x02},
+	{CS35L35_MULT_DEV_SYNCH2,	0x80},
+	{CS35L35_PROT_RELEASE_CTL,	0x00},
+	{CS35L35_DIAG_MODE_REG_LOCK,	0x00},
+	{CS35L35_DIAG_MODE_CTL_1,	0x40},
+	{CS35L35_DIAG_MODE_CTL_2,	0x00},
+	{CS35L35_INT_MASK_1,		0xFF},
+	{CS35L35_INT_MASK_2,		0xFF},
+	{CS35L35_INT_MASK_3,		0xFF},
+	{CS35L35_INT_MASK_4,		0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_DEVID_AB ... CS35L35_REV_ID:
+	case CS35L35_INT_STATUS_1:
+	case CS35L35_INT_STATUS_2:
+	case CS35L35_INT_STATUS_3:
+	case CS35L35_INT_STATUS_4:
+	case CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+	case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+	case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+	case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+	case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+	case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+	case CS35L35_CLASS_H_STATUS:
+	case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+	case CS35L35_VPBR_ATTEN_STATUS:
+	case CS35L35_SPKR_MON_CTL:
+	case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+	case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+	case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+	case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case CS35L35_INT_STATUS_1:
+	case CS35L35_INT_STATUS_2:
+	case CS35L35_INT_STATUS_3:
+	case CS35L35_INT_STATUS_4:
+	case CS35L35_PLL_STATUS:
+	case CS35L35_OTP_TRIM_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+			CS35L35_MCLK_DIS_MASK, 0 << CS35L35_MCLK_DIS_SHIFT);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_DISCHG_FILT_MASK, 0 << CS35L35_DISCHG_FILT_SHIFT);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL_MASK, 0);
+	break;
+	case SND_SOC_DAPM_POST_PMD:
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL_MASK, 1);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_DISCHG_FILT_MASK, 1 << CS35L35_DISCHG_FILT_SHIFT);
+
+		ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+							msecs_to_jiffies(100));
+		if (ret == 0) {
+			pr_err("TIMEOUT PDN_DONE did not complete in 100ms\n");
+			ret = -ETIMEDOUT;
+		}
+
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+			CS35L35_MCLK_DIS_MASK, 1 << CS35L35_MCLK_DIS_SHIFT);
+	break;
+	default:
+		pr_err("Invalid event = 0x%x\n", event);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int cs35l35_main_amp_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 cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	unsigned int reg[4];
+	int i;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (cs35l35->pdata.bst_pdn_fet_on)
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETON_SHIFT);
+		else
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+			regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+				CS35L35_AMP_MUTE_MASK, 0 << CS35L35_AMP_MUTE_SHIFT);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5100);
+		/* If PDM mode we must use VP
+		 * for Voltage control
+		 */
+		if (cs35l35->pdm_mode)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+				0 << CS35L35_BST_CTL_SHIFT);
+		for (i = 0; i < 2; i++)
+			regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+				&reg, ARRAY_SIZE(reg));
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+			CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+		if (cs35l35->pdata.bst_pdn_fet_on)
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+		else
+			regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+				CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(5000, 5100);
+		/* If PDM mode we should switch back to pdata value
+		 * for Voltage control when we go down
+		 */
+		if (cs35l35->pdm_mode)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_BST_CVTR_V_CTL, CS35L35_BST_CTL_MASK,
+				cs35l35->pdata.bst_vctl << CS35L35_BST_CTL_SHIFT);
+
+		break;
+	default:
+		pr_err("Invalid event = 0x%x\n", event);
+	}
+	return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+	SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+		      0, 0x34, 0xE4, dig_vol_tlv),
+	SOC_SINGLE_TLV("AMP Audio Gain", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+			amp_gain_tlv),
+	SOC_SINGLE_TLV("AMP PDM Gain", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+			amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+	SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+		      0, 0x34, 0xE4, dig_vol_tlv),
+	SOC_SINGLE_TLV("AMP Advisory Gain", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+			amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+				cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+	SND_SOC_DAPM_OUTPUT("SPK"),
+
+	SND_SOC_DAPM_INPUT("VP"),
+	SND_SOC_DAPM_INPUT("VBST"),
+	SND_SOC_DAPM_INPUT("ISENSE"),
+	SND_SOC_DAPM_INPUT("VSENSE"),
+
+	SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+	SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+	SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+	SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+	SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+	SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+		cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+	{"VPMON ADC", NULL, "VP"},
+	{"VBSTMON ADC", NULL, "VBST"},
+	{"IMON ADC", NULL, "ISENSE"},
+	{"VMON ADC", NULL, "VSENSE"},
+	{"SDOUT", NULL, "IMON ADC"},
+	{"SDOUT", NULL, "VMON ADC"},
+	{"SDOUT", NULL, "VBSTMON ADC"},
+	{"SDOUT", NULL, "VPMON ADC"},
+	{"AMP Capture", NULL, "SDOUT"},
+
+	{"SDIN", NULL, "AMP Playback"},
+	{"CLASS H", NULL, "SDIN"},
+	{"Main AMP", NULL, "CLASS H"},
+	{"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				    CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+		cs35l35->slave_mode = false;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+				    CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+		cs35l35->slave_mode = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		cs35l35->i2s_mode = true;
+		cs35l35->pdm_mode = false;
+		break;
+	case SND_SOC_DAIFMT_PDM:
+		cs35l35->pdm_mode = true;
+		cs35l35->i2s_mode = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct cs35l35_sysclk_config {
+	int sysclk;
+	int srate;
+	u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+	/* SYSCLK, Sample Rate, Serial Port Cfg */
+	{5644800, 44100, 0x00},
+	{5644800, 88200, 0x40},
+	{6144000, 48000, 0x10},
+	{6144000, 96000, 0x50},
+	{11289600, 44100, 0x01},
+	{11289600, 88200, 0x41},
+	{11289600, 176400, 0x81},
+	{12000000, 44100, 0x03},
+	{12000000, 48000, 0x13},
+	{12000000, 88200, 0x43},
+	{12000000, 96000, 0x53},
+	{12000000, 176400, 0x83},
+	{12000000, 192000, 0x93},
+	{12288000, 48000, 0x11},
+	{12288000, 96000, 0x51},
+	{12288000, 192000, 0x91},
+	{13000000, 44100, 0x07},
+	{13000000, 48000, 0x17},
+	{13000000, 88200, 0x47},
+	{13000000, 96000, 0x57},
+	{13000000, 176400, 0x87},
+	{13000000, 192000, 0x97},
+	{22579200, 44100, 0x02},
+	{22579200, 88200, 0x42},
+	{22579200, 176400, 0x82},
+	{24000000, 44100, 0x0B},
+	{24000000, 48000, 0x1B},
+	{24000000, 88200, 0x4B},
+	{24000000, 96000, 0x5B},
+	{24000000, 176400, 0x8B},
+	{24000000, 192000, 0x9B},
+	{24576000, 48000, 0x12},
+	{24576000, 96000, 0x52},
+	{24576000, 192000, 0x92},
+	{26000000, 44100, 0x0F},
+	{26000000, 48000, 0x1F},
+	{26000000, 88200, 0x4F},
+	{26000000, 96000, 0x5F},
+	{26000000, 176400, 0x8F},
+	{26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+		if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+			cs35l35_clk_ctl[i].srate == srate)
+			return cs35l35_clk_ctl[i].clk_cfg;
+	}
+	return -EINVAL;
+}
+
+static int cs35l35_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+	int srate = params_rate(params);
+	int ret = 0;
+	u8 sp_sclks;
+	int audin_format;
+	int errata_chk;
+
+	int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+	if (clk_ctl < 0) {
+		dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
+			cs35l35->sysclk, srate);
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+			  CS35L35_CLK_CTL2_MASK, clk_ctl);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set port config %d\n", ret);
+		return ret;
+	}
+
+	/* Rev A0 Errata
+	 *
+	 * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+	 * the Class H algorithm does not enable weak-drive operation for
+	 * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+	 *
+	 */
+	errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
+
+	if (classh->classh_wk_fet_disable == 0x00 &&
+		(errata_chk == 0x01 || errata_chk == 0x03)) {
+		ret = regmap_update_bits(cs35l35->regmap,
+			CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+			0 << CS35L35_CH_WKFET_DEL_SHIFT);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to set fet config %d\n",
+				ret);
+			return ret;
+		}
+	}
+
+/*
+ * You can pull more Monitor data from the SDOUT pin than going to SDIN
+ * Just make sure your SCLK is fast enough to fill the frame
+ */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		switch (params_width(params)) {
+		case 8:
+			audin_format = CS35L35_SDIN_DEPTH_8;
+			break;
+		case 16:
+			audin_format = CS35L35_SDIN_DEPTH_16;
+			break;
+		case 24:
+			audin_format = CS35L35_SDIN_DEPTH_24;
+			break;
+		default:
+			dev_err(codec->dev, "Unsupported Width %d\n",
+				params_width(params));
+		}
+		regmap_update_bits(cs35l35->regmap,
+			CS35L35_AUDIN_DEPTH_CTL, CS35L35_AUDIN_DEPTH_MASK,
+			audin_format << CS35L35_AUDIN_DEPTH_SHIFT);
+		if (cs35l35->pdata.stereo) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_AUDIN_DEPTH_CTL, CS35L35_ADVIN_DEPTH_MASK,
+				audin_format << CS35L35_ADVIN_DEPTH_SHIFT);
+		}
+	}
+/* We have to take the SCLK to derive num sclks
+ * to configure the CLOCK_CTL3 register correctly
+ */
+	if ((cs35l35->sclk / srate) % 4) {
+		dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
+					cs35l35->sclk, srate);
+		return -EINVAL;
+	}
+	sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+	if (cs35l35->i2s_mode) {
+		/* Only certain ratios are supported in I2S Slave Mode */
+		if (cs35l35->slave_mode) {
+			switch (sp_sclks) {
+			case CS35L35_SP_SCLKS_32FS:
+			case CS35L35_SP_SCLKS_48FS:
+			case CS35L35_SP_SCLKS_64FS:
+			break;
+			default:
+				dev_err(codec->dev, "ratio not supported\n");
+				return -EINVAL;
+			};
+		} else {
+			/* Only certain ratios supported in I2S MASTER Mode */
+			switch (sp_sclks) {
+			case CS35L35_SP_SCLKS_32FS:
+			case CS35L35_SP_SCLKS_64FS:
+			break;
+			default:
+				dev_err(codec->dev, "ratio not supported\n");
+				return -EINVAL;
+			};
+		}
+		ret = regmap_update_bits(cs35l35->regmap,
+			CS35L35_CLK_CTL3, CS35L35_SP_SCLKS_MASK,
+			sp_sclks << CS35L35_SP_SCLKS_SHIFT);
+		if (ret != 0) {
+			dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
+			return ret;
+		}
+	}
+	if (cs35l35->pdm_mode) {
+		regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+			CS35L35_PDM_MODE_MASK, 1 << CS35L35_PDM_MODE_SHIFT);
+	} else {
+		regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+			CS35L35_PDM_MODE_MASK, 0 << CS35L35_PDM_MODE_SHIFT);
+	}
+	return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+	44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+	.count  = ARRAY_SIZE(cs35l35_src_rates),
+	.list   = cs35l35_src_rates,
+};
+
+static int cs35l35_pcm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints);
+	return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+	44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+	.count  = ARRAY_SIZE(cs35l35_pdm_rates),
+	.list   = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&cs35l35_pdm_constraints);
+	return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+				int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+	/* Need the SCLK Frequency */
+	cs35l35->sclk = freq;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+	.startup = cs35l35_pcm_startup,
+	.set_fmt = cs35l35_set_dai_fmt,
+	.hw_params = cs35l35_pcm_hw_params,
+	.set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+	.startup = cs35l35_pdm_startup,
+	.set_fmt = cs35l35_set_dai_fmt,
+	.hw_params = cs35l35_pcm_hw_params,
+	.set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+	{
+		.name = "cs35l35-pcm",
+		.id = 0,
+		.playback = {
+			.stream_name = "AMP Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AMP Capture",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.ops = &cs35l35_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "cs35l35-pdm",
+		.id = 1,
+		.playback = {
+			.stream_name = "PDM Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_KNOT,
+			.formats = CS35L35_FORMATS,
+		},
+		.ops = &cs35l35_pdm_ops,
+	},
+};
+
+static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
+				int clk_id, int source, unsigned int freq,
+				int dir)
+{
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	int clksrc;
+	int ret = 0;
+
+	switch (clk_id) {
+	case 0:
+		clksrc = CS35L35_CLK_SOURCE_MCLK;
+		break;
+	case 1:
+		clksrc = CS35L35_CLK_SOURCE_SCLK;
+		break;
+	case 2:
+		clksrc = CS35L35_CLK_SOURCE_PDM;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid CLK Source\n");
+		return -EINVAL;
+	};
+
+	switch (freq) {
+	case 5644800:
+	case 6144000:
+	case 11289600:
+	case 12000000:
+	case 12288000:
+	case 13000000:
+	case 22579200:
+	case 24000000:
+	case 24576000:
+	case 26000000:
+		cs35l35->sysclk = freq;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid CLK Frequency\n");
+		return -EINVAL;
+	}
+
+	ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+		CS35L35_CLK_SOURCE_MASK, clksrc << CS35L35_CLK_SOURCE_SHIFT);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set sysclk %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int cs35l35_codec_probe(struct snd_soc_codec *codec)
+{
+	struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+	struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+	struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+	int ret;
+
+	/* Set Platform Data */
+	if (cs35l35->pdata.bst_vctl)
+		regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+			CS35L35_BST_CTL_MASK, cs35l35->pdata.bst_vctl);
+
+	if (cs35l35->pdata.bst_ipk)
+		regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+			CS35L35_BST_IPK_MASK,
+			cs35l35->pdata.bst_ipk << CS35L35_BST_IPK_SHIFT);
+
+	if (cs35l35->pdata.gain_zc)
+		regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+			CS35L35_AMP_GAIN_ZC_MASK,
+			cs35l35->pdata.gain_zc << CS35L35_AMP_GAIN_ZC_SHIFT);
+
+	if (cs35l35->pdata.aud_channel)
+		regmap_update_bits(cs35l35->regmap,
+			CS35L35_AUDIN_RXLOC_CTL,
+			CS35L35_AUD_IN_LR_MASK,
+			cs35l35->pdata.aud_channel << CS35L35_AUD_IN_LR_SHIFT);
+
+	if (cs35l35->pdata.stereo) {
+		regmap_update_bits(cs35l35->regmap,
+			CS35L35_ADVIN_RXLOC_CTL, CS35L35_ADV_IN_LR_MASK,
+			cs35l35->pdata.adv_channel << CS35L35_ADV_IN_LR_SHIFT);
+		if (cs35l35->pdata.shared_bst)
+			regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+				CS35L35_CH_STEREO_MASK, 1 << CS35L35_CH_STEREO_SHIFT);
+		ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
+					ARRAY_SIZE(cs35l35_adv_controls));
+		if (ret)
+			return ret;
+	}
+
+	if (cs35l35->pdata.sp_drv_str)
+		regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+			CS35L35_SP_DRV_MASK,
+			cs35l35->pdata.sp_drv_str << CS35L35_SP_DRV_SHIFT);
+
+	if (classh->classh_algo_enable) {
+		if (classh->classh_bst_override)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_CTL, CS35L35_CH_BST_OVR_MASK,
+				classh->classh_bst_override << CS35L35_CH_BST_OVR_SHIFT);
+		if (classh->classh_bst_max_limit)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_CTL, CS35L35_CH_BST_LIM_MASK,
+				classh->classh_bst_max_limit << CS35L35_CH_BST_LIM_SHIFT);
+		if (classh->classh_mem_depth)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_CTL, CS35L35_CH_MEM_DEPTH_MASK,
+				classh->classh_mem_depth << CS35L35_CH_MEM_DEPTH_SHIFT);
+		if (classh->classh_headroom)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_HEADRM_CTL, CS35L35_CH_HDRM_CTL_MASK,
+				classh->classh_headroom << CS35L35_CH_HDRM_CTL_SHIFT);
+		if (classh->classh_release_rate)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_RELEASE_RATE, CS35L35_CH_REL_RATE_MASK,
+				classh->classh_release_rate << CS35L35_CH_REL_RATE_SHIFT);
+		if (classh->classh_wk_fet_disable)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DIS_MASK,
+				classh->classh_wk_fet_disable << CS35L35_CH_WKFET_DIS_SHIFT);
+		if (classh->classh_wk_fet_delay)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_DEL_MASK,
+				classh->classh_wk_fet_delay << CS35L35_CH_WKFET_DEL_SHIFT);
+		if (classh->classh_wk_fet_thld)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_FET_DRIVE_CTL, CS35L35_CH_WKFET_THLD_MASK,
+				classh->classh_wk_fet_thld << CS35L35_CH_WKFET_THLD_SHIFT);
+		if (classh->classh_vpch_auto)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_AUTO_MASK,
+				classh->classh_vpch_auto << CS35L35_CH_VP_AUTO_SHIFT);
+		if (classh->classh_vpch_rate)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_RATE_MASK,
+				classh->classh_vpch_rate << CS35L35_CH_VP_RATE_SHIFT);
+		if (classh->classh_vpch_man)
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_CLASS_H_VP_CTL, CS35L35_CH_VP_MAN_MASK,
+				classh->classh_vpch_man << CS35L35_CH_VP_MAN_SHIFT);
+	}
+
+	if (monitor_config->is_present) {
+		if (monitor_config->vmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SPKMON_DEPTH_CTL, CS35L35_VMON_DEPTH_MASK,
+				monitor_config->vmon_dpth << CS35L35_VMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vmon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vmon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->imon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SPKMON_DEPTH_CTL, CS35L35_IMON_DEPTH_MASK,
+				monitor_config->imon_dpth << CS35L35_IMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_IMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->imon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_IMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->imon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vpmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPMON_DEPTH_MASK,
+				monitor_config->vpmon_dpth << CS35L35_VPMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vpmon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vpmon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vbstmon_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_VBSTMON_DEPTH_MASK,
+				monitor_config->vpmon_dpth << CS35L35_VBSTMON_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vbstmon_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VBSTMON_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vbstmon_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->vpbrstat_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_VPBRSTAT_DEPTH_MASK,
+				monitor_config->vpbrstat_dpth << CS35L35_VPBRSTAT_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->vpbrstat_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_VPBR_STATUS_TXLOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->vpbrstat_frm << CS35L35_MON_FRM_SHIFT);
+		}
+		if (monitor_config->zerofill_specs) {
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_SUPMON_DEPTH_CTL, CS35L35_ZEROFILL_DEPTH_MASK,
+				monitor_config->zerofill_dpth << CS35L35_ZEROFILL_DEPTH_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_TXLOC_MASK,
+				monitor_config->zerofill_loc << CS35L35_MON_TXLOC_SHIFT);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_ZERO_FILL_LOC_CTL, CS35L35_MON_FRM_MASK,
+				monitor_config->zerofill_frm << CS35L35_MON_FRM_SHIFT);
+		}
+	}
+
+	return ret;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
+	.probe = cs35l35_codec_probe,
+	.set_sysclk = cs35l35_codec_set_sysclk,
+	.component_driver = {
+		.controls = cs35l35_aud_controls,
+		.num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+		.dapm_widgets = cs35l35_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+
+		.dapm_routes = cs35l35_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+	},
+};
+
+static struct regmap_config cs35l35_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = CS35L35_MAX_REGISTER,
+	.reg_defaults = cs35l35_reg,
+	.num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+	.volatile_reg = cs35l35_volatile_register,
+	.readable_reg = cs35l35_readable_register,
+	.precious_reg = cs35l35_precious_register,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+	struct cs35l35_private *cs35l35 = data;
+	unsigned int sticky1, sticky2, sticky3, sticky4;
+	unsigned int mask1, mask2, mask3, mask4, current1;
+
+	/* ack the irq by reading all status registers */
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+	regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+	/* Check to see if unmasked bits are active */
+	if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+			&& !(sticky4 & ~mask4))
+		return IRQ_NONE;
+
+	if (sticky2 & CS35L35_PDN_DONE)
+		complete(&cs35l35->pdn_done);
+
+	/* read the current values */
+	regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &current1);
+
+	/* handle the interrupts */
+	if (sticky1 & CS35L35_CAL_ERR) {
+		pr_err("%s : Calibration Error\n", __func__);
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_CAL_ERR)) {
+			pr_debug("%s : Cal error release\n", __func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS,
+				CS35L35_CAL_ERR_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_CAL_ERR_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_AMP_SHORT) {
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_AMP_SHORT)) {
+			pr_debug("%s :Amp short error release\n", __func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS,
+				CS35L35_SHORT_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_SHORT_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_OTW) {
+		pr_err("%s : Over temperature warning\n", __func__);
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_OTW)) {
+			pr_debug("%s : Over temperature warning release\n",
+				__func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS,
+				CS35L35_OTW_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTW_RLS, 0);
+		}
+	}
+
+	if (sticky1 & CS35L35_OTE) {
+		pr_crit("%s : Over temperature error\n", __func__);
+
+		/* error is no longer asserted; safe to reset */
+		if (!(current1 & CS35L35_OTE)) {
+			pr_debug("%s : Over temperature error release\n",
+				__func__);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS,
+				CS35L35_OTE_RLS);
+			regmap_update_bits(cs35l35->regmap,
+				CS35L35_PROT_RELEASE_CTL, CS35L35_OTE_RLS, 0);
+		}
+	}
+
+	if (sticky3 & CS35L35_BST_HIGH) {
+		pr_crit("%s : VBST error: powering off!\n", __func__);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+	}
+
+	if (sticky3 & CS35L35_LBST_SHORT) {
+		pr_crit("%s : LBST error: powering off!\n", __func__);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+			CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+	}
+
+	if (sticky2 & CS35L35_VPBR_ERR)
+		pr_err("%s : Error: Reactive Brownout\n", __func__);
+
+	if (sticky4 & CS35L35_VMON_OVFL)
+		pr_err("%s : Error: VMON overflow\n", __func__);
+
+	if (sticky4 & CS35L35_IMON_OVFL)
+		pr_err("%s : Error: IMON overflow\n", __func__);
+
+	return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+				struct cs35l35_platform_data *pdata)
+{
+	struct device_node *np = i2c_client->dev.of_node;
+	struct device_node *classh, *signal_format;
+	struct classh_cfg *classh_config = &pdata->classh_algo;
+	struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+	unsigned int val32 = 0;
+	u8 monitor_array[3];
+	int ret = 0;
+
+	if (!np)
+		return 0;
+
+	pdata->bst_pdn_fet_on = of_property_read_bool(np,
+					"cirrus,boost-pdn-fet-on");
+
+	if (of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32) >= 0)
+		pdata->bst_vctl = val32;
+
+	if (of_property_read_u32(np, "cirrus,boost-ipk-milliamp", &val32) >= 0)
+		pdata->bst_ipk = val32;
+
+	if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+		pdata->sp_drv_str = val32;
+
+	pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+	if (pdata->stereo) {
+		if (of_property_read_u32(np, "cirrus,audio-channel", &val32) >= 0)
+			pdata->aud_channel = val32;
+		if (of_property_read_u32(np, "cirrus,advisory-channel",
+					&val32) >= 0)
+			pdata->adv_channel = val32;
+		pdata->shared_bst = of_property_read_bool(np,
+						"cirrus,shared-boost");
+	}
+
+	pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+	classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+	classh_config->classh_algo_enable = classh ? true : false;
+
+	if (classh_config->classh_algo_enable) {
+		classh_config->classh_bst_override =
+			of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+		if (of_property_read_u32(classh, "cirrus,classh-bst-max-limit",
+					&val32) >= 0)
+			classh_config->classh_bst_max_limit = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-mem-depth",
+					&val32) >= 0)
+			classh_config->classh_mem_depth = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-release-rate",
+					&val32) >= 0)
+			classh_config->classh_release_rate = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-headroom",
+					&val32) >= 0)
+			classh_config->classh_headroom = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-wk-fet-disable",
+					&val32) >= 0)
+			classh_config->classh_wk_fet_disable = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+					&val32) >= 0)
+			classh_config->classh_wk_fet_delay = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+					&val32) >= 0)
+			classh_config->classh_wk_fet_thld = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+					&val32) >= 0)
+			classh_config->classh_vpch_auto = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+					&val32) >= 0)
+			classh_config->classh_vpch_rate = val32;
+		if (of_property_read_u32(classh, "cirrus,classh-vpch-man",
+					&val32) >= 0)
+			classh_config->classh_vpch_man = val32;
+	}
+	of_node_put(classh);
+
+	/* frame depth location */
+	signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+	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));
+		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];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vmon_specs = true;
+			monitor_config->vmon_dpth = monitor_array[0];
+			monitor_config->vmon_loc = monitor_array[1];
+			monitor_config->vmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vpmon_specs = true;
+			monitor_config->vpmon_dpth = monitor_array[0];
+			monitor_config->vpmon_loc = monitor_array[1];
+			monitor_config->vpmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vbstmon_specs = true;
+			monitor_config->vbstmon_dpth = monitor_array[0];
+			monitor_config->vbstmon_loc = monitor_array[1];
+			monitor_config->vbstmon_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->vpbrstat_specs = true;
+			monitor_config->vpbrstat_dpth = monitor_array[0];
+			monitor_config->vpbrstat_loc = monitor_array[1];
+			monitor_config->vpbrstat_frm = monitor_array[2];
+		}
+		ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+				   monitor_array, ARRAY_SIZE(monitor_array));
+		if (!ret) {
+			monitor_config->zerofill_specs = true;
+			monitor_config->zerofill_dpth = monitor_array[0];
+			monitor_config->zerofill_loc = monitor_array[1];
+			monitor_config->zerofill_frm = monitor_array[2];
+		}
+	}
+	of_node_put(signal_format);
+
+	return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+	{ 0x7F, 0x99 },
+	{ 0x00, 0x99 },
+	{ 0x52, 0x22 },
+	{ 0x04, 0x14 },
+	{ 0x6D, 0x44 },
+	{ 0x24, 0x10 },
+	{ 0x58, 0xC4 },
+	{ 0x00, 0x98 },
+	{ 0x18, 0x08 },
+	{ 0x00, 0x00 },
+	{ 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
+			      const struct i2c_device_id *id)
+{
+	struct cs35l35_private *cs35l35;
+	struct cs35l35_platform_data *pdata =
+		dev_get_platdata(&i2c_client->dev);
+	int i;
+	int ret;
+	unsigned int devid = 0;
+	unsigned int reg;
+
+	cs35l35 = devm_kzalloc(&i2c_client->dev,
+			       sizeof(struct cs35l35_private),
+			       GFP_KERNEL);
+	if (!cs35l35) {
+		dev_err(&i2c_client->dev, "could not allocate codec\n");
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(i2c_client, cs35l35);
+	cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+	if (IS_ERR(cs35l35->regmap)) {
+		ret = PTR_ERR(cs35l35->regmap);
+		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+		cs35l35->supplies[i].supply = cs35l35_supplies[i];
+		cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+	ret = devm_regulator_bulk_get(&i2c_client->dev,
+			cs35l35->num_supplies,
+			cs35l35->supplies);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev,
+			"Failed to request core supplies: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (pdata) {
+		cs35l35->pdata = *pdata;
+	} else {
+		pdata = devm_kzalloc(&i2c_client->dev,
+				     sizeof(struct cs35l35_platform_data),
+				GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&i2c_client->dev,
+				"could not allocate pdata\n");
+			return -ENOMEM;
+		}
+		if (i2c_client->dev.of_node) {
+			ret = cs35l35_handle_of_data(i2c_client, pdata);
+			if (ret != 0)
+				return ret;
+
+		}
+		cs35l35->pdata = *pdata;
+	}
+
+	ret = regulator_bulk_enable(cs35l35->num_supplies,
+					cs35l35->supplies);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev,
+			"Failed to enable core supplies: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* returning NULL can be an option if in stereo mode */
+	cs35l35->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+		"reset", GPIOD_OUT_LOW);
+	if (IS_ERR(cs35l35->reset_gpio))
+		return PTR_ERR(cs35l35->reset_gpio);
+
+	if (cs35l35->reset_gpio)
+		gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+
+	init_completion(&cs35l35->pdn_done);
+
+	ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+				    ARRAY_SIZE(cs35l35_errata_patch));
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Failed to apply errata patch\n");
+		return ret;
+	}
+
+	ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+			cs35l35_irq, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+			"cs35l35", cs35l35);
+	if (ret != 0) {
+		dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+		goto err;
+	}
+	/* initialize codec */
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, &reg);
+
+	devid = (reg & 0xFF) << 12;
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, &reg);
+	devid |= (reg & 0xFF) << 4;
+	ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, &reg);
+	devid |= (reg & 0xF0) >> 4;
+
+	if (devid != CS35L35_CHIP_ID) {
+		dev_err(&i2c_client->dev,
+			"CS35L35 Device ID (%X). Expected ID %X\n",
+			devid, CS35L35_CHIP_ID);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, &reg);
+	if (ret < 0) {
+		dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+		goto err;
+	}
+
+	dev_info(&i2c_client->dev,
+		 "Cirrus Logic CS35L35 (%x), Revision: %02X\n", devid,
+		ret & 0xFF);
+
+	/* Set the INT Masks for critical errors */
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1, CS35L35_INT1_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2, CS35L35_INT2_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3, CS35L35_INT3_CRIT_MASK);
+	regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4, CS35L35_INT4_CRIT_MASK);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+		CS35L35_PWR2_PDN_MASK, CS35L35_PWR2_PDN_MASK);
+
+	if (cs35l35->pdata.bst_pdn_fet_on)
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETON_SHIFT);
+	else
+		regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+			CS35L35_PDN_BST_MASK, 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+		CS35L35_PWR3_PDN_MASK, CS35L35_PWR3_PDN_MASK);
+
+	regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+		CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+	ret =  snd_soc_register_codec(&i2c_client->dev,
+			&soc_codec_dev_cs35l35, cs35l35_dai,
+			ARRAY_SIZE(cs35l35_dai));
+	if (ret < 0) {
+		dev_err(&i2c_client->dev,
+			"%s: Register codec failed\n", __func__);
+		goto err;
+	}
+
+err:
+	regulator_bulk_disable(cs35l35->num_supplies,
+			       cs35l35->supplies);
+	return ret;
+}
+
+static int cs35l35_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+	{.compatible = "cirrus,cs35l35"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+	{"cs35l35", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+	.driver = {
+		.name = "cs35l35",
+		.of_match_table = cs35l35_of_match,
+	},
+	.id_table = cs35l35_id,
+	.probe = cs35l35_i2c_probe,
+	.remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Li Xu, Cirrus Logic Inc, <li.xu@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 0000000..fb02785
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,284 @@
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG		0x01
+#define CS35L35_LASTREG			0x7E
+#define CS35L35_CHIP_ID			0x00035A35
+#define CS35L35_DEVID_AB		0x01	/* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD		0x02    /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E			0x03    /* Device ID E [RO] */
+#define CS35L35_FAB_ID			0x04	/* Fab ID [RO] */
+#define CS35L35_REV_ID			0x05	/* Revision ID [RO] */
+#define CS35L35_PWRCTL1			0x06    /* Power Ctl 1 */
+#define CS35L35_PWRCTL2			0x07    /* Power Ctl 2 */
+#define CS35L35_PWRCTL3			0x08	/* Power Ctl 3 */
+#define CS35L35_CLK_CTL1		0x0A	/* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2		0x0B	/* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3		0x0C	/* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1		0x0D	/* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2		0x0E	/* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3		0x0F	/* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL		0x13	/* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL		0x14	/* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL		0x15	/* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL		0x16	/* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL		0x17	/* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL		0x18	/* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL	0x19	/* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL	0x1A	/* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL	0x1B	/* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL			0x1C	/* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL		0x1D	/* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I		0x1E	/* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL		0x20	/* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1		0x21	/* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2		0x22	/* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP	0x23	/* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ	0x24	/* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL		0x30	/* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL	0x31	/* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE	0x32	/* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL	0x33	/* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL		0x34	/* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS		0x38	/* CLS H Status */
+#define CS35L35_VPBR_CTL		0x3A	/* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL		0x3B	/* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL		0x3C	/* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL	0x3D	/* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS	0x4B	/* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL		0x4E	/* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL		0x51	/* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL		0x52	/* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL		0x53	/* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL		0x54	/* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL		0x55	/* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL		0x56	/* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL	0x57	/* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL	0x58	/* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL	0x59	/* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL		0x5A	/* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL	0x5B	/* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL	0x5C	/* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL	0x5D	/* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1		0x62	/* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2		0x63	/* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL	0x64	/* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK	0x68	/* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1		0x69	/* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2		0x6A	/* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1		0x70	/* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2		0x71	/* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3		0x72	/* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4		0x73	/* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1		0x74	/* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2		0x75	/* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3		0x76	/* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4		0x77	/* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS		0x78	/* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS		0x7E	/* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER		0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST			0x80
+#define CS35L35_DISCHG_FLT		0x02
+#define CS35L35_PDN_ALL			0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON		0x80
+#define CS35L35_PDN_IMON		0x40
+#define CS35L35_PDN_CLASSH		0x20
+#define CS35L35_PDN_VPBR		0x10
+#define CS35L35_PDN_BST			0x04
+#define CS35L35_PDN_AMP			0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT		0x10
+#define CS35L35_PDN_VMON_OUT		0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK	0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT	0
+#define CS35L35_ADVIN_DEPTH_MASK	0x12
+#define CS35L35_ADVIN_DEPTH_SHIFT	2
+#define CS35L35_SDIN_DEPTH_8		0x01
+#define CS35L35_SDIN_DEPTH_16		0x02
+#define CS35L35_SDIN_DEPTH_24		0x03
+
+#define CS35L35_SDOUT_DEPTH_8		0x01
+#define CS35L35_SDOUT_DEPTH_12		0x02
+#define CS35L35_SDOUT_DEPTH_16		0x03
+
+#define CS35L35_AUD_IN_LR_MASK		0x80
+#define CS35L35_AUD_IN_LR_SHIFT		7
+#define CS35L35_ADV_IN_LR_MASK		0x80
+#define CS35L35_ADV_IN_LR_SHIFT		7
+#define CS35L35_AUD_IN_LOC_MASK		0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT	0
+#define CS35L35_ADV_IN_LOC_MASK		0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT	0
+
+#define CS35L35_IMON_DEPTH_MASK		0x03
+#define CS35L35_IMON_DEPTH_SHIFT	0
+#define CS35L35_VMON_DEPTH_MASK		0x0C
+#define CS35L35_VMON_DEPTH_SHIFT	2
+#define CS35L35_VBSTMON_DEPTH_MASK	0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT	0
+#define CS35L35_VPMON_DEPTH_MASK	0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT	2
+#define CS35L35_VPBRSTAT_DEPTH_MASK	0x18
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT	4
+#define CS35L35_ZEROFILL_DEPTH_MASK	0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT	0x00
+
+#define CS35L35_MON_TXLOC_MASK		0x3F
+#define CS35L35_MON_TXLOC_SHIFT		0
+#define CS35L35_MON_FRM_MASK		0x80
+#define CS35L35_MON_FRM_SHIFT		7
+
+#define CS35L35_MS_MASK			0x80
+#define CS35L35_MS_SHIFT		7
+#define CS35L35_SPMODE_MASK		0x40
+#define CS35L35_SP_DRV_MASK		0x10
+#define CS35L35_SP_DRV_SHIFT		4
+#define CS35L35_CLK_CTL2_MASK		0xFF
+#define CS35L35_PDM_MODE_MASK		0x40
+#define CS35L35_PDM_MODE_SHIFT		6
+#define CS35L35_CLK_SOURCE_MASK		0x03
+#define CS35L35_CLK_SOURCE_SHIFT	0
+#define CS35L35_CLK_SOURCE_MCLK		0
+#define CS35L35_CLK_SOURCE_SCLK		1
+#define CS35L35_CLK_SOURCE_PDM		2
+
+#define CS35L35_SP_SCLKS_MASK		0x0F
+#define CS35L35_SP_SCLKS_SHIFT		0x00
+#define CS35L35_SP_SCLKS_16FS		0x03
+#define CS35L35_SP_SCLKS_32FS		0x07
+#define CS35L35_SP_SCLKS_48FS		0x0B
+#define CS35L35_SP_SCLKS_64FS		0x0F
+#define CS35L35_SP_RATE_MASK		0xC0
+
+#define CS35L35_PDN_BST_MASK		0x06
+#define CS35L35_PDN_BST_FETON_SHIFT	1
+#define CS35L35_PDN_BST_FETOFF_SHIFT	2
+#define CS35L35_PWR2_PDN_MASK		0xE0
+#define CS35L35_PWR3_PDN_MASK		0x1E
+#define CS35L35_PDN_ALL_MASK		0x01
+#define CS35L35_DISCHG_FILT_MASK	0x02
+#define CS35L35_DISCHG_FILT_SHIFT	1
+#define CS35L35_MCLK_DIS_MASK		0x04
+#define CS35L35_MCLK_DIS_SHIFT		2
+
+#define CS35L35_BST_CTL_MASK		0x7F
+#define CS35L35_BST_CTL_SHIFT		0
+#define CS35L35_BST_IPK_MASK		0x1F
+#define CS35L35_BST_IPK_SHIFT		0
+#define CS35L35_AMP_MUTE_MASK		0x20
+#define CS35L35_AMP_MUTE_SHIFT		5
+#define CS35L35_AMP_GAIN_ZC_MASK	0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT	4
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK		0x40
+#define CS35L35_CH_STEREO_SHIFT		6
+#define CS35L35_CH_BST_OVR_MASK		0x04
+#define CS35L35_CH_BST_OVR_SHIFT	2
+#define CS35L35_CH_BST_LIM_MASK		0x08
+#define CS35L35_CH_BST_LIM_SHIFT	3
+#define CS35L35_CH_MEM_DEPTH_MASK	0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT	0
+#define CS35L35_CH_HDRM_CTL_MASK	0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT	0
+#define CS35L35_CH_REL_RATE_MASK	0xFF
+#define CS35L35_CH_REL_RATE_SHIFT	0
+#define CS35L35_CH_WKFET_DIS_MASK	0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT	7
+#define CS35L35_CH_WKFET_DEL_MASK	0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT	4
+#define CS35L35_CH_WKFET_THLD_MASK	0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT	0
+#define CS35L35_CH_VP_AUTO_MASK		0x80
+#define CS35L35_CH_VP_AUTO_SHIFT	7
+#define CS35L35_CH_VP_RATE_MASK		0x60
+#define CS35L35_CH_VP_RATE_SHIFT	5
+#define CS35L35_CH_VP_MAN_MASK		0x1F
+#define CS35L35_CH_VP_MAN_SHIFT		0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS		0x80
+#define CS35L35_SHORT_RLS		0x04
+#define CS35L35_OTW_RLS			0x02
+#define CS35L35_OTE_RLS			0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK		0x38
+#define CS35L35_INT2_CRIT_MASK		0xEF
+#define CS35L35_INT3_CRIT_MASK		0xEE
+#define CS35L35_INT4_CRIT_MASK		0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT	4
+#define CS35L35_M_PDN_DONE_MASK		0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR			0x80
+#define CS35L35_OTP_ERR			0x40
+#define CS35L35_LRCLK_ERR		0x20
+#define CS35L35_SPCLK_ERR		0x10
+#define CS35L35_MCLK_ERR		0x08
+#define CS35L35_AMP_SHORT		0x04
+#define CS35L35_OTW			0x02
+#define CS35L35_OTE			0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE		0x10
+#define CS35L35_VPBR_ERR		0x02
+#define CS35L35_VPBR_CLR		0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH		0x10
+#define CS35L35_BST_HIGH_FLAG		0x08
+#define CS35L35_BST_IPK_FLAG		0x04
+#define CS35L35_LBST_SHORT		0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL		0x08
+#define CS35L35_IMON_OVFL		0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct  cs35l35_private {
+	struct snd_soc_codec *codec;
+	struct cs35l35_platform_data pdata;
+	struct regmap *regmap;
+	struct regulator_bulk_data supplies[2];
+	int num_supplies;
+	int sysclk;
+	int sclk;
+	bool pdm_mode;
+	bool i2s_mode;
+	bool slave_mode;
+	/* GPIO for /RST */
+	struct gpio_desc *reset_gpio;
+	struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+	"VA",
+	"VP",
+};
+
+#endif
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH v6 3/8] PWM: add pwm-stm32 DT bindings
From: Rob Herring @ 2016-12-13 15:57 UTC (permalink / raw)
  To: Lee Jones
  Cc: Mark Rutland, devicetree@vger.kernel.org,
	linaro-kernel@lists.linaro.org, Lars-Peter Clausen,
	Alexandre Torgue, Linux PWM List, linux-iio@vger.kernel.org,
	Peter Meerwald, Arnaud POULIQUEN, linux-kernel@vger.kernel.org,
	Thierry Reding, linux-arm-kernel@lists.infradead.org,
	Benjamin Gaignard, Hartmut Knaack, Gerald Baeza, Fabrice Gasnier,
	Linus Walleij, Jonathan Cameron
In-Reply-To: <20161213111137.GW3625@dell.home>

On Tue, Dec 13, 2016 at 5:11 AM, Lee Jones <lee.jones@linaro.org> wrote:
> On Mon, 12 Dec 2016, Rob Herring wrote:
>
>> On Fri, Dec 09, 2016 at 03:15:14PM +0100, Benjamin Gaignard wrote:
>> > Define bindings for pwm-stm32
>> >
>> > version 6:
>> > - change st,breakinput parameter format to make it usuable on stm32f7 too.
>> >
>> > version 2:
>> > - use parameters instead of compatible of handle the hardware configuration
>> >
>> > Signed-off-by: Benjamin Gaignard <benjamin.gaignard@st.com>
>> > ---
>> >  .../devicetree/bindings/pwm/pwm-stm32.txt          | 33 ++++++++++++++++++++++
>> >  1 file changed, 33 insertions(+)
>> >  create mode 100644 Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>> >
>> > diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>> > new file mode 100644
>> > index 0000000..866f222
>> > --- /dev/null
>> > +++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt
>> > @@ -0,0 +1,33 @@
>> > +STMicroelectronics STM32 Timers PWM bindings
>> > +
>> > +Must be a sub-node of an STM32 Timers device tree node.
>> > +See ../mfd/stm32-timers.txt for details about the parent node.
>> > +
>> > +Required parameters:
>> > +- compatible:              Must be "st,stm32-pwm".
>> > +- pinctrl-names:   Set to "default".
>> > +- pinctrl-0:               List of phandles pointing to pin configuration nodes for PWM module.
>> > +                   For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt
>> > +
>> > +Optional parameters:
>> > +- st,breakinput:   Arrays of three u32 <index level filter> to describe break input configurations.
>> > +                   "index" indicates on which break input the configuration should be applied.
>> > +                   "level" gives the active level (0=low or 1=high) for this configuration.
>> > +                   "filter" gives the filtering value to be applied.
>> > +
>> > +Example:
>> > +   timers@40010000 {
>>
>> timer@...
>
> No, it should be timers.

Read the spec. "timer" is a generic node name. "timers" is not. How
many is not relevant.

> The 's' is intentional, since this parent (MFD) device houses 3
> different types of timers.  The "timer" node is a child of this one.

^ permalink raw reply

* Re: [PATCH 3/6] ARM: dts: sun8i: add a cpu0 label to cpu@0 node on A23/33
From: Maxime Ripard @ 2016-12-13 15:45 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: devicetree, Quentin Schulz, Michael Turquette, Stephen Boyd,
	Russell King, linux-kernel, Hans de Goede, Chen-Yu Tsai,
	linux-clk, linux-arm-kernel, Jorik Jonker
In-Reply-To: <20161213152252.53749-4-icenowy@aosc.xyz>


[-- Attachment #1.1: Type: text/plain, Size: 388 bytes --]

On Tue, Dec 13, 2016 at 11:22:49PM +0800, Icenowy Zheng wrote:
> A "cpu0" label is needed on cpu@0 for cpufreq-dt to work.
> 
> Add such a label, in order to prepare for cpufreq support of A23/33.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>

Applied, thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 2/6] clk: sunxi-ng: set the parent rate when adjustin CPUX clock on A33
From: Maxime Ripard @ 2016-12-13 15:44 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: devicetree, Quentin Schulz, Michael Turquette, Stephen Boyd,
	Russell King, linux-kernel, Hans de Goede, Chen-Yu Tsai,
	linux-clk, linux-arm-kernel, Jorik Jonker
In-Reply-To: <20161213152252.53749-3-icenowy@aosc.xyz>


[-- Attachment #1.1: Type: text/plain, Size: 481 bytes --]

On Tue, Dec 13, 2016 at 11:22:48PM +0800, Icenowy Zheng wrote:
> The CPUX clock on A33, which is for the Cortex-A7 cores, is designed to
> be changeable by changing the rate of PLL_CPUX.
> 
> Add CLK_SET_RATE_PARENT flag to this clock.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>

Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 1/6] clk: sunxi-ng: fix PLL_CPUX adjusting on A33
From: Maxime Ripard @ 2016-12-13 15:44 UTC (permalink / raw)
  To: Icenowy Zheng
  Cc: devicetree, Quentin Schulz, Michael Turquette, Stephen Boyd,
	Russell King, linux-kernel, Hans de Goede, Chen-Yu Tsai,
	linux-clk, linux-arm-kernel, Jorik Jonker
In-Reply-To: <20161213152252.53749-2-icenowy@aosc.xyz>


[-- Attachment #1.1: Type: text/plain, Size: 462 bytes --]

On Tue, Dec 13, 2016 at 11:22:47PM +0800, Icenowy Zheng wrote:
> When adjusting PLL_CPUX on A33, the PLL is temporarily driven too high,
> and the system hangs.
> 
> Add a notifier to avoid this situation by temporarily switching to a
> known stable 24 MHz oscillator.
> 
> Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>

Applied, thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH 6/6] ARM: dts: sun8i: raise the max voltage of DCDC2 in sun8i reference tablets
From: Icenowy Zheng @ 2016-12-13 15:22 UTC (permalink / raw)
  To: Russell King, Maxime Ripard, Chen-Yu Tsai, Michael Turquette,
	Stephen Boyd, Jorik Jonker, Hans de Goede, Quentin Schulz
  Cc: devicetree, Icenowy Zheng, linux-kernel, linux-arm-kernel,
	linux-clk
In-Reply-To: <20161213152252.53749-1-icenowy@aosc.xyz>

The "extremity_freq" frequency described in the original FEX files uses
a voltage of 1.46v, which is beyond the current maximum voltage value of
DCDC2 (Cortex-A7 supply) in the sun8i reference tablet DTSI file.

Raise the maximum value to 1.46v.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
index 7ac8bb4bc95a..325ca5bd67a5 100644
--- a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
+++ b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
@@ -180,7 +180,7 @@
 &reg_dcdc2 {
 	regulator-always-on;
 	regulator-min-microvolt = <900000>;
-	regulator-max-microvolt = <1400000>;
+	regulator-max-microvolt = <1460000>;
 	regulator-name = "vdd-sys";
 };
 
-- 
2.11.0

^ permalink raw reply related

* [PATCH 5/6] ARM: dts: sun8i: set cpu-supply in reference tablet DTSI
From: Icenowy Zheng @ 2016-12-13 15:22 UTC (permalink / raw)
  To: Russell King, Maxime Ripard, Chen-Yu Tsai, Michael Turquette,
	Stephen Boyd, Jorik Jonker, Hans de Goede, Quentin Schulz
  Cc: devicetree, Icenowy Zheng, linux-kernel, linux-arm-kernel,
	linux-clk
In-Reply-To: <20161213152252.53749-1-icenowy@aosc.xyz>

All reference design A33 tablets uses DCDC2 of AXP223 as the power
supply of the Cortex-A7 cores.

Set the cpu-supply in the DTSI of sun8i reference tablets.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
index 08cd00143635..7ac8bb4bc95a 100644
--- a/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
+++ b/arch/arm/boot/dts/sun8i-reference-design-tablet.dtsi
@@ -213,6 +213,10 @@
 	regulator-name = "vcc-rtc";
 };
 
+&cpu0 {
+	cpu-supply = <&reg_dcdc2>;
+};
+
 &r_uart {
 	pinctrl-names = "default";
 	pinctrl-0 = <&r_uart_pins_a>;
-- 
2.11.0

^ permalink raw reply related

* [PATCH 4/6] ARM: dts: sun8i: add opp-v2 table for A33
From: Icenowy Zheng @ 2016-12-13 15:22 UTC (permalink / raw)
  To: Russell King, Maxime Ripard, Chen-Yu Tsai, Michael Turquette,
	Stephen Boyd, Jorik Jonker, Hans de Goede, Quentin Schulz
  Cc: devicetree, Icenowy Zheng, linux-kernel, linux-arm-kernel,
	linux-clk
In-Reply-To: <20161213152252.53749-1-icenowy@aosc.xyz>

An operating point table is needed for the cpu frequency adjusting to
work.

The operating point table is converted from the common value in
extracted script.fex from many A33 board/tablets.

1.344GHz is set as a turbo-mode operating point, as it's described as
"extremity_freq" in the FEX file. (the "max_freq" is 1.2GHz)

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 arch/arm/boot/dts/sun8i-a33.dtsi | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/arch/arm/boot/dts/sun8i-a33.dtsi b/arch/arm/boot/dts/sun8i-a33.dtsi
index 504996cbee29..035c058324b8 100644
--- a/arch/arm/boot/dts/sun8i-a33.dtsi
+++ b/arch/arm/boot/dts/sun8i-a33.dtsi
@@ -46,7 +46,45 @@
 #include <dt-bindings/dma/sun4i-a10.h>
 
 / {
+	cpu0_opp_table: opp_table0 {
+		compatible = "operating-points-v2";
+		opp-shared;
+
+		opp@648000000 {
+			opp-hz = /bits/ 64 <648000000>;
+			opp-microvolt = <1040000>;
+			clock-latency-ns = <244144>; /* 8 32k periods */
+		};
+		opp@816000000 {
+			opp-hz = /bits/ 64 <816000000>;
+			opp-microvolt = <1100000>;
+			clock-latency-ns = <244144>; /* 8 32k periods */
+		};
+		opp@1008000000 {
+			opp-hz = /bits/ 64 <1008000000>;
+			opp-microvolt = <1200000>;
+			clock-latency-ns = <244144>; /* 8 32k periods */
+		};
+		opp@1200000000 {
+			opp-hz = /bits/ 64 <1200000000>;
+			opp-microvolt = <1320000>;
+			clock-latency-ns = <244144>; /* 8 32k periods */
+		};
+		opp@1344000000 {
+			opp-hz = /bits/ 64 <1344000000>;
+			opp-microvolt = <1460000>;
+			clock-latency-ns = <244144>; /* 8 32k periods */
+			turbo-mode;
+		};
+	};
+
 	cpus {
+		cpu0: cpu@0 {
+			clocks = <&ccu CLK_CPUX>;
+			clock-names = "cpu";
+			operating-points-v2 = <&cpu0_opp_table>;
+		};
+
 		cpu@2 {
 			compatible = "arm,cortex-a7";
 			device_type = "cpu";
-- 
2.11.0

^ permalink raw reply related

* [PATCH 3/6] ARM: dts: sun8i: add a cpu0 label to cpu@0 node on A23/33
From: Icenowy Zheng @ 2016-12-13 15:22 UTC (permalink / raw)
  To: Russell King, Maxime Ripard, Chen-Yu Tsai, Michael Turquette,
	Stephen Boyd, Jorik Jonker, Hans de Goede, Quentin Schulz
  Cc: devicetree, Icenowy Zheng, linux-kernel, linux-arm-kernel,
	linux-clk
In-Reply-To: <20161213152252.53749-1-icenowy@aosc.xyz>

A "cpu0" label is needed on cpu@0 for cpufreq-dt to work.

Add such a label, in order to prepare for cpufreq support of A23/33.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 arch/arm/boot/dts/sun8i-a23-a33.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sun8i-a23-a33.dtsi b/arch/arm/boot/dts/sun8i-a23-a33.dtsi
index 817747f41288..5931cc4d1567 100644
--- a/arch/arm/boot/dts/sun8i-a23-a33.dtsi
+++ b/arch/arm/boot/dts/sun8i-a23-a33.dtsi
@@ -84,7 +84,7 @@
 		#address-cells = <1>;
 		#size-cells = <0>;
 
-		cpu@0 {
+		cpu0: cpu@0 {
 			compatible = "arm,cortex-a7";
 			device_type = "cpu";
 			reg = <0>;
-- 
2.11.0

^ permalink raw reply related

* [PATCH 2/6] clk: sunxi-ng: set the parent rate when adjustin CPUX clock on A33
From: Icenowy Zheng @ 2016-12-13 15:22 UTC (permalink / raw)
  To: Russell King, Maxime Ripard, Chen-Yu Tsai, Michael Turquette,
	Stephen Boyd, Jorik Jonker, Hans de Goede, Quentin Schulz
  Cc: devicetree, Icenowy Zheng, linux-kernel, linux-arm-kernel,
	linux-clk
In-Reply-To: <20161213152252.53749-1-icenowy@aosc.xyz>

The CPUX clock on A33, which is for the Cortex-A7 cores, is designed to
be changeable by changing the rate of PLL_CPUX.

Add CLK_SET_RATE_PARENT flag to this clock.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/clk/sunxi-ng/ccu-sun8i-a33.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
index 0f3e7d2dc19a..0d513d2674cb 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
@@ -170,7 +170,7 @@ static SUNXI_CCU_N_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1",
 static const char * const cpux_parents[] = { "osc32k", "osc24M",
 					     "pll-cpux" , "pll-cpux" };
 static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents,
-		     0x050, 16, 2, CLK_IS_CRITICAL);
+		     0x050, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT);
 
 static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0);
 
-- 
2.11.0

^ permalink raw reply related

* [PATCH 1/6] clk: sunxi-ng: fix PLL_CPUX adjusting on A33
From: Icenowy Zheng @ 2016-12-13 15:22 UTC (permalink / raw)
  To: Russell King, Maxime Ripard, Chen-Yu Tsai, Michael Turquette,
	Stephen Boyd, Jorik Jonker, Hans de Goede, Quentin Schulz
  Cc: devicetree, Icenowy Zheng, linux-kernel, linux-arm-kernel,
	linux-clk
In-Reply-To: <20161213152252.53749-1-icenowy@aosc.xyz>

When adjusting PLL_CPUX on A33, the PLL is temporarily driven too high,
and the system hangs.

Add a notifier to avoid this situation by temporarily switching to a
known stable 24 MHz oscillator.

Signed-off-by: Icenowy Zheng <icenowy@aosc.xyz>
---
 drivers/clk/sunxi-ng/ccu-sun8i-a33.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
index 3cd4190ccd59..0f3e7d2dc19a 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c
@@ -752,6 +752,13 @@ static const struct sunxi_ccu_desc sun8i_a33_ccu_desc = {
 	.num_resets	= ARRAY_SIZE(sun8i_a33_ccu_resets),
 };
 
+static struct ccu_mux_nb sun8i_a33_cpu_nb = {
+	.common		= &cpux_clk.common,
+	.cm		= &cpux_clk.mux,
+	.delay_us	= 1, /* > 8 clock cycles at 24 MHz */
+	.bypass_index	= 1, /* index of 24 MHz oscillator */
+};
+
 static void __init sun8i_a33_ccu_setup(struct device_node *node)
 {
 	void __iomem *reg;
@@ -775,6 +782,9 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node)
 	writel(val, reg + SUN8I_A33_PLL_MIPI_REG);
 
 	sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
+
+	ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
+				  &sun8i_a33_cpu_nb);
 }
 CLK_OF_DECLARE(sun8i_a33_ccu, "allwinner,sun8i-a33-ccu",
 	       sun8i_a33_ccu_setup);
-- 
2.11.0

^ permalink raw reply related

* [PATCH 0/6] Allwinner A33 CPU frequency scaling support
From: Icenowy Zheng @ 2016-12-13 15:22 UTC (permalink / raw)
  To: Russell King, Maxime Ripard, Chen-Yu Tsai, Michael Turquette,
	Stephen Boyd, Jorik Jonker, Hans de Goede, Quentin Schulz
  Cc: devicetree, linux-kernel, linux-arm-kernel, linux-clk

This series of patch adds frequency scaling support to Allwinner A33 SoC.

The first two patches fixes some bugs in the A33 CCU code.

The patch 3 and 4 is for enabling the cpufreq-dt driver to work.

The patch 5 is for enabling the voltage adjusting on reference design tablets.

The patch 6 is for enabling the "turbo-mode" of A33. (According to the
"extremity_freq" property in the FEX file. When I tested it with 3.4 BSP, it
really performs as a turbo mode.)

If there's any doubt of safety, the patch 6 can be ignored.

If there's any problem in patch 3, 4 and 5, they can also be temporarily
ignored, but finally we need them ;-)

Although there's now currently no thermal support for A33, many A33 devices
are tablets with battery, and it will be valuable to save some power energy,
so cpufreq support is also useful.

P.S.

Chen-Yu,

Do you want to test the CCU fix and the operating point table on A23?

Regards,
Icenowy

^ permalink raw reply

* Re: [PATCH resend] ARM: dts: sun8i: Support DTB build for NanoPi M1
From: Maxime Ripard @ 2016-12-13 15:15 UTC (permalink / raw)
  To: Milo Kim; +Cc: Chen-Yu Tsai, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20161212231815.6114-1-woogyom.kim@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 399 bytes --]

On Tue, Dec 13, 2016 at 08:18:15AM +0900, Milo Kim wrote:
> The commit 10efbf5f1633 ("ARM: dts: sun8i: Add dts file for NanoPi M1 SBC")
> introduced NanoPi M1 board but it's missing in Allwinner H3 DTB build.
> 
> Signed-off-by: Milo Kim <woogyom.kim@gmail.com>

Applied, thanks!
Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

^ permalink raw reply

* Re: [PATCH v4 5/5] i2c: designware: Cleaning comments and formatation
From: Jarkko Nikula @ 2016-12-13 15:08 UTC (permalink / raw)
  To: Luis Oliveira, wsa, robh+dt, mark.rutland, andriy.shevchenko,
	mika.westerberg, linux-i2c, devicetree, linux-kernel
  Cc: Ramiro.Oliveira, Joao.Pinto, CARLOS.PALMINHA
In-Reply-To: <02856fdb6ce3230a4ac1ba0958938ad51e763205.1481131072.git.lolivei@synopsys.com>

On 12/07/2016 07:55 PM, Luis Oliveira wrote:
> - Missspelling, comment formatation and fix a string of
>   the existing code
>
> Signed-off-by: Luis Oliveira <lolivei@synopsys.com>
> ---
> Changes V3->V4: (Andy Shevchenko)
> - created a commit message
>
>  drivers/i2c/busses/i2c-designware-common.c |  2 +-
>  drivers/i2c/busses/i2c-designware-slave.c  | 10 ++++++----
>  2 files changed, 7 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
> index 41b38d8b8732..838ef662d2c8 100644
> --- a/drivers/i2c/busses/i2c-designware-common.c
> +++ b/drivers/i2c/busses/i2c-designware-common.c
> @@ -42,7 +42,7 @@ static char *abort_sources[] = {
>  	[ABRT_TXDATA_NOACK] =
>  		"data not acknowledged",
>  	[ABRT_GCALL_NOACK] =
> -		"no acknowledgement for a general call",
> +		"no acknowledgment for a general call",

I'm not a native speaker but are both acknowledgement and acknowledgment 
ok? I.e. is there need to change?

> diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c
> index 1c7f82bb2513..442bc5ce6d47 100644
> --- a/drivers/i2c/busses/i2c-designware-slave.c
> +++ b/drivers/i2c/busses/i2c-designware-slave.c
> @@ -70,8 +70,8 @@ int i2c_dw_init_slave(struct dw_i2c_dev *dev)
>  		/* Configure register access mode 16bit */
>  		dev->accessor_flags |= ACCESS_16BIT;
>  	} else if (reg != DW_IC_COMP_TYPE_VALUE) {
> -		dev_err(dev->dev, "Unknown Synopsys component type: "
> -			"0x%08x\n", reg);
> +		dev_err(dev->dev,
> +		 "Unknown Synopsys component type: 0x%08x\n", reg);
>  		i2c_dw_release_lock(dev);
>  		return -ENODEV;
>  	}
> @@ -181,8 +181,10 @@ int i2c_dw_reg_slave(struct i2c_client *slave)
>  		return -EBUSY;
>  	if (slave->flags & I2C_CLIENT_TEN)
>  		return -EAFNOSUPPORT;
> -		/* set slave address in the IC_SAR register,
> -		* the address to which the DW_apb_i2c responds */
> +		/*
> +		 * set slave address in the IC_SAR register,
> +		 * the address to which the DW_apb_i2c responds
> +		 */

These two should be done already in the patch 4/5 since it introduced 
the lines that you are changing here.

-- 
Jarkko

^ permalink raw reply

* Re: [PATCH v2 3/9] ARM: dts: dra72: Add separate dtsi for tps65917
From: Roger Quadros @ 2016-12-13 14:55 UTC (permalink / raw)
  To: Lokesh Vutla, Tony Lindgren, Linux OMAP Mailing List,
	Tomi Valkeinen, KISHON VIJAY ABRAHAM
  Cc: Tero Kristo, Sekhar Nori, Nishanth Menon,
	Device Tree Mailing List, Rob Herring, Linux ARM Mailing List,
	Carlos Hernandez
In-Reply-To: <91b4075f-2dfb-0d17-592d-f99b91ace590-l0cyMroinI0@public.gmane.org>

Lokesh,

On 13/12/16 15:08, Lokesh Vutla wrote:
> 
> 
> On Tuesday 13 December 2016 06:10 PM, Roger Quadros wrote:
>> +Tomi, Kishon, Carlos
>>  
>> Hi,
>>
>> On 21/10/16 13:38, Lokesh Vutla wrote:
>>> dra72-evm-common.dtsi consolidates dra72-evm.dts and dra72-evm-revc.dts
>>> which also include tps65917 pmic support as both the evms uses the same
>>> pmic. But, dra71-evm has mostly similar features with a different pmic.
>>> In order to exploit dra72-evm-common.dtsi, creating a separate dtsi
>>> for tps65915 support and including it in respective board files.
>>>
>>> Signed-off-by: Lokesh Vutla <lokeshvutla-l0cyMroinI0@public.gmane.org>
>>> ---
>>>  arch/arm/boot/dts/dra72-evm-common.dtsi   | 128 ----------------------------
>>>  arch/arm/boot/dts/dra72-evm-revc.dts      |  21 +++--
>>>  arch/arm/boot/dts/dra72-evm-tps65917.dtsi | 134 ++++++++++++++++++++++++++++++
>>>  arch/arm/boot/dts/dra72-evm.dts           |  14 ++--
>>>  4 files changed, 154 insertions(+), 143 deletions(-)
>>>  create mode 100644 arch/arm/boot/dts/dra72-evm-tps65917.dtsi
>>>
>>
>> This patch breaks USB XHCI and boot on dra72-evm (both revC and non revC)
>>
>> I'll explain why below.
>>
>> [   13.625167] Unhandled fault: imprecise external abort (0x1406) at 0x00000000
>> [   13.632557] pgd = ede10000
>> [   13.635390] [00000000] *pgd=00000000
>> [   13.639145] Internal error: : 1406 [#1] SMP ARM
>> [   13.643893] Modules linked in: xhci_plat_hcd(+) xhci_hcd usbcore omapfb dwc3 cfbfillrect snd_soc_davinci_mcasp cfbimgblt cfbcopyarea udc_core connector_hdmi encoder_tpd12s015 snd_soc_edma m25p80 snd_soc_simpe
>> [   13.695557] CPU: 0 PID: 440 Comm: modprobe Not tainted 4.9.0-rc1 #1050
>> [   13.702399] Hardware name: Generic DRA72X (Flattened Device Tree)
>> [   13.708786] task: edb5c040 task.stack: edd10000
>> [   13.713540] PC is at _raw_spin_unlock_irqrestore+0x0/0x44
>> [   13.719219] LR is at xhci_hub_control+0xc2c/0x15e0 [xhci_hcd]
>> [   13.725242] pc : [<c07df718>]    lr : [<bf486300>]    psr: a0000093
>> [   13.725242] sp : edd118c0  ip : c0e306b4  fp : 00000000
>> [   13.737278] r10: 00000000  r9 : 60000013  r8 : edf28218
>> [   13.742753] r7 : edf28000  r6 : 00000000  r5 : 00000000  r4 : edf2a000
>> [   13.749593] r3 : 00000000  r2 : 00000000  r1 : 60000013  r0 : edf28218
> 
> Hmm.. Thanks for catching it. I remember it was booting when I tested,
> not sure how I missed this :(. usb2_phy1 & 2, mmc and dss supplies needs
> to be added in dra72-evm-tps65917.dtsi file.

OK.
> 
> Tony,
> 	Do you want me to resend this patch or fixup patch on top of this?

In case you send a new patch. please add

Reported-by: Carlos Hernandez <ceh-l0cyMroinI0@public.gmane.org>

Thanks.

cheers,
-roger
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v6 2/2] Add support for OV5647 sensor.
From: Ramiro Oliveira @ 2016-12-13 14:32 UTC (permalink / raw)
  To: mchehab, linux-kernel, linux-media, robh+dt, devicetree
  Cc: davem, gregkh, geert+renesas, akpm, linux, hverkuil, dheitmueller,
	slongerbeam, lars, robert.jarzmik, pavel, pali.rohar,
	sakari.ailus, mark.rutland, Ramiro.Oliveira, CARLOS.PALMINHA
In-Reply-To: <cover.1481639091.git.roliveir@synopsys.com>

Modes supported:
 - 640x480 RAW 8

Signed-off-by: Ramiro Oliveira <roliveir@synopsys.com>
---
 MAINTAINERS                |   7 +
 drivers/media/i2c/Kconfig  |  12 +
 drivers/media/i2c/Makefile |   1 +
 drivers/media/i2c/ov5647.c | 718 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 738 insertions(+)
 create mode 100644 drivers/media/i2c/ov5647.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 52cc077..72e828a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8923,6 +8923,13 @@ M:	Harald Welte <laforge@gnumonks.org>
 S:	Maintained
 F:	drivers/char/pcmcia/cm4040_cs.*
 
+OMNIVISION OV5647 SENSOR DRIVER
+M:	Ramiro Oliveira <roliveir@synopsys.com>
+L:	linux-media@vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/i2c/ov5647.c
+
 OMNIVISION OV7670 SENSOR DRIVER
 M:	Jonathan Corbet <corbet@lwn.net>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index b31fa6f..c1b78e5 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -531,6 +531,18 @@ config VIDEO_OV2659
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov2659.
 
+config VIDEO_OV5647
+	tristate "OmniVision OV5647 sensor support"
+	depends on OF
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV5647 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov5647.
+
 config VIDEO_OV7640
 	tristate "OmniVision OV7640 sensor support"
 	depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 92773b2..0d9014c 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -82,3 +82,4 @@ obj-$(CONFIG_VIDEO_IR_I2C)  += ir-kbd-i2c.o
 obj-$(CONFIG_VIDEO_ML86V7667)	+= ml86v7667.o
 obj-$(CONFIG_VIDEO_OV2659)	+= ov2659.o
 obj-$(CONFIG_VIDEO_TC358743)	+= tc358743.o
+obj-$(CONFIG_VIDEO_OV5647)	+= ov5647.o
diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c
new file mode 100644
index 0000000..c282865
--- /dev/null
+++ b/drivers/media/i2c/ov5647.c
@@ -0,0 +1,718 @@
+/*
+ * A V4L2 driver for OmniVision OV5647 cameras.
+ *
+ * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver
+ * Copyright (C) 2011 Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Based on Omnivision OV7670 Camera Driver
+ * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
+ * Copyright (C) 2016, Synopsys, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-image-sizes.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-of.h>
+
+#define SENSOR_NAME "ov5647"
+
+#define OV5647_SW_RESET		0x1003
+#define OV5647_REG_CHIPID_H	0x300A
+#define OV5647_REG_CHIPID_L	0x300B
+
+#define REG_TERM 0xfffe
+#define VAL_TERM 0xfe
+#define REG_DLY  0xffff
+
+#define OV5647_ROW_START		0x01
+#define OV5647_ROW_START_MIN		0
+#define OV5647_ROW_START_MAX		2004
+#define OV5647_ROW_START_DEF		54
+
+#define OV5647_COLUMN_START		0x02
+#define OV5647_COLUMN_START_MIN		0
+#define OV5647_COLUMN_START_MAX		2750
+#define OV5647_COLUMN_START_DEF		16
+
+#define OV5647_WINDOW_HEIGHT		0x03
+#define OV5647_WINDOW_HEIGHT_MIN	2
+#define OV5647_WINDOW_HEIGHT_MAX	2006
+#define OV5647_WINDOW_HEIGHT_DEF	1944
+
+#define OV5647_WINDOW_WIDTH		0x04
+#define OV5647_WINDOW_WIDTH_MIN		2
+#define OV5647_WINDOW_WIDTH_MAX		2752
+#define OV5647_WINDOW_WIDTH_DEF		2592
+
+struct regval_list {
+	u16 addr;
+	u8 data;
+};
+
+struct cfg_array {
+	struct regval_list *regs;
+	int size;
+};
+
+struct ov5647 {
+	struct device			*dev;
+	struct v4l2_subdev		sd;
+	struct media_pad		pad;
+	struct mutex			lock;
+	struct v4l2_mbus_framefmt	format;
+	unsigned int			width;
+	unsigned int			height;
+	int				power_count;
+	struct clk			*xclk;
+	/* External clock frequency currently supported is 30MHz */
+	u32				xclk_freq;
+};
+
+static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
+{
+	return container_of(sd, struct ov5647, sd);
+}
+
+static struct regval_list sensor_oe_disable_regs[] = {
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+};
+
+static struct regval_list sensor_oe_enable_regs[] = {
+	{0x3000, 0x0f},
+	{0x3001, 0xff},
+	{0x3002, 0xe4},
+};
+
+static struct regval_list ov5647_640x480[] = {
+	{0x0100, 0x00},
+	{0x0103, 0x01},
+	{0x3034, 0x08},
+	{0x3035, 0x21},
+	{0x3036, 0x46},
+	{0x303c, 0x11},
+	{0x3106, 0xf5},
+	{0x3821, 0x07},
+	{0x3820, 0x41},
+	{0x3827, 0xec},
+	{0x370c, 0x0f},
+	{0x3612, 0x59},
+	{0x3618, 0x00},
+	{0x5000, 0x06},
+	{0x5001, 0x01},
+	{0x5002, 0x41},
+	{0x5003, 0x08},
+	{0x5a00, 0x08},
+	{0x3000, 0x00},
+	{0x3001, 0x00},
+	{0x3002, 0x00},
+	{0x3016, 0x08},
+	{0x3017, 0xe0},
+	{0x3018, 0x44},
+	{0x301c, 0xf8},
+	{0x301d, 0xf0},
+	{0x3a18, 0x00},
+	{0x3a19, 0xf8},
+	{0x3c01, 0x80},
+	{0x3b07, 0x0c},
+	{0x380c, 0x07},
+	{0x380d, 0x68},
+	{0x380e, 0x03},
+	{0x380f, 0xd8},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3708, 0x64},
+	{0x3709, 0x52},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xE0},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0xa1},
+	{0x3811, 0x08},
+	{0x3813, 0x02},
+	{0x3630, 0x2e},
+	{0x3632, 0xe2},
+	{0x3633, 0x23},
+	{0x3634, 0x44},
+	{0x3636, 0x06},
+	{0x3620, 0x64},
+	{0x3621, 0xe0},
+	{0x3600, 0x37},
+	{0x3704, 0xa0},
+	{0x3703, 0x5a},
+	{0x3715, 0x78},
+	{0x3717, 0x01},
+	{0x3731, 0x02},
+	{0x370b, 0x60},
+	{0x3705, 0x1a},
+	{0x3f05, 0x02},
+	{0x3f06, 0x10},
+	{0x3f01, 0x0a},
+	{0x3a08, 0x01},
+	{0x3a09, 0x27},
+	{0x3a0a, 0x00},
+	{0x3a0b, 0xf6},
+	{0x3a0d, 0x04},
+	{0x3a0e, 0x03},
+	{0x3a0f, 0x58},
+	{0x3a10, 0x50},
+	{0x3a1b, 0x58},
+	{0x3a1e, 0x50},
+	{0x3a11, 0x60},
+	{0x3a1f, 0x28},
+	{0x4001, 0x02},
+	{0x4004, 0x02},
+	{0x4000, 0x09},
+	{0x4837, 0x24},
+	{0x4050, 0x6e},
+	{0x4051, 0x8f},
+	{0x0100, 0x01},
+};
+
+/**
+ * @short I2C Write operation
+ * @param[in] i2c_client I2C client
+ * @param[in] reg register address
+ * @param[in] val value to write
+ * @return Error code
+ */
+static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
+{
+	int ret;
+	unsigned char data[3] = { reg >> 8, reg & 0xff, val};
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ret = i2c_master_send(client, data, 3);
+	if (ret != 3) {
+		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
+				__func__, reg);
+		return ret < 0 ? ret : -EIO;
+	}
+	return 0;
+}
+
+/**
+ * @short I2C Read operation
+ * @param[in] i2c_client I2C client
+ * @param[in] reg register address
+ * @param[out] val value read
+ * @return Error code
+ */
+static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val)
+{
+	int ret;
+	unsigned char data_w[2] = { reg >> 8, reg & 0xff };
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+
+	ret = i2c_master_send(client, data_w, 2);
+
+	if (ret < 2) {
+		dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
+			__func__, reg);
+		return ret < 0 ? ret : -EIO;
+	}
+
+
+	ret = i2c_master_recv(client, val, 1);
+
+	if (ret < 1) {
+		dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
+				__func__, reg);
+		return ret < 0 ? ret : -EIO;
+	}
+	return 0;
+}
+
+static int ov5647_write_array(struct v4l2_subdev *sd,
+				struct regval_list *regs, int array_size)
+{
+	int i = 0;
+	int ret = 0;
+
+	if (!regs)
+		return -EINVAL;
+
+	while (i < array_size) {
+		ret = ov5647_write(sd, regs->addr, regs->data);
+		if (ret < 0)
+			return ret;
+		i++;
+		regs++;
+	}
+	return 0;
+}
+
+static void ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel)
+{
+	u8 channel_id;
+
+	ov5647_read(sd, 0x4814, &channel_id);
+	channel_id &= ~(3 << 6);
+	ov5647_write(sd, 0x4814, channel_id | (channel << 6));
+}
+
+void ov5647_stream_on(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ov5647_write(sd, 0x4202, 0x00);
+	dev_dbg(&client->dev, "Stream on");
+	ov5647_write(sd, 0x300D, 0x00);
+}
+
+void ov5647_stream_off(struct v4l2_subdev *sd)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ov5647_write(sd, 0x4202, 0x0f);
+	dev_dbg(&client->dev, "Stream off");
+	ov5647_write(sd, 0x300D, 0x01);
+}
+
+/**
+ * @short Set SW standby
+ * @param[in] sd v4l2 sd
+ * @param[in] stanby standby mode status (on or off)
+ * @return Error code
+ */
+static int set_sw_standby(struct v4l2_subdev *sd, bool standby)
+{
+	int ret;
+	unsigned char rdval;
+
+	ret = ov5647_read(sd, 0x0100, &rdval);
+	if (ret != 0)
+		return ret;
+
+	if (standby)
+		rdval &= 0xfe;
+	else
+		rdval |= 0x01;
+
+	ret = ov5647_write(sd, 0x0100, rdval);
+
+	return ret;
+}
+
+/**
+ * @short Initialize sensor
+ * @param[in] sd v4l2 subdev
+ * @param[in] val not used
+ * @return Error code
+ */
+static int __sensor_init(struct v4l2_subdev *sd)
+{
+	int ret;
+	u8 resetval;
+	u8 rdval;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	dev_dbg(&client->dev, "sensor init\n");
+
+	ret = ov5647_read(sd, 0x0100, &rdval);
+	if (ret != 0)
+		return ret;
+
+	ov5647_write(sd, 0x4800, 0x25);
+	ov5647_stream_off(sd);
+
+	ret = ov5647_write_array(sd, ov5647_640x480,
+					ARRAY_SIZE(ov5647_640x480));
+	if (ret < 0) {
+		dev_err(&client->dev, "write sensor_default_regs error\n");
+		return ret;
+	}
+
+	ov5647_set_virtual_channel(sd, 0);
+
+	ov5647_read(sd, 0x0100, &resetval);
+	if (!(resetval & 0x01)) {
+		dev_err(&client->dev, "Device was in SW standby");
+		ov5647_write(sd, 0x0100, 0x01);
+	}
+
+	ov5647_write(sd, 0x4800, 0x04);
+	ov5647_stream_on(sd);
+
+	return 0;
+}
+
+/**
+ * @short Control sensor power state
+ * @param[in] sd v4l2 subdev
+ * @param[in] on Sensor power
+ * @return Error code
+ */
+static int sensor_power(struct v4l2_subdev *sd, int on)
+{
+	int ret;
+	struct ov5647 *ov5647 = to_state(sd);
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ret = 0;
+	mutex_lock(&ov5647->lock);
+
+	if (on && !ov5647->power_count)	{
+		dev_dbg(&client->dev, "OV5647 power on\n");
+
+		clk_set_rate(ov5647->xclk, ov5647->xclk_freq);
+
+		ret = clk_prepare_enable(ov5647->xclk);
+		if (ret < 0) {
+			dev_err(ov5647->dev, "clk prepare enable failed\n");
+			goto out;
+		}
+
+		ret = ov5647_write_array(sd, sensor_oe_enable_regs,
+				ARRAY_SIZE(sensor_oe_enable_regs));
+		if (ret < 0) {
+			clk_disable_unprepare(ov5647->xclk);
+			dev_err(&client->dev,
+				"write sensor_oe_enable_regs error\n");
+			goto out;
+		}
+
+		ret = __sensor_init(sd);
+		if (ret < 0) {
+			clk_disable_unprepare(ov5647->xclk);
+			dev_err(&client->dev,
+				"Camera not available, check Power\n");
+			goto out;
+		}
+	} else if (!on && ov5647->power_count == 1) {
+		dev_dbg(&client->dev, "OV5647 power off\n");
+
+		dev_dbg(&client->dev, "disable oe\n");
+		ret = ov5647_write_array(sd, sensor_oe_disable_regs,
+				ARRAY_SIZE(sensor_oe_disable_regs));
+
+		if (ret < 0)
+			dev_dbg(&client->dev, "disable oe failed\n");
+
+		ret = set_sw_standby(sd, true);
+
+		if (ret < 0)
+			dev_dbg(&client->dev, "soft stby failed\n");
+
+		clk_disable_unprepare(ov5647->xclk);
+	}
+
+	/* Update the power count. */
+	ov5647->power_count += on ? 1 : -1;
+	WARN_ON(ov5647->power_count < 0);
+
+out:
+	mutex_unlock(&ov5647->lock);
+
+	return ret;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/**
+ * @short Get register value
+ * @param[in] sd v4l2 subdev
+ * @param[in] reg register struct
+ * @return Error code
+ */
+static int sensor_get_register(struct v4l2_subdev *sd,
+				struct v4l2_dbg_register *reg)
+{
+	unsigned char val = 0;
+	int ret;
+
+	ret = ov5647_read(sd, reg->reg & 0xff, &val);
+	if (ret != 0)
+		return ret;
+
+	reg->val = val;
+	reg->size = 1;
+
+	return ret;
+}
+
+/**
+ * @short Set register value
+ * @param[in] sd v4l2 subdev
+ * @param[in] reg register struct
+ * @return Error code
+ */
+static int sensor_set_register(struct v4l2_subdev *sd,
+				const struct v4l2_dbg_register *reg)
+{
+	return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff);
+}
+#endif
+
+/**
+ * @short Subdev core operations registration
+ */
+static const struct v4l2_subdev_core_ops sensor_core_ops = {
+	.s_power		= sensor_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	.g_register		= sensor_get_register,
+	.s_register		= sensor_set_register,
+#endif
+};
+
+static int enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index > 0)
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+
+	return 0;
+}
+
+static const struct v4l2_subdev_pad_ops subdev_pad_ops = {
+	.enum_mbus_code = enum_mbus_code,
+};
+
+
+/**
+ * @short Subdev operations registration
+ *
+ */
+static const struct v4l2_subdev_ops subdev_ops = {
+	.core		= &sensor_core_ops,
+	.pad		= &subdev_pad_ops,
+};
+
+/**
+ * @short Detect camera version and model
+ * @param[in] sd v4l2 subdev
+ * @return Error code
+ */
+static int ov5647_detect(struct v4l2_subdev *sd)
+{
+	unsigned char v;
+	int ret;
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+	ret = ov5647_write(sd, OV5647_SW_RESET, 0x01);
+	if (ret < 0)
+		return ret;
+	ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x56) {
+		dev_err(&client->dev, "Wrong model version detected");
+		return -ENODEV;
+	}
+	ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x47) {
+		dev_err(&client->dev, "Wrong model version detected");
+		return -ENODEV;
+	}
+
+	ret = ov5647_write(sd, OV5647_SW_RESET, 0x00);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/**
+ * @short Detect if camera is registered
+ * @param[in] sd v4l2 subdev
+ * @return Error code
+ */
+static int ov5647_registered(struct v4l2_subdev *sd)
+{
+	return 0;
+}
+
+/**
+ * @short Open device
+ * @param[in] sd v4l2 subdev
+ * @param[in] fh v4l2 file handler
+ * @return Error code
+ */
+static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct v4l2_mbus_framefmt *format =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	struct v4l2_rect *crop =
+				v4l2_subdev_get_try_crop(sd, fh->pad, 0);
+
+	crop->left = OV5647_COLUMN_START_DEF;
+	crop->top = OV5647_ROW_START_DEF;
+	crop->width = OV5647_WINDOW_WIDTH_DEF;
+	crop->height = OV5647_WINDOW_HEIGHT_DEF;
+
+	format->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+
+	format->width = OV5647_WINDOW_WIDTH_DEF;
+	format->height = OV5647_WINDOW_HEIGHT_DEF;
+	format->field = V4L2_FIELD_NONE;
+	format->colorspace = V4L2_COLORSPACE_SRGB;
+
+	return sensor_power(sd, true);
+}
+
+/**
+ * @short Open device
+ * @param[in] sd v4l2 subdev
+ * @param[in] fh v4l2 file handler
+ * @return Error code
+ */
+static int ov5647_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	return sensor_power(sd, false);
+}
+
+/**
+ * @short Subdev internal operations registration
+ *
+ */
+static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = {
+	.registered = ov5647_registered,
+	.open = ov5647_open,
+	.close = ov5647_close,
+};
+
+/**
+ * @short Initialization routine - Entry point of the driver
+ * @param[in] client pointer to the i2c client structure
+ * @param[in] id pointer to the i2c device id structure
+ * @return 0 on success and a negative number on failure
+ */
+static int ov5647_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ov5647 *sensor;
+	int ret;
+	struct v4l2_subdev *sd;
+
+	dev_info(&client->dev, "Installing OmniVision OV5647 camera driver\n");
+
+	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+	if (sensor == NULL)
+		return -ENOMEM;
+
+	/* get system clock (xclk) */
+	sensor->xclk = devm_clk_get(dev, "xclk");
+	if (IS_ERR(sensor->xclk)) {
+		dev_err(dev, "could not get xclk");
+		return PTR_ERR(sensor->xclk);
+	}
+
+	ret = of_property_read_u32(dev->of_node, "clock-frequency",
+				    &sensor->xclk_freq);
+	if (ret) {
+		dev_err(dev, "could not get xclk frequency\n");
+		return ret;
+	}
+
+	mutex_init(&sensor->lock);
+	sensor->dev = dev;
+
+	sd = &sensor->sd;
+	v4l2_i2c_subdev_init(sd, client, &subdev_ops);
+	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
+	if (ret < 0)
+		goto mutex_remove;
+
+	ret = ov5647_detect(sd);
+	if (ret < 0)
+		goto error;
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+error:
+	media_entity_cleanup(&sd->entity);
+mutex_remove:
+	mutex_destroy(&sensor->lock);
+	return ret;
+}
+
+/**
+ * @short Exit routine - Exit point of the driver
+ * @param[in] client pointer to the i2c client structure
+ * @return 0 on success and a negative number on failure
+ */
+static int ov5647_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov5647 *ov5647 = to_state(sd);
+
+	v4l2_async_unregister_subdev(&ov5647->sd);
+	media_entity_cleanup(&ov5647->sd.entity);
+	v4l2_device_unregister_subdev(sd);
+	mutex_destroy(&ov5647->lock);
+
+	return 0;
+}
+
+static const struct i2c_device_id ov5647_id[] = {
+	{ "ov5647", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ov5647_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ov5647_of_match[] = {
+	{ .compatible = "ovti,ov5647" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ov5647_of_match);
+#endif
+
+/**
+ * @short i2c driver structure
+ */
+static struct i2c_driver ov5647_driver = {
+	.driver = {
+		.of_match_table = of_match_ptr(ov5647_of_match),
+		.owner	= THIS_MODULE,
+		.name	= "ov5647",
+	},
+	.probe		= ov5647_probe,
+	.remove		= ov5647_remove,
+	.id_table	= ov5647_id,
+};
+
+module_i2c_driver(ov5647_driver);
+
+MODULE_AUTHOR("Ramiro Oliveira <roliveir@synopsys.com>");
+MODULE_DESCRIPTION("A low-level driver for OmniVision ov5647 sensors");
+MODULE_LICENSE("GPL v2");
-- 
2.10.2

^ permalink raw reply related

* [PATCH v6 1/2] Add OV5647 device tree documentation
From: Ramiro Oliveira @ 2016-12-13 14:32 UTC (permalink / raw)
  To: mchehab, linux-kernel, linux-media, robh+dt, devicetree
  Cc: davem, gregkh, geert+renesas, akpm, linux, hverkuil, dheitmueller,
	slongerbeam, lars, robert.jarzmik, pavel, pali.rohar,
	sakari.ailus, mark.rutland, Ramiro.Oliveira, CARLOS.PALMINHA
In-Reply-To: <cover.1481639091.git.roliveir@synopsys.com>

Create device tree bindings documentation.

Signed-off-by: Ramiro Oliveira <roliveir@synopsys.com>
---
 .../devicetree/bindings/media/i2c/ov5647.txt       | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5647.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/ov5647.txt b/Documentation/devicetree/bindings/media/i2c/ov5647.txt
new file mode 100644
index 0000000..46e5e30
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov5647.txt
@@ -0,0 +1,35 @@
+Omnivision OV5647 raw image sensor
+---------------------------------
+
+OV5647 is a raw image sensor with MIPI CSI-2 and CCP2 image data interfaces
+and CCI (I2C compatible) control bus.
+
+Required properties:
+
+- compatible	: "ovti,ov5647";
+- reg		: I2C slave address of the sensor;
+- clocks	: Reference to the xclk clock.
+- clock-names	: Should be "xclk".
+- clock-frequency: Frequency of the xclk clock
+
+The common video interfaces bindings (see video-interfaces.txt) should be
+used to specify link to the image data receiver. The OV5647 device
+node should contain one 'port' child node with an 'endpoint' subnode.
+
+Example:
+
+	i2c@0x02000 {
+		...
+		ov: camera@0x36 {
+			compatible = "ovti,ov5647";
+			reg = <0x36>;
+			clocks = <&camera_clk>;
+			clock-names = "xclk";
+			clock-frequency = <30000000>;
+			port {
+				camera_1: endpoint {
+					remote-endpoint = <&csi1_ep1>;
+				};
+			};
+		};
+	};
-- 
2.10.2

^ permalink raw reply related

* [PATCH v6 0/2] Add support for Omnivision OV5647
From: Ramiro Oliveira @ 2016-12-13 14:32 UTC (permalink / raw)
  To: mchehab, linux-kernel, linux-media, robh+dt, devicetree
  Cc: davem, gregkh, geert+renesas, akpm, linux, hverkuil, dheitmueller,
	slongerbeam, lars, robert.jarzmik, pavel, pali.rohar,
	sakari.ailus, mark.rutland, Ramiro.Oliveira, CARLOS.PALMINHA

Hello,

This patch adds support for the Omnivision OV5647 sensor.

At the moment it only supports 640x480 in Raw 8.

This is the sixth version of the OV5647 camera driver patchset.

v6:
 - Add example to DT documentation
 - Remove data-lanes and clock-lane property from DT
 - Add external clock property to DT
 - Order includes
 - Remove unused variables and functions
 - Add external clock handling
 - Add power on counter
 - Change from g/s_parm to g/s_frame_interval

v5:
 - Refactor code 
 - Change comments
 - Add missing error handling in some functions

v4: 
 - Add correct license
 - Revert debugging info to generic infrastructure
 - Turn defines into enums
 - Correct code style issues
 - Remove unused defines
 - Make sure all errors where being handled
 - Rename some functions to make code more readable
 - Add some debugging info

v3: 
 - No changes. Re-submitted due to lack of responses

v2: 
 - Corrections in DT documentation

Ramiro Oliveira (2):
  Add OV5647 device tree documentation
  Add support for OV5647 sensor.

 .../devicetree/bindings/media/i2c/ov5647.txt       |  35 +
 MAINTAINERS                                        |   7 +
 drivers/media/i2c/Kconfig                          |  12 +
 drivers/media/i2c/Makefile                         |   1 +
 drivers/media/i2c/ov5647.c                         | 718 +++++++++++++++++++++
 5 files changed, 773 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5647.txt
 create mode 100644 drivers/media/i2c/ov5647.c

-- 
2.10.2

^ permalink raw reply

* Re: [PATCH] iio: misc: add a generic regulator driver
From: Bartosz Golaszewski @ 2016-12-13 14:28 UTC (permalink / raw)
  To: Lars-Peter Clausen, Linus Walleij
  Cc: Jonathan Cameron, Hartmut Knaack, Peter Meerwald-Stadler,
	Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-devicetree, LKML, Kevin Hilman, Patrick Titiano,
	Neil Armstrong, Liam Girdwood, Mark Brown
In-Reply-To: <c670d597-46b6-235f-545f-7136a3abff7f-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>

2016-12-12 18:15 GMT+01:00 Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>:
> On 12/06/2016 12:12 PM, Bartosz Golaszewski wrote:

[snip!]

>>
>> So the problem we have is not power-cycling the adc - it's
>> power-cycling the device connected to a probe on which there's an adc.
>> What I was trying to do was adding support for the power-switch on
>> baylibre-acme[1] probes.
>>
>> For example: we have a USB probe on which the VBUS signal goes through
>> a power load switch and than through the adc. The adc (in this case
>> ina226) is always powered on, while the fixed regulator I wanted to
>> enable/disable actually drives the power switch to cut/restore power
>> to the connected USB device i.e. there's no real regulator - just a
>> GPIO driving the power switch.
>>
>> A typical use case is measuring the power consumption of development
>> boards[2]. Rebooting them remotely using acme probes is already done,
>> but we're using the obsolete /sys/class/gpio interface.
>>
>> We're already using libiio to read the measured data from the power
>> monitor, that's why we'd like to use the iio framework for
>> power-cycling the devices as well. My question is: would bridging the
>> regulator framework be the right solution? Should we look for
>> something else? Bridge the GPIO framework instead?
>
> I wouldn't necessaries create bridge, but instead just use the GPIO
> framework directly.
>
> We now have the GPIO chardev interface which meant to be used to support
> application specific logic that control the GPIOs, but where you don't want
> to write a kernel driver.
>
> My idea was to add GPIOs and GPIO chips as high level object inside libiio
> that can be accessed through the same context as the IIO devices. Similar to
> the current IIO API you have a API for gpios that allows to enumerate the
> GPIO devices and their pins as well as modify the pin state.
>

+ Linus

While the new GPIO interface would be very convenient - in our case we
could simply name the lines appropriately in the device tree - I'm not
sure this would be the correct approach.

>From this year's ELCE in Berlin I remember Linus suggested during his
talk that it's always better to write a kernel driver. Also: this way
the relevant GPIO lines would not be reserved for exclusive use by
power switches.

Linus - do you have any thoughts/suggestions on that subject?

Best regards,
Bartosz Golaszewski

^ permalink raw reply

* [PATCH v4 2/2] eeprom: Add IDT 89HPESx driver bindings file
From: Serge Semin @ 2016-12-13 14:22 UTC (permalink / raw)
  To: gregkh, srinivas.kandagatla, andrew, robh+dt, mark.rutland
  Cc: Sergey.Semin, linux-kernel, devicetree, Serge Semin
In-Reply-To: <1481638971-6247-1-git-send-email-fancer.lancer@gmail.com>

IDT 89HPESx PCIe-switches exposes SMBus interface to have an access to
the device CSRs and EEPROM. So to properly utilize the interface
functionality, developer should declare a valid dts-file node, which
would refer to the corresponding 89HPESx device.

Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
---
 .../devicetree/bindings/misc/idt_89hpesx.txt       | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/idt_89hpesx.txt

diff --git a/Documentation/devicetree/bindings/misc/idt_89hpesx.txt b/Documentation/devicetree/bindings/misc/idt_89hpesx.txt
new file mode 100644
index 0000000..b9093b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/idt_89hpesx.txt
@@ -0,0 +1,44 @@
+EEPROM / CSR SMBus-slave interface of IDT 89HPESx devices
+
+Required properties:
+  - compatible : should be "<manufacturer>,<type>"
+		 Basically there is only one manufacturer: idt, but some
+		 compatible devices may be produced in future. Following devices
+		 are supported: 89hpes8nt2, 89hpes12nt3, 89hpes24nt6ag2,
+		 89hpes32nt8ag2, 89hpes32nt8bg2, 89hpes12nt12g2, 89hpes16nt16g2,
+		 89hpes24nt24g2, 89hpes32nt24ag2, 89hpes32nt24bg2;
+		 89hpes12n3, 89hpes12n3a, 89hpes24n3, 89hpes24n3a;
+		 89hpes32h8, 89hpes32h8g2, 89hpes48h12, 89hpes48h12g2,
+		 89hpes48h12ag2, 89hpes16h16, 89hpes22h16, 89hpes22h16g2,
+		 89hpes34h16, 89hpes34h16g2, 89hpes64h16, 89hpes64h16g2,
+		 89hpes64h16ag2;
+		 89hpes12t3g2, 89hpes24t3g2, 89hpes16t4, 89hpes4t4g2,
+		 89hpes10t4g2, 89hpes16t4g2, 89hpes16t4ag2, 89hpes5t5,
+		 89hpes6t5, 89hpes8t5, 89hpes8t5a, 89hpes24t6, 89hpes6t6g2,
+		 89hpes24t6g2, 89hpes16t7, 89hpes32t8, 89hpes32t8g2,
+		 89hpes48t12, 89hpes48t12g2.
+  - reg :	 I2C address of the IDT 89HPESx device.
+
+Optionally there can be EEPROM-compatible subnode:
+  - compatible:  There are five EEPROM devices supported: 24c32, 24c64, 24c128,
+		 24c256 and 24c512 differed by size.
+  - reg:         Custom address of EEPROM device (If not specified IDT 89HPESx
+    (optional)	 device will try to communicate with EEPROM sited by default
+		 address - 0x50)
+  - read-only :	 Parameterless property disables writes to the EEPROM
+    (optional)
+
+Example:
+	idt@60 {
+		compatible = "idt,89hpes32nt8ag2";
+		reg = <0x74>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		eeprom@50 {
+			compatible = "onsemi,24c64";
+			reg = <0x50>;
+			read-only;
+		};
+	};
+
-- 
2.6.6

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox