diff options
| author | Takashi Iwai <tiwai@suse.de> | 2012-10-06 16:33:52 +0200 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2012-10-06 16:33:52 +0200 | 
| commit | 0fd0ba5f9e8ebae66afded580f5f34936f740ac7 (patch) | |
| tree | f232be1bf350b2aec4e860e7156f5292344b0c62 | |
| parent | b7ef37d0e382298bcf7ba399ce67b044d9add23a (diff) | |
| parent | 9911f7f7562a25381eff93fdc660a4a3b4c0f6e0 (diff) | |
| download | olio-linux-3.10-0fd0ba5f9e8ebae66afded580f5f34936f740ac7.tar.xz olio-linux-3.10-0fd0ba5f9e8ebae66afded580f5f34936f740ac7.zip  | |
Merge tag 'asoc-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Additional updates for v3.7
A couple more updates for 3.7, enhancements to the ux500 and wm2000
drivers, a new driver for DA9055 and the support for regulator bypass
mode.  With the exception of the DA9055 this has all had a chance to
soak in -next (the driver was added on Friday so should be in -next
today).
40 files changed, 2307 insertions, 174 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator index e091fa87379..bc578bc6062 100644 --- a/Documentation/ABI/testing/sysfs-class-regulator +++ b/Documentation/ABI/testing/sysfs-class-regulator @@ -349,3 +349,24 @@ Description:  		This will be one of the same strings reported by  		the "state" attribute. + +What:		/sys/class/regulator/.../bypass +Date:		September 2012 +KernelVersion:	3.7 +Contact:	Mark Brown <broonie@opensource.wolfsonmicro.com> +Description: +		Some regulator directories will contain a field called +		bypass.  This indicates if the device is in bypass mode. + +		This will be one of the following strings: + +		'enabled' +		'disabled' +		'unknown' + +		'enabled' means the regulator is in bypass mode. + +		'disabled' means that the regulator is regulating. + +		'unknown' means software cannot determine the state, or +		the reported state is invalid. diff --git a/Documentation/devicetree/bindings/sound/cs4271.txt b/Documentation/devicetree/bindings/sound/cs4271.txt new file mode 100644 index 00000000000..c81b5fd5a5b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs4271.txt @@ -0,0 +1,36 @@ +Cirrus Logic CS4271 DT bindings + +This driver supports both the I2C and the SPI bus. + +Required properties: + + - compatible: "cirrus,cs4271" + +For required properties on SPI, please consult +Documentation/devicetree/bindings/spi/spi-bus.txt + +Required properties on I2C: + + - reg: the i2c address + + +Optional properties: + + - reset-gpio: 	a GPIO spec to define which pin is connected to the chip's +		!RESET pin + +Examples: + +	codec_i2c: cs4271@10 { +		compatible = "cirrus,cs4271"; +		reg = <0x10>; +		reset-gpio = <&gpio 23 0>; +	}; + +	codec_spi: cs4271@0 { +		compatible = "cirrus,cs4271"; +		reg = <0x0>; +		reset-gpio = <&gpio 23 0>; +		spi-max-frequency = <6000000>; +	}; + diff --git a/Documentation/devicetree/bindings/sound/ux500-mop500.txt b/Documentation/devicetree/bindings/sound/ux500-mop500.txt new file mode 100644 index 00000000000..48e071c96b4 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ux500-mop500.txt @@ -0,0 +1,39 @@ +* MOP500 Audio Machine Driver + +This node is responsible for linking together all ux500 Audio Driver components. + +Required properties: + - compatible              : "stericsson,snd-soc-mop500" + +Non-standard properties: + - stericsson,cpu-dai      : Phandle to the CPU-side DAI + - stericsson,audio-codec  : Phandle to the Audio CODEC + - stericsson,card-name    : Over-ride default card name + +Example: + +	sound { +		compatible = "stericsson,snd-soc-mop500"; + +		stericsson,cpu-dai = <&msp1 &msp3>; +		stericsson,audio-codec = <&codec>; +	}; + +	msp1: msp@80124000 { +		compatible = "stericsson,ux500-msp-i2s"; +		reg = <0x80124000 0x1000>; +		interrupts = <0 62 0x4>; +		v-ape-supply = <&db8500_vape_reg>; +	}; + +	msp3: msp@80125000 { +		compatible = "stericsson,ux500-msp-i2s"; +		reg = <0x80125000 0x1000>; +		interrupts = <0 62 0x4>; +		v-ape-supply = <&db8500_vape_reg>; +	}; + +	codec: ab8500-codec { +		compatible = "stericsson,ab8500-codec"; +		stericsson,earpeice-cmv = <950>; /* Units in mV. */ +	}; diff --git a/Documentation/devicetree/bindings/sound/ux500-msp.txt b/Documentation/devicetree/bindings/sound/ux500-msp.txt new file mode 100644 index 00000000000..99acd9c774e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ux500-msp.txt @@ -0,0 +1,43 @@ +* ux500 MSP (CPU-side Digital Audio Interface) + +Required properties: + - compatible       :"stericsson,ux500-msp-i2s" + - reg              : Physical base address and length of the device's registers. + +Optional properties: + - interrupts       : The interrupt output from the device. + - interrupt-parent : The parent interrupt controller. + - <name>-supply    : Phandle to the regulator <name> supply + +Example: + +	sound { +		compatible = "stericsson,snd-soc-mop500"; + +		stericsson,platform-pcm-dma = <&pcm>; +		stericsson,cpu-dai = <&msp1 &msp3>; +		stericsson,audio-codec = <&codec>; +	}; + +	pcm: ux500-pcm { +		compatible = "stericsson,ux500-pcm"; +	}; + +	msp1: msp@80124000 { +		compatible = "stericsson,ux500-msp-i2s"; +		reg = <0x80124000 0x1000>; +		interrupts = <0 62 0x4>; +		v-ape-supply = <&db8500_vape_reg>; +	}; + +	msp3: msp@80125000 { +		compatible = "stericsson,ux500-msp-i2s"; +		reg = <0x80125000 0x1000>; +		interrupts = <0 62 0x4>; +		v-ape-supply = <&db8500_vape_reg>; +	}; + +	codec: ab8500-codec { +		compatible = "stericsson,ab8500-codec"; +		stericsson,earpeice-cmv = <950>; /* Units in mV. */ +	}; diff --git a/arch/arm/mach-imx/eukrea_mbimx27-baseboard.c b/arch/arm/mach-imx/eukrea_mbimx27-baseboard.c index fd3177f9e79..98aef571b9f 100644 --- a/arch/arm/mach-imx/eukrea_mbimx27-baseboard.c +++ b/arch/arm/mach-imx/eukrea_mbimx27-baseboard.c @@ -348,4 +348,5 @@ void __init eukrea_mbimx27_baseboard_init(void)  	imx27_add_imx_keypad(&eukrea_mbimx27_keymap_data);  	gpio_led_register_device(-1, &eukrea_mbimx27_gpio_led_info); +	imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);  } diff --git a/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c b/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c index dfd2da87c2d..0b84666792f 100644 --- a/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c +++ b/arch/arm/mach-imx/eukrea_mbimxsd25-baseboard.c @@ -306,4 +306,5 @@ void __init eukrea_mbimxsd25_baseboard_init(void)  	platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));  	gpio_led_register_device(-1, &eukrea_mbimxsd_led_info);  	imx_add_gpio_keys(&eukrea_mbimxsd_button_data); +	imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);  } diff --git a/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c b/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c index 6e9dd12a696..c6532a007d4 100644 --- a/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c +++ b/arch/arm/mach-imx/eukrea_mbimxsd35-baseboard.c @@ -315,4 +315,5 @@ void __init eukrea_mbimxsd35_baseboard_init(void)  	platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));  	gpio_led_register_device(-1, &eukrea_mbimxsd_led_info);  	imx_add_gpio_keys(&eukrea_mbimxsd_button_data); +	imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);  } diff --git a/arch/arm/mach-imx/eukrea_mbimxsd51-baseboard.c b/arch/arm/mach-imx/eukrea_mbimxsd51-baseboard.c index 96a24b73dc2..8b0de30d7a3 100644 --- a/arch/arm/mach-imx/eukrea_mbimxsd51-baseboard.c +++ b/arch/arm/mach-imx/eukrea_mbimxsd51-baseboard.c @@ -228,4 +228,5 @@ void __init eukrea_mbimxsd51_baseboard_init(void)  	gpio_led_register_device(-1, &eukrea_mbimxsd51_led_info);  	imx_add_gpio_keys(&eukrea_mbimxsd51_button_data); +	imx_add_platform_device("eukrea_tlv320", 0, NULL, 0, NULL, 0);  } diff --git a/arch/arm/mach-ux500/board-mop500-msp.c b/arch/arm/mach-ux500/board-mop500-msp.c index df15646036a..ace6c051bc1 100644 --- a/arch/arm/mach-ux500/board-mop500-msp.c +++ b/arch/arm/mach-ux500/board-mop500-msp.c @@ -7,7 +7,6 @@  #include <linux/platform_device.h>  #include <linux/init.h>  #include <linux/gpio.h> -#include <linux/pinctrl/consumer.h>  #include <plat/gpio-nomadik.h>  #include <plat/pincfg.h> @@ -23,53 +22,6 @@  #include "devices-db8500.h"  #include "pins-db8500.h" -/* MSP1/3 Tx/Rx usage protection */ -static DEFINE_SPINLOCK(msp_rxtx_lock); - -/* Reference Count */ -static int msp_rxtx_ref; - -/* Pin modes */ -struct pinctrl *msp1_p; -struct pinctrl_state *msp1_def; -struct pinctrl_state *msp1_sleep; - -int msp13_i2s_init(void) -{ -	int retval = 0; -	unsigned long flags; - -	spin_lock_irqsave(&msp_rxtx_lock, flags); -	if (msp_rxtx_ref == 0 && !(IS_ERR(msp1_p) || IS_ERR(msp1_def))) { -		retval = pinctrl_select_state(msp1_p, msp1_def); -		if (retval) -			pr_err("could not set MSP1 defstate\n"); -	} -	if (!retval) -		msp_rxtx_ref++; -	spin_unlock_irqrestore(&msp_rxtx_lock, flags); - -	return retval; -} - -int msp13_i2s_exit(void) -{ -	int retval = 0; -	unsigned long flags; - -	spin_lock_irqsave(&msp_rxtx_lock, flags); -	WARN_ON(!msp_rxtx_ref); -	msp_rxtx_ref--; -	if (msp_rxtx_ref == 0 && !(IS_ERR(msp1_p) || IS_ERR(msp1_sleep))) { -		retval = pinctrl_select_state(msp1_p, msp1_sleep); -		if (retval) -			pr_err("could not set MSP1 sleepstate\n"); -	} -	spin_unlock_irqrestore(&msp_rxtx_lock, flags); - -	return retval; -} -  static struct stedma40_chan_cfg msp0_dma_rx = {  	.high_priority = true,  	.dir = STEDMA40_PERIPH_TO_MEM, @@ -132,8 +84,6 @@ static struct msp_i2s_platform_data msp1_platform_data = {  	.id = MSP_I2S_1,  	.msp_i2s_dma_rx = NULL,  	.msp_i2s_dma_tx = &msp1_dma_tx, -	.msp_i2s_init = msp13_i2s_init, -	.msp_i2s_exit = msp13_i2s_exit,  };  static struct stedma40_chan_cfg msp2_dma_rx = { @@ -219,49 +169,22 @@ static struct msp_i2s_platform_data msp3_platform_data = {  	.id		= MSP_I2S_3,  	.msp_i2s_dma_rx	= &msp1_dma_rx,  	.msp_i2s_dma_tx	= NULL, -	.msp_i2s_init = msp13_i2s_init, -	.msp_i2s_exit = msp13_i2s_exit,  };  int mop500_msp_init(struct device *parent)  { -	struct platform_device *msp1; -  	pr_info("%s: Register platform-device 'snd-soc-mop500'.\n", __func__);  	platform_device_register(&snd_soc_mop500);  	pr_info("Initialize MSP I2S-devices.\n");  	db8500_add_msp_i2s(parent, 0, U8500_MSP0_BASE, IRQ_DB8500_MSP0,  			   &msp0_platform_data); -	msp1 = db8500_add_msp_i2s(parent, 1, U8500_MSP1_BASE, IRQ_DB8500_MSP1, +	db8500_add_msp_i2s(parent, 1, U8500_MSP1_BASE, IRQ_DB8500_MSP1,  			   &msp1_platform_data);  	db8500_add_msp_i2s(parent, 2, U8500_MSP2_BASE, IRQ_DB8500_MSP2,  			   &msp2_platform_data);  	db8500_add_msp_i2s(parent, 3, U8500_MSP3_BASE, IRQ_DB8500_MSP1,  			   &msp3_platform_data); -	/* Get the pinctrl handle for MSP1 */ -	if (msp1) { -		msp1_p = pinctrl_get(&msp1->dev); -		if (IS_ERR(msp1_p)) -			dev_err(&msp1->dev, "could not get MSP1 pinctrl\n"); -		else { -			msp1_def = pinctrl_lookup_state(msp1_p, -							PINCTRL_STATE_DEFAULT); -			if (IS_ERR(msp1_def)) { -				dev_err(&msp1->dev, -					"could not get MSP1 defstate\n"); -			} -			msp1_sleep = pinctrl_lookup_state(msp1_p, -							  PINCTRL_STATE_SLEEP); -			if (IS_ERR(msp1_sleep)) -				dev_err(&msp1->dev, -					"could not get MSP1 idlestate\n"); -		} -	} - -	pr_info("%s: Register platform-device 'ux500-pcm'\n", __func__); -	platform_device_register(&ux500_pcm); -  	return 0;  } diff --git a/arch/arm/mach-ux500/include/mach/msp.h b/arch/arm/mach-ux500/include/mach/msp.h index 798be19129e..3cc7142eee0 100644 --- a/arch/arm/mach-ux500/include/mach/msp.h +++ b/arch/arm/mach-ux500/include/mach/msp.h @@ -22,8 +22,6 @@ struct msp_i2s_platform_data {  	enum msp_i2s_id id;  	struct stedma40_chan_cfg *msp_i2s_dma_rx;  	struct stedma40_chan_cfg *msp_i2s_dma_tx; -	int (*msp_i2s_init) (void); -	int (*msp_i2s_exit) (void);  };  #endif diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 427a289f32a..6c19833ed2d 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -434,6 +434,11 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)  	regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,  			   ARIZONA_JD1_ENA, ARIZONA_JD1_ENA); +	ret = regulator_allow_bypass(info->micvdd, true); +	if (ret != 0) +		dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n", +			 ret); +  	pm_runtime_put(&pdev->dev);  	return 0; diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index c8f95c07adb..80e012f1416 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -39,6 +39,8 @@ static struct regulator_ops arizona_ldo1_ops = {  	.map_voltage = regulator_map_voltage_linear,  	.get_voltage_sel = regulator_get_voltage_sel_regmap,  	.set_voltage_sel = regulator_set_voltage_sel_regmap, +	.get_bypass = regulator_get_bypass_regmap, +	.set_bypass = regulator_set_bypass_regmap,  };  static const struct regulator_desc arizona_ldo1 = { @@ -49,6 +51,8 @@ static const struct regulator_desc arizona_ldo1 = {  	.vsel_reg = ARIZONA_LDO1_CONTROL_1,  	.vsel_mask = ARIZONA_LDO1_VSEL_MASK, +	.bypass_reg = ARIZONA_LDO1_CONTROL_1, +	.bypass_mask = ARIZONA_LDO1_BYPASS,  	.min_uV = 900000,  	.uV_step = 50000,  	.n_voltages = 7, diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 450a069aa9b..d9b1f82cc5b 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -82,6 +82,9 @@ static struct regulator_ops arizona_micsupp_ops = {  	.get_voltage_sel = regulator_get_voltage_sel_regmap,  	.set_voltage_sel = regulator_set_voltage_sel_regmap, + +	.get_bypass = regulator_get_bypass_regmap, +	.set_bypass = regulator_set_bypass_regmap,  };  static const struct regulator_desc arizona_micsupp = { @@ -95,6 +98,8 @@ static const struct regulator_desc arizona_micsupp = {  	.vsel_mask = ARIZONA_LDO2_VSEL_MASK,  	.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,  	.enable_mask = ARIZONA_CPMIC_ENA, +	.bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1, +	.bypass_mask = ARIZONA_CPMIC_BYPASS,  	.owner = THIS_MODULE,  }; diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 48385318175..419805cdd9d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -77,6 +77,7 @@ struct regulator {  	struct device *dev;  	struct list_head list;  	unsigned int always_on:1; +	unsigned int bypass:1;  	int uA_load;  	int min_uV;  	int max_uV; @@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev,  	case REGULATOR_STATUS_STANDBY:  		label = "standby";  		break; +	case REGULATOR_STATUS_BYPASS: +		label = "bypass"; +		break;  	case REGULATOR_STATUS_UNDEFINED:  		label = "undefined";  		break; @@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,  static DEVICE_ATTR(suspend_standby_state, 0444,  		regulator_suspend_standby_state_show, NULL); +static ssize_t regulator_bypass_show(struct device *dev, +				     struct device_attribute *attr, char *buf) +{ +	struct regulator_dev *rdev = dev_get_drvdata(dev); +	const char *report; +	bool bypass; +	int ret; + +	ret = rdev->desc->ops->get_bypass(rdev, &bypass); + +	if (ret != 0) +		report = "unknown"; +	else if (bypass) +		report = "enabled"; +	else +		report = "disabled"; + +	return sprintf(buf, "%s\n", report); +} +static DEVICE_ATTR(bypass, 0444, +		   regulator_bypass_show, NULL);  /*   * These are the only attributes are present for all regulators. @@ -2674,6 +2699,100 @@ out:  EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);  /** + * regulator_set_bypass_regmap - Default set_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: state to set. + */ +int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable) +{ +	unsigned int val; + +	if (enable) +		val = rdev->desc->bypass_mask; +	else +		val = 0; + +	return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg, +				  rdev->desc->bypass_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap); + +/** + * regulator_get_bypass_regmap - Default get_bypass() using regmap + * + * @rdev: device to operate on. + * @enable: current state. + */ +int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) +{ +	unsigned int val; +	int ret; + +	ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val); +	if (ret != 0) +		return ret; + +	*enable = val & rdev->desc->bypass_mask; + +	return 0; +} +EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap); + +/** + * regulator_allow_bypass - allow the regulator to go into bypass mode + * + * @regulator: Regulator to configure + * @allow: enable or disable bypass mode + * + * Allow the regulator to go into bypass mode if all other consumers + * for the regulator also enable bypass mode and the machine + * constraints allow this.  Bypass mode means that the regulator is + * simply passing the input directly to the output with no regulation. + */ +int regulator_allow_bypass(struct regulator *regulator, bool enable) +{ +	struct regulator_dev *rdev = regulator->rdev; +	int ret = 0; + +	if (!rdev->desc->ops->set_bypass) +		return 0; + +	if (rdev->constraints && +	    !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS)) +		return 0; + +	mutex_lock(&rdev->mutex); + +	if (enable && !regulator->bypass) { +		rdev->bypass_count++; + +		if (rdev->bypass_count == rdev->open_count) { +			ret = rdev->desc->ops->set_bypass(rdev, enable); +			if (ret != 0) +				rdev->bypass_count--; +		} + +	} else if (!enable && regulator->bypass) { +		rdev->bypass_count--; + +		if (rdev->bypass_count != rdev->open_count) { +			ret = rdev->desc->ops->set_bypass(rdev, enable); +			if (ret != 0) +				rdev->bypass_count++; +		} +	} + +	if (ret == 0) +		regulator->bypass = enable; + +	mutex_unlock(&rdev->mutex); + +	return ret; +} +EXPORT_SYMBOL_GPL(regulator_allow_bypass); + +/**   * regulator_register_notifier - register regulator event notifier   * @regulator: regulator source   * @nb: notifier block @@ -3036,6 +3155,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev)  		if (status < 0)  			return status;  	} +	if (ops->get_bypass) { +		status = device_create_file(dev, &dev_attr_bypass); +		if (status < 0) +			return status; +	}  	/* some attributes are type-specific */  	if (rdev->desc->type == REGULATOR_CURRENT) { @@ -3124,6 +3248,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)  			   &rdev->use_count);  	debugfs_create_u32("open_count", 0444, rdev->debugfs,  			   &rdev->open_count); +	debugfs_create_u32("bypass_count", 0444, rdev->debugfs, +			   &rdev->bypass_count);  }  /** diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 5cb70ca1e98..f203a972ded 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -237,6 +237,8 @@ static struct regulator_ops wm831x_gp_ldo_ops = {  	.set_mode = wm831x_gp_ldo_set_mode,  	.get_status = wm831x_gp_ldo_get_status,  	.get_optimum_mode = wm831x_gp_ldo_get_optimum_mode, +	.get_bypass = regulator_get_bypass_regmap, +	.set_bypass = regulator_set_bypass_regmap,  	.is_enabled = regulator_is_enabled_regmap,  	.enable = regulator_enable_regmap, @@ -293,6 +295,8 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)  	ldo->desc.vsel_mask = WM831X_LDO1_ON_VSEL_MASK;  	ldo->desc.enable_reg = WM831X_LDO_ENABLE;  	ldo->desc.enable_mask = 1 << id; +	ldo->desc.bypass_reg = ldo->base; +	ldo->desc.bypass_mask = WM831X_LDO1_SWI;  	config.dev = pdev->dev.parent;  	if (pdata) @@ -488,6 +492,8 @@ static struct regulator_ops wm831x_aldo_ops = {  	.get_mode = wm831x_aldo_get_mode,  	.set_mode = wm831x_aldo_set_mode,  	.get_status = wm831x_aldo_get_status, +	.set_bypass = regulator_set_bypass_regmap, +	.get_bypass = regulator_get_bypass_regmap,  	.is_enabled = regulator_is_enabled_regmap,  	.enable = regulator_enable_regmap, @@ -544,6 +550,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)  	ldo->desc.vsel_mask = WM831X_LDO7_ON_VSEL_MASK;  	ldo->desc.enable_reg = WM831X_LDO_ENABLE;  	ldo->desc.enable_mask = 1 << id; +	ldo->desc.bypass_reg = ldo->base; +	ldo->desc.bypass_mask = WM831X_LDO7_SWI;  	config.dev = pdev->dev.parent;  	if (pdata) diff --git a/include/linux/mfd/abx500/ab8500-codec.h b/include/linux/mfd/abx500/ab8500-codec.h index dc6529202cd..d7079413def 100644 --- a/include/linux/mfd/abx500/ab8500-codec.h +++ b/include/linux/mfd/abx500/ab8500-codec.h @@ -23,7 +23,8 @@ enum amic_type {  /* Mic-biases */  enum amic_micbias {  	AMIC_MICBIAS_VAMIC1, -	AMIC_MICBIAS_VAMIC2 +	AMIC_MICBIAS_VAMIC2, +	AMIC_MICBIAS_UNKNOWN  };  /* Bias-voltage */ @@ -31,7 +32,8 @@ enum ear_cm_voltage {  	EAR_CMV_0_95V,  	EAR_CMV_1_10V,  	EAR_CMV_1_27V, -	EAR_CMV_1_58V +	EAR_CMV_1_58V, +	EAR_CMV_UNKNOWN  };  /* Analog microphone settings */ diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index da339fd8c75..ea3e3581662 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -177,6 +177,8 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode);  unsigned int regulator_get_mode(struct regulator *regulator);  int regulator_set_optimum_mode(struct regulator *regulator, int load_uA); +int regulator_allow_bypass(struct regulator *regulator, bool allow); +  /* regulator notifier block */  int regulator_register_notifier(struct regulator *regulator,  			      struct notifier_block *nb); @@ -328,6 +330,12 @@ static inline int regulator_set_optimum_mode(struct regulator *regulator,  	return REGULATOR_MODE_NORMAL;  } +static inline int regulator_allow_bypass(struct regulator *regulator, +					 bool allow) +{ +	return 0; +} +  static inline int regulator_register_notifier(struct regulator *regulator,  			      struct notifier_block *nb)  { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index bac4c871f3b..7274a469e8d 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -32,6 +32,8 @@ enum regulator_status {  	REGULATOR_STATUS_NORMAL,  	REGULATOR_STATUS_IDLE,  	REGULATOR_STATUS_STANDBY, +	/* The regulator is enabled but not regulating */ +	REGULATOR_STATUS_BYPASS,  	/* in case that any other status doesn't apply */  	REGULATOR_STATUS_UNDEFINED,  }; @@ -67,6 +69,9 @@ enum regulator_status {   * @get_optimum_mode: Get the most efficient operating mode for the regulator   *                    when running with the specified parameters.   * + * @set_bypass: Set the regulator in bypass mode. + * @get_bypass: Get the regulator bypass mode state. + *   * @enable_time: Time taken for the regulator voltage output voltage to   *               stabilise after being enabled, in microseconds.   * @set_ramp_delay: Set the ramp delay for the regulator. The driver should @@ -133,6 +138,10 @@ struct regulator_ops {  	unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,  					  int output_uV, int load_uA); +	/* control and report on bypass mode */ +	int (*set_bypass)(struct regulator_dev *dev, bool enable); +	int (*get_bypass)(struct regulator_dev *dev, bool *enable); +  	/* the operations below are for configuration of regulator state when  	 * its parent PMIC enters a global STANDBY/HIBERNATE state */ @@ -205,6 +214,8 @@ struct regulator_desc {  	unsigned int vsel_mask;  	unsigned int enable_reg;  	unsigned int enable_mask; +	unsigned int bypass_reg; +	unsigned int bypass_mask;  	unsigned int enable_time;  }; @@ -253,6 +264,7 @@ struct regulator_dev {  	int exclusive;  	u32 use_count;  	u32 open_count; +	u32 bypass_count;  	/* lists we belong to */  	struct list_head list; /* list of all regulators */ @@ -310,6 +322,8 @@ int regulator_disable_regmap(struct regulator_dev *rdev);  int regulator_set_voltage_time_sel(struct regulator_dev *rdev,  				   unsigned int old_selector,  				   unsigned int new_selector); +int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable); +int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable);  void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 40dd0a394cf..36adbc82de6 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -32,6 +32,7 @@ struct regulator;   *           board/machine.   * STATUS:   Regulator can be enabled and disabled.   * DRMS:     Dynamic Regulator Mode Switching is enabled for this regulator. + * BYPASS:   Regulator can be put into bypass mode   */  #define REGULATOR_CHANGE_VOLTAGE	0x1 @@ -39,6 +40,7 @@ struct regulator;  #define REGULATOR_CHANGE_MODE		0x4  #define REGULATOR_CHANGE_STATUS		0x8  #define REGULATOR_CHANGE_DRMS		0x10 +#define REGULATOR_CHANGE_BYPASS		0x20  /**   * struct regulator_state - regulator state during low power system states diff --git a/include/sound/da9055.h b/include/sound/da9055.h new file mode 100644 index 00000000000..cf1241b64d8 --- /dev/null +++ b/include/sound/da9055.h @@ -0,0 +1,33 @@ +/* + * DA9055 ALSA Soc codec driver + * + * Copyright (c) 2012 Dialog Semiconductor + * + * Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C + * Written by David Chen <david.chen@diasemi.com> and + * Ashish Chavan <ashish.chavan@kpitcummins.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __SOUND_DA9055_H__ +#define __SOUND_DA9055_H__ + +enum da9055_micbias_voltage { +	DA9055_MICBIAS_1_6V = 0, +	DA9055_MICBIAS_1_8V = 1, +	DA9055_MICBIAS_2_1V = 2, +	DA9055_MICBIAS_2_2V = 3, +}; + +struct da9055_platform_data { +	/* Selects which of the two MicBias pins acts as the bias source */ +	bool micbias_source; +	/* Selects the micbias voltage */ +	enum da9055_micbias_voltage micbias; +}; + +#endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c96bf5ae80a..e1ef63d4a5c 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -320,6 +320,9 @@ struct device;  #define SND_SOC_DAPM_EVENT_OFF(e)	\  	(e & (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD)) +/* regulator widget flags */ +#define SND_SOC_DAPM_REGULATOR_BYPASS     0x1     /* bypass when disabled */ +  struct snd_soc_dapm_widget;  enum snd_soc_dapm_type;  struct snd_soc_dapm_path; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3684255e5fb..b92759a3936 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -37,6 +37,7 @@ config SND_SOC_ALL_CODECS  	select SND_SOC_CX20442  	select SND_SOC_DA7210 if I2C  	select SND_SOC_DA732X if I2C +	select SND_SOC_DA9055 if I2C  	select SND_SOC_DFBMCS320  	select SND_SOC_ISABELLE if I2C  	select SND_SOC_JZ4740_CODEC @@ -239,6 +240,9 @@ config SND_SOC_DA7210  config SND_SOC_DA732X          tristate +config SND_SOC_DA9055 +	tristate +  config SND_SOC_DFBMCS320  	tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index ca508b251df..9bd4d95aab4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -24,6 +24,7 @@ snd-soc-cs4271-objs := cs4271.o  snd-soc-cx20442-objs := cx20442.o  snd-soc-da7210-objs := da7210.o  snd-soc-da732x-objs := da732x.o +snd-soc-da9055-objs := da9055.o  snd-soc-dfbmcs320-objs := dfbmcs320.o  snd-soc-dmic-objs := dmic.o  snd-soc-isabelle-objs := isabelle.o @@ -144,6 +145,7 @@ obj-$(CONFIG_SND_SOC_CS4271)	+= snd-soc-cs4271.o  obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o  obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o  obj-$(CONFIG_SND_SOC_DA732X)	+= snd-soc-da732x.o +obj-$(CONFIG_SND_SOC_DA9055)	+= snd-soc-da9055.o  obj-$(CONFIG_SND_SOC_DFBMCS320)	+= snd-soc-dfbmcs320.o  obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o  obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 2c1c2524ef8..af547490b4f 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -34,6 +34,7 @@  #include <linux/mfd/abx500/ab8500-sysctrl.h>  #include <linux/mfd/abx500/ab8500-codec.h>  #include <linux/regulator/consumer.h> +#include <linux/of.h>  #include <sound/core.h>  #include <sound/pcm.h> @@ -2394,9 +2395,65 @@ struct snd_soc_dai_driver ab8500_codec_dai[] = {  	}  }; +static void ab8500_codec_of_probe(struct device *dev, struct device_node *np, +				struct ab8500_codec_platform_data *codec) +{ +	u32 value; + +	if (of_get_property(np, "stericsson,amic1-type-single-ended", NULL)) +		codec->amics.mic1_type = AMIC_TYPE_SINGLE_ENDED; +	else +		codec->amics.mic1_type = AMIC_TYPE_DIFFERENTIAL; + +	if (of_get_property(np, "stericsson,amic2-type-single-ended", NULL)) +		codec->amics.mic2_type = AMIC_TYPE_SINGLE_ENDED; +	else +		codec->amics.mic2_type = AMIC_TYPE_DIFFERENTIAL; + +	/* Has a non-standard Vamic been requested? */ +	if (of_get_property(np, "stericsson,amic1a-bias-vamic2", NULL)) +		codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC2; +	else +		codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC1; + +	if (of_get_property(np, "stericsson,amic1b-bias-vamic2", NULL)) +		codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC2; +	else +		codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC1; + +	if (of_get_property(np, "stericsson,amic2-bias-vamic1", NULL)) +		codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC1; +	else +		codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC2; + +	if (!of_property_read_u32(np, "stericsson,earpeice-cmv", &value)) { +		switch (value) { +		case 950 : +			codec->ear_cmv = EAR_CMV_0_95V; +			break; +		case 1100 : +			codec->ear_cmv = EAR_CMV_1_10V; +			break; +		case 1270 : +			codec->ear_cmv = EAR_CMV_1_27V; +			break; +		case 1580 : +			codec->ear_cmv = EAR_CMV_1_58V; +			break; +		default : +			codec->ear_cmv = EAR_CMV_UNKNOWN; +			dev_err(dev, "Unsuitable earpiece voltage found in DT\n"); +		} +	} else { +		dev_warn(dev, "No earpiece voltage found in DT - using default\n"); +		codec->ear_cmv = EAR_CMV_0_95V; +	} +} +  static int ab8500_codec_probe(struct snd_soc_codec *codec)  {  	struct device *dev = codec->dev; +	struct device_node *np = dev->of_node;  	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);  	struct ab8500_platform_data *pdata;  	struct filter_control *fc; @@ -2407,6 +2464,30 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)  	/* Setup AB8500 according to board-settings */  	pdata = dev_get_platdata(dev->parent); +	if (np) { +		if (!pdata) +			pdata = devm_kzalloc(dev, +					sizeof(struct ab8500_platform_data), +					GFP_KERNEL); + +		if (pdata && !pdata->codec) +			pdata->codec +				= devm_kzalloc(dev, +					sizeof(struct ab8500_codec_platform_data), +					GFP_KERNEL); + +		if (!(pdata && pdata->codec)) +			return -ENOMEM; + +		ab8500_codec_of_probe(dev, np, pdata->codec); + +	} else { +		if (!(pdata && pdata->codec)) { +			dev_err(dev, "No codec platform data or DT found\n"); +			return -EINVAL; +		} +	} +  	status = ab8500_audio_setup_mics(codec, &pdata->codec->amics);  	if (status < 0) {  		pr_err("%s: Failed to setup mics (%d)!\n", __func__, status); diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index c167c896eae..c03b65af305 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -119,6 +119,24 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {  	"DSP1.4",  	"DSP1.5",  	"DSP1.6", +	"DSP2.1", +	"DSP2.2", +	"DSP2.3", +	"DSP2.4", +	"DSP2.5", +	"DSP2.6", +	"DSP3.1", +	"DSP3.2", +	"DSP3.3", +	"DSP3.4", +	"DSP3.5", +	"DSP3.6", +	"DSP4.1", +	"DSP4.2", +	"DSP4.3", +	"DSP4.4", +	"DSP4.5", +	"DSP4.6",  	"ASRC1L",  	"ASRC1R",  	"ASRC2L", @@ -180,6 +198,24 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {  	0x6b,  	0x6c,  	0x6d, +	0x70,  /* DSP2.1 */ +	0x71, +	0x72, +	0x73, +	0x74, +	0x75, +	0x78,  /* DSP3.1 */ +	0x79, +	0x7a, +	0x7b, +	0x7c, +	0x7d, +	0x80,  /* DSP4.1 */ +	0x81, +	0x82, +	0x83, +	0x84, +	0x85,  	0x90,  /* ASRC1L */  	0x91,  	0x92, @@ -234,6 +270,9 @@ static unsigned int arizona_sysclk_48k_rates[] = {  	12288000,  	22579200,  	49152000, +	73728000, +	98304000, +	147456000,  };  static unsigned int arizona_sysclk_44k1_rates[] = { @@ -241,6 +280,9 @@ static unsigned int arizona_sysclk_44k1_rates[] = {  	11289600,  	24576000,  	45158400, +	67737600, +	90316800, +	135475200,  };  static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk, diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index eb66b52777c..36ec6494612 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -61,7 +61,7 @@ struct arizona_priv {  	struct arizona_dai_priv dai[ARIZONA_MAX_DAI];  }; -#define ARIZONA_NUM_MIXER_INPUTS 57 +#define ARIZONA_NUM_MIXER_INPUTS 75  extern const unsigned int arizona_mixer_tlv[];  extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 815b53bc2d2..8e4779812b9 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -459,7 +459,7 @@ static struct snd_soc_dai_driver cs4270_dai = {  	.name = "cs4270-hifi",  	.playback = {  		.stream_name = "Playback", -		.channels_min = 1, +		.channels_min = 2,  		.channels_max = 2,  		.rates = SNDRV_PCM_RATE_CONTINUOUS,  		.rate_min = 4000, @@ -468,7 +468,7 @@ static struct snd_soc_dai_driver cs4270_dai = {  	},  	.capture = {  		.stream_name = "Capture", -		.channels_min = 1, +		.channels_min = 2,  		.channels_max = 2,  		.rates = SNDRV_PCM_RATE_CONTINUOUS,  		.rate_min = 4000, diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 9eb01d7d58a..f994af34f55 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -22,12 +22,14 @@  #include <linux/module.h>  #include <linux/slab.h>  #include <linux/delay.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include <sound/tlv.h>  #include <linux/gpio.h>  #include <linux/i2c.h>  #include <linux/spi/spi.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/tlv.h>  #include <sound/cs4271.h>  #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -458,6 +460,14 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec)  #define cs4271_soc_resume	NULL  #endif /* CONFIG_PM */ +#ifdef CONFIG_OF +static const struct of_device_id cs4271_dt_ids[] = { +	{ .compatible = "cirrus,cs4271", }, +	{ } +}; +MODULE_DEVICE_TABLE(of, cs4271_dt_ids); +#endif +  static int cs4271_probe(struct snd_soc_codec *codec)  {  	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); @@ -465,6 +475,12 @@ static int cs4271_probe(struct snd_soc_codec *codec)  	int ret;  	int gpio_nreset = -EINVAL; +#ifdef CONFIG_OF +	if (of_match_device(cs4271_dt_ids, codec->dev)) +		gpio_nreset = of_get_named_gpio(codec->dev->of_node, +						"reset-gpio", 0); +#endif +  	if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))  		gpio_nreset = cs4271plat->gpio_nreset; @@ -569,6 +585,7 @@ static struct spi_driver cs4271_spi_driver = {  	.driver = {  		.name	= "cs4271",  		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(cs4271_dt_ids),  	},  	.probe		= cs4271_spi_probe,  	.remove		= __devexit_p(cs4271_spi_remove), @@ -608,6 +625,7 @@ static struct i2c_driver cs4271_i2c_driver = {  	.driver = {  		.name	= "cs4271",  		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(cs4271_dt_ids),  	},  	.id_table	= cs4271_i2c_id,  	.probe		= cs4271_i2c_probe, diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c new file mode 100644 index 00000000000..185d8dd3639 --- /dev/null +++ b/sound/soc/codecs/da9055.c @@ -0,0 +1,1510 @@ +/* + * DA9055 ALSA Soc codec driver + * + * Copyright (c) 2012 Dialog Semiconductor + * + * Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C + * Written by David Chen <david.chen@diasemi.com> and + * Ashish Chavan <ashish.chavan@kpitcummins.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/da9055.h> + +/* DA9055 register space */ + +/* Status Registers */ +#define DA9055_STATUS1			0x02 +#define DA9055_PLL_STATUS		0x03 +#define DA9055_AUX_L_GAIN_STATUS	0x04 +#define DA9055_AUX_R_GAIN_STATUS	0x05 +#define DA9055_MIC_L_GAIN_STATUS	0x06 +#define DA9055_MIC_R_GAIN_STATUS	0x07 +#define DA9055_MIXIN_L_GAIN_STATUS	0x08 +#define DA9055_MIXIN_R_GAIN_STATUS	0x09 +#define DA9055_ADC_L_GAIN_STATUS	0x0A +#define DA9055_ADC_R_GAIN_STATUS	0x0B +#define DA9055_DAC_L_GAIN_STATUS	0x0C +#define DA9055_DAC_R_GAIN_STATUS	0x0D +#define DA9055_HP_L_GAIN_STATUS		0x0E +#define DA9055_HP_R_GAIN_STATUS		0x0F +#define DA9055_LINE_GAIN_STATUS		0x10 + +/* System Initialisation Registers */ +#define DA9055_CIF_CTRL			0x20 +#define DA9055_DIG_ROUTING_AIF		0X21 +#define DA9055_SR			0x22 +#define DA9055_REFERENCES		0x23 +#define DA9055_PLL_FRAC_TOP		0x24 +#define DA9055_PLL_FRAC_BOT		0x25 +#define DA9055_PLL_INTEGER		0x26 +#define DA9055_PLL_CTRL			0x27 +#define DA9055_AIF_CLK_MODE		0x28 +#define DA9055_AIF_CTRL			0x29 +#define DA9055_DIG_ROUTING_DAC		0x2A +#define DA9055_ALC_CTRL1		0x2B + +/* Input - Gain, Select and Filter Registers */ +#define DA9055_AUX_L_GAIN		0x30 +#define DA9055_AUX_R_GAIN		0x31 +#define DA9055_MIXIN_L_SELECT		0x32 +#define DA9055_MIXIN_R_SELECT		0x33 +#define DA9055_MIXIN_L_GAIN		0x34 +#define DA9055_MIXIN_R_GAIN		0x35 +#define DA9055_ADC_L_GAIN		0x36 +#define DA9055_ADC_R_GAIN		0x37 +#define DA9055_ADC_FILTERS1		0x38 +#define DA9055_MIC_L_GAIN		0x39 +#define DA9055_MIC_R_GAIN		0x3A + +/* Output - Gain, Select and Filter Registers */ +#define DA9055_DAC_FILTERS5		0x40 +#define DA9055_DAC_FILTERS2		0x41 +#define DA9055_DAC_FILTERS3		0x42 +#define DA9055_DAC_FILTERS4		0x43 +#define DA9055_DAC_FILTERS1		0x44 +#define DA9055_DAC_L_GAIN		0x45 +#define DA9055_DAC_R_GAIN		0x46 +#define DA9055_CP_CTRL			0x47 +#define DA9055_HP_L_GAIN		0x48 +#define DA9055_HP_R_GAIN		0x49 +#define DA9055_LINE_GAIN		0x4A +#define DA9055_MIXOUT_L_SELECT		0x4B +#define DA9055_MIXOUT_R_SELECT		0x4C + +/* System Controller Registers */ +#define DA9055_SYSTEM_MODES_INPUT	0x50 +#define DA9055_SYSTEM_MODES_OUTPUT	0x51 + +/* Control Registers */ +#define DA9055_AUX_L_CTRL		0x60 +#define DA9055_AUX_R_CTRL		0x61 +#define DA9055_MIC_BIAS_CTRL		0x62 +#define DA9055_MIC_L_CTRL		0x63 +#define DA9055_MIC_R_CTRL		0x64 +#define DA9055_MIXIN_L_CTRL		0x65 +#define DA9055_MIXIN_R_CTRL		0x66 +#define DA9055_ADC_L_CTRL		0x67 +#define DA9055_ADC_R_CTRL		0x68 +#define DA9055_DAC_L_CTRL		0x69 +#define DA9055_DAC_R_CTRL		0x6A +#define DA9055_HP_L_CTRL		0x6B +#define DA9055_HP_R_CTRL		0x6C +#define DA9055_LINE_CTRL		0x6D +#define DA9055_MIXOUT_L_CTRL		0x6E +#define DA9055_MIXOUT_R_CTRL		0x6F + +/* Configuration Registers */ +#define DA9055_LDO_CTRL			0x90 +#define DA9055_IO_CTRL			0x91 +#define DA9055_GAIN_RAMP_CTRL		0x92 +#define DA9055_MIC_CONFIG		0x93 +#define DA9055_PC_COUNT			0x94 +#define DA9055_CP_VOL_THRESHOLD1	0x95 +#define DA9055_CP_DELAY			0x96 +#define DA9055_CP_DETECTOR		0x97 +#define DA9055_AIF_OFFSET		0x98 +#define DA9055_DIG_CTRL			0x99 +#define DA9055_ALC_CTRL2		0x9A +#define DA9055_ALC_CTRL3		0x9B +#define DA9055_ALC_NOISE		0x9C +#define DA9055_ALC_TARGET_MIN		0x9D +#define DA9055_ALC_TARGET_MAX		0x9E +#define DA9055_ALC_GAIN_LIMITS		0x9F +#define DA9055_ALC_ANA_GAIN_LIMITS	0xA0 +#define DA9055_ALC_ANTICLIP_CTRL	0xA1 +#define DA9055_ALC_ANTICLIP_LEVEL	0xA2 +#define DA9055_ALC_OFFSET_OP2M_L	0xA6 +#define DA9055_ALC_OFFSET_OP2U_L	0xA7 +#define DA9055_ALC_OFFSET_OP2M_R	0xAB +#define DA9055_ALC_OFFSET_OP2U_R	0xAC +#define DA9055_ALC_CIC_OP_LVL_CTRL	0xAD +#define DA9055_ALC_CIC_OP_LVL_DATA	0xAE +#define DA9055_DAC_NG_SETUP_TIME	0xAF +#define DA9055_DAC_NG_OFF_THRESHOLD	0xB0 +#define DA9055_DAC_NG_ON_THRESHOLD	0xB1 +#define DA9055_DAC_NG_CTRL		0xB2 + +/* SR bit fields */ +#define DA9055_SR_8000			(0x1 << 0) +#define DA9055_SR_11025			(0x2 << 0) +#define DA9055_SR_12000			(0x3 << 0) +#define DA9055_SR_16000			(0x5 << 0) +#define DA9055_SR_22050			(0x6 << 0) +#define DA9055_SR_24000			(0x7 << 0) +#define DA9055_SR_32000			(0x9 << 0) +#define DA9055_SR_44100			(0xA << 0) +#define DA9055_SR_48000			(0xB << 0) +#define DA9055_SR_88200			(0xE << 0) +#define DA9055_SR_96000			(0xF << 0) + +/* REFERENCES bit fields */ +#define DA9055_BIAS_EN			(1 << 3) +#define DA9055_VMID_EN			(1 << 7) + +/* PLL_CTRL bit fields */ +#define DA9055_PLL_INDIV_10_20_MHZ	(1 << 2) +#define DA9055_PLL_SRM_EN		(1 << 6) +#define DA9055_PLL_EN			(1 << 7) + +/* AIF_CLK_MODE bit fields */ +#define DA9055_AIF_BCLKS_PER_WCLK_32	(0 << 0) +#define DA9055_AIF_BCLKS_PER_WCLK_64	(1 << 0) +#define DA9055_AIF_BCLKS_PER_WCLK_128	(2 << 0) +#define DA9055_AIF_BCLKS_PER_WCLK_256	(3 << 0) +#define DA9055_AIF_CLK_EN_SLAVE_MODE	(0 << 7) +#define DA9055_AIF_CLK_EN_MASTER_MODE	(1 << 7) + +/* AIF_CTRL bit fields */ +#define DA9055_AIF_FORMAT_I2S_MODE	(0 << 0) +#define DA9055_AIF_FORMAT_LEFT_J	(1 << 0) +#define DA9055_AIF_FORMAT_RIGHT_J	(2 << 0) +#define DA9055_AIF_WORD_S16_LE		(0 << 2) +#define DA9055_AIF_WORD_S20_3LE		(1 << 2) +#define DA9055_AIF_WORD_S24_LE		(2 << 2) +#define DA9055_AIF_WORD_S32_LE		(3 << 2) + +/* MIXIN_L_CTRL bit fields */ +#define DA9055_MIXIN_L_MIX_EN		(1 << 3) + +/* MIXIN_R_CTRL bit fields */ +#define DA9055_MIXIN_R_MIX_EN		(1 << 3) + +/* ADC_L_CTRL bit fields */ +#define DA9055_ADC_L_EN			(1 << 7) + +/* ADC_R_CTRL bit fields */ +#define DA9055_ADC_R_EN			(1 << 7) + +/* DAC_L_CTRL bit fields */ +#define DA9055_DAC_L_MUTE_EN		(1 << 6) + +/* DAC_R_CTRL bit fields */ +#define DA9055_DAC_R_MUTE_EN		(1 << 6) + +/* HP_L_CTRL bit fields */ +#define DA9055_HP_L_AMP_OE		(1 << 3) + +/* HP_R_CTRL bit fields */ +#define DA9055_HP_R_AMP_OE		(1 << 3) + +/* LINE_CTRL bit fields */ +#define DA9055_LINE_AMP_OE		(1 << 3) + +/* MIXOUT_L_CTRL bit fields */ +#define DA9055_MIXOUT_L_MIX_EN		(1 << 3) + +/* MIXOUT_R_CTRL bit fields */ +#define DA9055_MIXOUT_R_MIX_EN		(1 << 3) + +/* MIC bias select bit fields */ +#define DA9055_MICBIAS2_EN		(1 << 6) + +/* ALC_CIC_OP_LEVEL_CTRL bit fields */ +#define DA9055_ALC_DATA_MIDDLE		(2 << 0) +#define DA9055_ALC_DATA_TOP		(3 << 0) +#define DA9055_ALC_CIC_OP_CHANNEL_LEFT	(0 << 7) +#define DA9055_ALC_CIC_OP_CHANNEL_RIGHT	(1 << 7) + +#define DA9055_AIF_BCLK_MASK		(3 << 0) +#define DA9055_AIF_CLK_MODE_MASK	(1 << 7) +#define DA9055_AIF_FORMAT_MASK		(3 << 0) +#define DA9055_AIF_WORD_LENGTH_MASK	(3 << 2) +#define DA9055_GAIN_RAMPING_EN		(1 << 5) +#define DA9055_MICBIAS_LEVEL_MASK	(3 << 4) + +#define DA9055_ALC_OFFSET_15_8		0x00FF00 +#define DA9055_ALC_OFFSET_17_16		0x030000 +#define DA9055_ALC_AVG_ITERATIONS	5 + +struct pll_div { +	int fref; +	int fout; +	u8 frac_top; +	u8 frac_bot; +	u8 integer; +	u8 mode;	/* 0 = slave, 1 = master */ +}; + +/* PLL divisor table */ +static const struct pll_div da9055_pll_div[] = { +	/* for MASTER mode, fs = 44.1Khz and its harmonics */ +	{11289600, 2822400, 0x00, 0x00, 0x20, 1},	/* MCLK=11.2896Mhz */ +	{12000000, 2822400, 0x03, 0x61, 0x1E, 1},	/* MCLK=12Mhz */ +	{12288000, 2822400, 0x0C, 0xCC, 0x1D, 1},	/* MCLK=12.288Mhz */ +	{13000000, 2822400, 0x19, 0x45, 0x1B, 1},	/* MCLK=13Mhz */ +	{13500000, 2822400, 0x18, 0x56, 0x1A, 1},	/* MCLK=13.5Mhz */ +	{14400000, 2822400, 0x02, 0xD0, 0x19, 1},	/* MCLK=14.4Mhz */ +	{19200000, 2822400, 0x1A, 0x1C, 0x12, 1},	/* MCLK=19.2Mhz */ +	{19680000, 2822400, 0x0B, 0x6D, 0x12, 1},	/* MCLK=19.68Mhz */ +	{19800000, 2822400, 0x07, 0xDD, 0x12, 1},	/* MCLK=19.8Mhz */ +	/* for MASTER mode, fs = 48Khz and its harmonics */ +	{11289600, 3072000, 0x1A, 0x8E, 0x22, 1},	/* MCLK=11.2896Mhz */ +	{12000000, 3072000, 0x18, 0x93, 0x20, 1},	/* MCLK=12Mhz */ +	{12288000, 3072000, 0x00, 0x00, 0x20, 1},	/* MCLK=12.288Mhz */ +	{13000000, 3072000, 0x07, 0xEA, 0x1E, 1},	/* MCLK=13Mhz */ +	{13500000, 3072000, 0x04, 0x11, 0x1D, 1},	/* MCLK=13.5Mhz */ +	{14400000, 3072000, 0x09, 0xD0, 0x1B, 1},	/* MCLK=14.4Mhz */ +	{19200000, 3072000, 0x0F, 0x5C, 0x14, 1},	/* MCLK=19.2Mhz */ +	{19680000, 3072000, 0x1F, 0x60, 0x13, 1},	/* MCLK=19.68Mhz */ +	{19800000, 3072000, 0x1B, 0x80, 0x13, 1},	/* MCLK=19.8Mhz */ +	/* for SLAVE mode with SRM */ +	{11289600, 2822400, 0x0D, 0x47, 0x21, 0},	/* MCLK=11.2896Mhz */ +	{12000000, 2822400, 0x0D, 0xFA, 0x1F, 0},	/* MCLK=12Mhz */ +	{12288000, 2822400, 0x16, 0x66, 0x1E, 0},	/* MCLK=12.288Mhz */ +	{13000000, 2822400, 0x00, 0x98, 0x1D, 0},	/* MCLK=13Mhz */ +	{13500000, 2822400, 0x1E, 0x33, 0x1B, 0},	/* MCLK=13.5Mhz */ +	{14400000, 2822400, 0x06, 0x50, 0x1A, 0},	/* MCLK=14.4Mhz */ +	{19200000, 2822400, 0x14, 0xBC, 0x13, 0},	/* MCLK=19.2Mhz */ +	{19680000, 2822400, 0x05, 0x66, 0x13, 0},	/* MCLK=19.68Mhz */ +	{19800000, 2822400, 0x01, 0xAE, 0x13, 0},	/* MCLK=19.8Mhz  */ +}; + +enum clk_src { +	DA9055_CLKSRC_MCLK +}; + +/* Gain and Volume */ + +static const unsigned int aux_vol_tlv[] = { +	TLV_DB_RANGE_HEAD(2), +	0x0, 0x10, TLV_DB_SCALE_ITEM(-5400, 0, 0), +	/* -54dB to 15dB */ +	0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0) +}; + +static const unsigned int digital_gain_tlv[] = { +	TLV_DB_RANGE_HEAD(2), +	0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), +	/* -78dB to 12dB */ +	0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0) +}; + +static const unsigned int alc_analog_gain_tlv[] = { +	TLV_DB_RANGE_HEAD(2), +	0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), +	/* 0dB to 36dB */ +	0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0) +}; + +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0); + +/* ADC and DAC high pass filter cutoff value */ +static const char * const da9055_hpf_cutoff_txt[] = { +	"Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000" +}; + +static const struct soc_enum da9055_dac_hpf_cutoff = +	SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt); + +static const struct soc_enum da9055_adc_hpf_cutoff = +	SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 4, 4, da9055_hpf_cutoff_txt); + +/* ADC and DAC voice mode (8kHz) high pass cutoff value */ +static const char * const da9055_vf_cutoff_txt[] = { +	"2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da9055_dac_vf_cutoff = +	SOC_ENUM_SINGLE(DA9055_DAC_FILTERS1, 0, 8, da9055_vf_cutoff_txt); + +static const struct soc_enum da9055_adc_vf_cutoff = +	SOC_ENUM_SINGLE(DA9055_ADC_FILTERS1, 0, 8, da9055_vf_cutoff_txt); + +/* Gain ramping rate value */ +static const char * const da9055_gain_ramping_txt[] = { +	"nominal rate", "nominal rate * 4", "nominal rate * 8", +	"nominal rate / 8" +}; + +static const struct soc_enum da9055_gain_ramping_rate = +	SOC_ENUM_SINGLE(DA9055_GAIN_RAMP_CTRL, 0, 4, da9055_gain_ramping_txt); + +/* DAC noise gate setup time value */ +static const char * const da9055_dac_ng_setup_time_txt[] = { +	"256 samples", "512 samples", "1024 samples", "2048 samples" +}; + +static const struct soc_enum da9055_dac_ng_setup_time = +	SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 0, 4, +			da9055_dac_ng_setup_time_txt); + +/* DAC noise gate rampup rate value */ +static const char * const da9055_dac_ng_rampup_txt[] = { +	"0.02 ms/dB", "0.16 ms/dB" +}; + +static const struct soc_enum da9055_dac_ng_rampup_rate = +	SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 2, 2, +			da9055_dac_ng_rampup_txt); + +/* DAC noise gate rampdown rate value */ +static const char * const da9055_dac_ng_rampdown_txt[] = { +	"0.64 ms/dB", "20.48 ms/dB" +}; + +static const struct soc_enum da9055_dac_ng_rampdown_rate = +	SOC_ENUM_SINGLE(DA9055_DAC_NG_SETUP_TIME, 3, 2, +			da9055_dac_ng_rampdown_txt); + +/* DAC soft mute rate value */ +static const char * const da9055_dac_soft_mute_rate_txt[] = { +	"1", "2", "4", "8", "16", "32", "64" +}; + +static const struct soc_enum da9055_dac_soft_mute_rate = +	SOC_ENUM_SINGLE(DA9055_DAC_FILTERS5, 4, 7, +			da9055_dac_soft_mute_rate_txt); + +/* DAC routing select */ +static const char * const da9055_dac_src_txt[] = { +	"ADC output left", "ADC output right", "AIF input left", +	"AIF input right" +}; + +static const struct soc_enum da9055_dac_l_src = +	SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 0, 4, da9055_dac_src_txt); + +static const struct soc_enum da9055_dac_r_src = +	SOC_ENUM_SINGLE(DA9055_DIG_ROUTING_DAC, 4, 4, da9055_dac_src_txt); + +/* MIC PGA Left source select */ +static const char * const da9055_mic_l_src_txt[] = { +	"MIC1_P_N", "MIC1_P", "MIC1_N", "MIC2_L" +}; + +static const struct soc_enum da9055_mic_l_src = +	SOC_ENUM_SINGLE(DA9055_MIXIN_L_SELECT, 4, 4, da9055_mic_l_src_txt); + +/* MIC PGA Right source select */ +static const char * const da9055_mic_r_src_txt[] = { +	"MIC2_R_L", "MIC2_R", "MIC2_L" +}; + +static const struct soc_enum da9055_mic_r_src = +	SOC_ENUM_SINGLE(DA9055_MIXIN_R_SELECT, 4, 3, da9055_mic_r_src_txt); + +/* ALC Input Signal Tracking rate select */ +static const char * const da9055_signal_tracking_rate_txt[] = { +	"1/4", "1/16", "1/256", "1/65536" +}; + +static const struct soc_enum da9055_integ_attack_rate = +	SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 4, 4, +			da9055_signal_tracking_rate_txt); + +static const struct soc_enum da9055_integ_release_rate = +	SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 6, 4, +			da9055_signal_tracking_rate_txt); + +/* ALC Attack Rate select */ +static const char * const da9055_attack_rate_txt[] = { +	"44/fs", "88/fs", "176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", +	"5632/fs", "11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static const struct soc_enum da9055_attack_rate = +	SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 0, 13, da9055_attack_rate_txt); + +/* ALC Release Rate select */ +static const char * const da9055_release_rate_txt[] = { +	"176/fs", "352/fs", "704/fs", "1408/fs", "2816/fs", "5632/fs", +	"11264/fs", "22528/fs", "45056/fs", "90112/fs", "180224/fs" +}; + +static const struct soc_enum da9055_release_rate = +	SOC_ENUM_SINGLE(DA9055_ALC_CTRL2, 4, 11, da9055_release_rate_txt); + +/* ALC Hold Time select */ +static const char * const da9055_hold_time_txt[] = { +	"62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs", +	"7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs", +	"253952/fs", "507904/fs", "1015808/fs", "2031616/fs" +}; + +static const struct soc_enum da9055_hold_time = +	SOC_ENUM_SINGLE(DA9055_ALC_CTRL3, 0, 16, da9055_hold_time_txt); + +static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val) +{ +	int mid_data, top_data; +	int sum = 0; +	u8 iteration; + +	for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS; +	     iteration++) { +		/* Select the left or right channel and capture data */ +		snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val); + +		/* Select middle 8 bits for read back from data register */ +		snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL, +			      reg_val | DA9055_ALC_DATA_MIDDLE); +		mid_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA); + +		/* Select top 8 bits for read back from data register */ +		snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL, +			      reg_val | DA9055_ALC_DATA_TOP); +		top_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA); + +		sum += ((mid_data << 8) | (top_data << 16)); +	} + +	return sum / DA9055_ALC_AVG_ITERATIONS; +} + +static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol, +			     struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +	u8 reg_val, adc_left, adc_right; +	int avg_left_data, avg_right_data, offset_l, offset_r; + +	if (ucontrol->value.integer.value[0]) { +		/* +		 * While enabling ALC (or ALC sync mode), calibration of the DC +		 * offsets must be done first +		 */ + +		/* Save current values from ADC control registers */ +		adc_left = snd_soc_read(codec, DA9055_ADC_L_CTRL); +		adc_right = snd_soc_read(codec, DA9055_ADC_R_CTRL); + +		/* Enable ADC Left and Right */ +		snd_soc_update_bits(codec, DA9055_ADC_L_CTRL, +				    DA9055_ADC_L_EN, DA9055_ADC_L_EN); +		snd_soc_update_bits(codec, DA9055_ADC_R_CTRL, +				    DA9055_ADC_R_EN, DA9055_ADC_R_EN); + +		/* Calculate average for Left and Right data */ +		/* Left Data */ +		avg_left_data = da9055_get_alc_data(codec, +				DA9055_ALC_CIC_OP_CHANNEL_LEFT); +		/* Right Data */ +		avg_right_data = da9055_get_alc_data(codec, +				 DA9055_ALC_CIC_OP_CHANNEL_RIGHT); + +		/* Calculate DC offset */ +		offset_l = -avg_left_data; +		offset_r = -avg_right_data; + +		reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8; +		snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_L, reg_val); +		reg_val = (offset_l & DA9055_ALC_OFFSET_17_16) >> 16; +		snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_L, reg_val); + +		reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8; +		snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_R, reg_val); +		reg_val = (offset_r & DA9055_ALC_OFFSET_17_16) >> 16; +		snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_R, reg_val); + +		/* Restore original values of ADC control registers */ +		snd_soc_write(codec, DA9055_ADC_L_CTRL, adc_left); +		snd_soc_write(codec, DA9055_ADC_R_CTRL, adc_right); +	} + +	return snd_soc_put_volsw(kcontrol, ucontrol); +} + +static const struct snd_kcontrol_new da9055_snd_controls[] = { + +	/* Volume controls */ +	SOC_DOUBLE_R_TLV("Mic Volume", +			 DA9055_MIC_L_GAIN, DA9055_MIC_R_GAIN, +			 0, 0x7, 0, mic_vol_tlv), +	SOC_DOUBLE_R_TLV("Aux Volume", +			 DA9055_AUX_L_GAIN, DA9055_AUX_R_GAIN, +			 0, 0x3f, 0, aux_vol_tlv), +	SOC_DOUBLE_R_TLV("Mixin PGA Volume", +			 DA9055_MIXIN_L_GAIN, DA9055_MIXIN_R_GAIN, +			 0, 0xf, 0, mixin_gain_tlv), +	SOC_DOUBLE_R_TLV("ADC Volume", +			 DA9055_ADC_L_GAIN, DA9055_ADC_R_GAIN, +			 0, 0x7f, 0, digital_gain_tlv), + +	SOC_DOUBLE_R_TLV("DAC Volume", +			 DA9055_DAC_L_GAIN, DA9055_DAC_R_GAIN, +			 0, 0x7f, 0, digital_gain_tlv), +	SOC_DOUBLE_R_TLV("Headphone Volume", +			 DA9055_HP_L_GAIN, DA9055_HP_R_GAIN, +			 0, 0x3f, 0, hp_vol_tlv), +	SOC_SINGLE_TLV("Lineout Volume", DA9055_LINE_GAIN, 0, 0x3f, 0, +		       lineout_vol_tlv), + +	/* DAC Equalizer controls */ +	SOC_SINGLE("DAC EQ Switch", DA9055_DAC_FILTERS4, 7, 1, 0), +	SOC_SINGLE_TLV("DAC EQ1 Volume", DA9055_DAC_FILTERS2, 0, 0xf, 0, +		       eq_gain_tlv), +	SOC_SINGLE_TLV("DAC EQ2 Volume", DA9055_DAC_FILTERS2, 4, 0xf, 0, +		       eq_gain_tlv), +	SOC_SINGLE_TLV("DAC EQ3 Volume", DA9055_DAC_FILTERS3, 0, 0xf, 0, +		       eq_gain_tlv), +	SOC_SINGLE_TLV("DAC EQ4 Volume", DA9055_DAC_FILTERS3, 4, 0xf, 0, +		       eq_gain_tlv), +	SOC_SINGLE_TLV("DAC EQ5 Volume", DA9055_DAC_FILTERS4, 0, 0xf, 0, +		       eq_gain_tlv), + +	/* High Pass Filter and Voice Mode controls */ +	SOC_SINGLE("ADC HPF Switch", DA9055_ADC_FILTERS1, 7, 1, 0), +	SOC_ENUM("ADC HPF Cutoff", da9055_adc_hpf_cutoff), +	SOC_SINGLE("ADC Voice Mode Switch", DA9055_ADC_FILTERS1, 3, 1, 0), +	SOC_ENUM("ADC Voice Cutoff", da9055_adc_vf_cutoff), + +	SOC_SINGLE("DAC HPF Switch", DA9055_DAC_FILTERS1, 7, 1, 0), +	SOC_ENUM("DAC HPF Cutoff", da9055_dac_hpf_cutoff), +	SOC_SINGLE("DAC Voice Mode Switch", DA9055_DAC_FILTERS1, 3, 1, 0), +	SOC_ENUM("DAC Voice Cutoff", da9055_dac_vf_cutoff), + +	/* Mute controls */ +	SOC_DOUBLE_R("Mic Switch", DA9055_MIC_L_CTRL, +		     DA9055_MIC_R_CTRL, 6, 1, 0), +	SOC_DOUBLE_R("Aux Switch", DA9055_AUX_L_CTRL, +		     DA9055_AUX_R_CTRL, 6, 1, 0), +	SOC_DOUBLE_R("Mixin PGA Switch", DA9055_MIXIN_L_CTRL, +		     DA9055_MIXIN_R_CTRL, 6, 1, 0), +	SOC_DOUBLE_R("ADC Switch", DA9055_ADC_L_CTRL, +		     DA9055_ADC_R_CTRL, 6, 1, 0), +	SOC_DOUBLE_R("Headphone Switch", DA9055_HP_L_CTRL, +		     DA9055_HP_R_CTRL, 6, 1, 0), +	SOC_SINGLE("Lineout Switch", DA9055_LINE_CTRL, 6, 1, 0), +	SOC_SINGLE("DAC Soft Mute Switch", DA9055_DAC_FILTERS5, 7, 1, 0), +	SOC_ENUM("DAC Soft Mute Rate", da9055_dac_soft_mute_rate), + +	/* Zero Cross controls */ +	SOC_DOUBLE_R("Aux ZC Switch", DA9055_AUX_L_CTRL, +		     DA9055_AUX_R_CTRL, 4, 1, 0), +	SOC_DOUBLE_R("Mixin PGA ZC Switch", DA9055_MIXIN_L_CTRL, +		     DA9055_MIXIN_R_CTRL, 4, 1, 0), +	SOC_DOUBLE_R("Headphone ZC Switch", DA9055_HP_L_CTRL, +		     DA9055_HP_R_CTRL, 4, 1, 0), +	SOC_SINGLE("Lineout ZC Switch", DA9055_LINE_CTRL, 4, 1, 0), + +	/* Gain Ramping controls */ +	SOC_DOUBLE_R("Aux Gain Ramping Switch", DA9055_AUX_L_CTRL, +		     DA9055_AUX_R_CTRL, 5, 1, 0), +	SOC_DOUBLE_R("Mixin Gain Ramping Switch", DA9055_MIXIN_L_CTRL, +		     DA9055_MIXIN_R_CTRL, 5, 1, 0), +	SOC_DOUBLE_R("ADC Gain Ramping Switch", DA9055_ADC_L_CTRL, +		     DA9055_ADC_R_CTRL, 5, 1, 0), +	SOC_DOUBLE_R("DAC Gain Ramping Switch", DA9055_DAC_L_CTRL, +		     DA9055_DAC_R_CTRL, 5, 1, 0), +	SOC_DOUBLE_R("Headphone Gain Ramping Switch", DA9055_HP_L_CTRL, +		     DA9055_HP_R_CTRL, 5, 1, 0), +	SOC_SINGLE("Lineout Gain Ramping Switch", DA9055_LINE_CTRL, 5, 1, 0), +	SOC_ENUM("Gain Ramping Rate", da9055_gain_ramping_rate), + +	/* DAC Noise Gate controls */ +	SOC_SINGLE("DAC NG Switch", DA9055_DAC_NG_CTRL, 7, 1, 0), +	SOC_SINGLE("DAC NG ON Threshold", DA9055_DAC_NG_ON_THRESHOLD, +		   0, 0x7, 0), +	SOC_SINGLE("DAC NG OFF Threshold", DA9055_DAC_NG_OFF_THRESHOLD, +		   0, 0x7, 0), +	SOC_ENUM("DAC NG Setup Time", da9055_dac_ng_setup_time), +	SOC_ENUM("DAC NG Rampup Rate", da9055_dac_ng_rampup_rate), +	SOC_ENUM("DAC NG Rampdown Rate", da9055_dac_ng_rampdown_rate), + +	/* DAC Invertion control */ +	SOC_SINGLE("DAC Left Invert", DA9055_DIG_CTRL, 3, 1, 0), +	SOC_SINGLE("DAC Right Invert", DA9055_DIG_CTRL, 7, 1, 0), + +	/* DMIC controls */ +	SOC_DOUBLE_R("DMIC Switch", DA9055_MIXIN_L_SELECT, +		     DA9055_MIXIN_R_SELECT, 7, 1, 0), + +	/* ALC Controls */ +	SOC_DOUBLE_EXT("ALC Switch", DA9055_ALC_CTRL1, 3, 7, 1, 0, +		       snd_soc_get_volsw, da9055_put_alc_sw), +	SOC_SINGLE_EXT("ALC Sync Mode Switch", DA9055_ALC_CTRL1, 1, 1, 0, +		       snd_soc_get_volsw, da9055_put_alc_sw), +	SOC_SINGLE("ALC Offset Switch", DA9055_ALC_CTRL1, 0, 1, 0), +	SOC_SINGLE("ALC Anticlip Mode Switch", DA9055_ALC_ANTICLIP_CTRL, +		   7, 1, 0), +	SOC_SINGLE("ALC Anticlip Level", DA9055_ALC_ANTICLIP_LEVEL, +		   0, 0x7f, 0), +	SOC_SINGLE_TLV("ALC Min Threshold Volume", DA9055_ALC_TARGET_MIN, +		       0, 0x3f, 1, alc_threshold_tlv), +	SOC_SINGLE_TLV("ALC Max Threshold Volume", DA9055_ALC_TARGET_MAX, +		       0, 0x3f, 1, alc_threshold_tlv), +	SOC_SINGLE_TLV("ALC Noise Threshold Volume", DA9055_ALC_NOISE, +		       0, 0x3f, 1, alc_threshold_tlv), +	SOC_SINGLE_TLV("ALC Max Gain Volume", DA9055_ALC_GAIN_LIMITS, +		       4, 0xf, 0, alc_gain_tlv), +	SOC_SINGLE_TLV("ALC Max Attenuation Volume", DA9055_ALC_GAIN_LIMITS, +		       0, 0xf, 0, alc_gain_tlv), +	SOC_SINGLE_TLV("ALC Min Analog Gain Volume", +		       DA9055_ALC_ANA_GAIN_LIMITS, +		       0, 0x7, 0, alc_analog_gain_tlv), +	SOC_SINGLE_TLV("ALC Max Analog Gain Volume", +		       DA9055_ALC_ANA_GAIN_LIMITS, +		       4, 0x7, 0, alc_analog_gain_tlv), +	SOC_ENUM("ALC Attack Rate", da9055_attack_rate), +	SOC_ENUM("ALC Release Rate", da9055_release_rate), +	SOC_ENUM("ALC Hold Time", da9055_hold_time), +	/* +	 * Rate at which input signal envelope is tracked as the signal gets +	 * larger +	 */ +	SOC_ENUM("ALC Integ Attack Rate", da9055_integ_attack_rate), +	/* +	 * Rate at which input signal envelope is tracked as the signal gets +	 * smaller +	 */ +	SOC_ENUM("ALC Integ Release Rate", da9055_integ_release_rate), +}; + +/* DAPM Controls */ + +/* Mic PGA Left Source */ +static const struct snd_kcontrol_new da9055_mic_l_mux_controls = +SOC_DAPM_ENUM("Route", da9055_mic_l_src); + +/* Mic PGA Right Source */ +static const struct snd_kcontrol_new da9055_mic_r_mux_controls = +SOC_DAPM_ENUM("Route", da9055_mic_r_src); + +/* In Mixer Left */ +static const struct snd_kcontrol_new da9055_dapm_mixinl_controls[] = { +	SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXIN_L_SELECT, 0, 1, 0), +	SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_L_SELECT, 1, 1, 0), +	SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_L_SELECT, 2, 1, 0), +}; + +/* In Mixer Right */ +static const struct snd_kcontrol_new da9055_dapm_mixinr_controls[] = { +	SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXIN_R_SELECT, 0, 1, 0), +	SOC_DAPM_SINGLE("Mic Right Switch", DA9055_MIXIN_R_SELECT, 1, 1, 0), +	SOC_DAPM_SINGLE("Mic Left Switch", DA9055_MIXIN_R_SELECT, 2, 1, 0), +	SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXIN_R_SELECT, 3, 1, 0), +}; + +/* DAC Left Source */ +static const struct snd_kcontrol_new da9055_dac_l_mux_controls = +SOC_DAPM_ENUM("Route", da9055_dac_l_src); + +/* DAC Right Source */ +static const struct snd_kcontrol_new da9055_dac_r_mux_controls = +SOC_DAPM_ENUM("Route", da9055_dac_r_src); + +/* Out Mixer Left */ +static const struct snd_kcontrol_new da9055_dapm_mixoutl_controls[] = { +	SOC_DAPM_SINGLE("Aux Left Switch", DA9055_MIXOUT_L_SELECT, 0, 1, 0), +	SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_L_SELECT, 1, 1, 0), +	SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_L_SELECT, 2, 1, 0), +	SOC_DAPM_SINGLE("DAC Left Switch", DA9055_MIXOUT_L_SELECT, 3, 1, 0), +	SOC_DAPM_SINGLE("Aux Left Invert Switch", DA9055_MIXOUT_L_SELECT, +			4, 1, 0), +	SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_L_SELECT, +			5, 1, 0), +	SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_L_SELECT, +			6, 1, 0), +}; + +/* Out Mixer Right */ +static const struct snd_kcontrol_new da9055_dapm_mixoutr_controls[] = { +	SOC_DAPM_SINGLE("Aux Right Switch", DA9055_MIXOUT_R_SELECT, 0, 1, 0), +	SOC_DAPM_SINGLE("Mixin Right Switch", DA9055_MIXOUT_R_SELECT, 1, 1, 0), +	SOC_DAPM_SINGLE("Mixin Left Switch", DA9055_MIXOUT_R_SELECT, 2, 1, 0), +	SOC_DAPM_SINGLE("DAC Right Switch", DA9055_MIXOUT_R_SELECT, 3, 1, 0), +	SOC_DAPM_SINGLE("Aux Right Invert Switch", DA9055_MIXOUT_R_SELECT, +			4, 1, 0), +	SOC_DAPM_SINGLE("Mixin Right Invert Switch", DA9055_MIXOUT_R_SELECT, +			5, 1, 0), +	SOC_DAPM_SINGLE("Mixin Left Invert Switch", DA9055_MIXOUT_R_SELECT, +			6, 1, 0), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = { +	/* Input Side */ + +	/* Input Lines */ +	SND_SOC_DAPM_INPUT("MIC1"), +	SND_SOC_DAPM_INPUT("MIC2"), +	SND_SOC_DAPM_INPUT("AUXL"), +	SND_SOC_DAPM_INPUT("AUXR"), + +	/* MUXs for Mic PGA source selection */ +	SND_SOC_DAPM_MUX("Mic Left Source", SND_SOC_NOPM, 0, 0, +			 &da9055_mic_l_mux_controls), +	SND_SOC_DAPM_MUX("Mic Right Source", SND_SOC_NOPM, 0, 0, +			 &da9055_mic_r_mux_controls), + +	/* Input PGAs */ +	SND_SOC_DAPM_PGA("Mic Left", DA9055_MIC_L_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Mic Right", DA9055_MIC_R_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Aux Left", DA9055_AUX_L_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Aux Right", DA9055_AUX_R_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("MIXIN Left", DA9055_MIXIN_L_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("MIXIN Right", DA9055_MIXIN_R_CTRL, 7, 0, NULL, 0), + +	SND_SOC_DAPM_SUPPLY("Mic Bias", DA9055_MIC_BIAS_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("AIF", DA9055_AIF_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_SUPPLY("Charge Pump", DA9055_CP_CTRL, 7, 0, NULL, 0), + +	/* Input Mixers */ +	SND_SOC_DAPM_MIXER("In Mixer Left", SND_SOC_NOPM, 0, 0, +			   &da9055_dapm_mixinl_controls[0], +			   ARRAY_SIZE(da9055_dapm_mixinl_controls)), +	SND_SOC_DAPM_MIXER("In Mixer Right", SND_SOC_NOPM, 0, 0, +			   &da9055_dapm_mixinr_controls[0], +			   ARRAY_SIZE(da9055_dapm_mixinr_controls)), + +	/* ADCs */ +	SND_SOC_DAPM_ADC("ADC Left", "Capture", DA9055_ADC_L_CTRL, 7, 0), +	SND_SOC_DAPM_ADC("ADC Right", "Capture", DA9055_ADC_R_CTRL, 7, 0), + +	/* Output Side */ + +	/* MUXs for DAC source selection */ +	SND_SOC_DAPM_MUX("DAC Left Source", SND_SOC_NOPM, 0, 0, +			 &da9055_dac_l_mux_controls), +	SND_SOC_DAPM_MUX("DAC Right Source", SND_SOC_NOPM, 0, 0, +			 &da9055_dac_r_mux_controls), + +	/* AIF input */ +	SND_SOC_DAPM_AIF_IN("AIFIN Left", "Playback", 0, SND_SOC_NOPM, 0, 0), +	SND_SOC_DAPM_AIF_IN("AIFIN Right", "Playback", 0, SND_SOC_NOPM, 0, 0), + +	/* DACs */ +	SND_SOC_DAPM_DAC("DAC Left", "Playback", DA9055_DAC_L_CTRL, 7, 0), +	SND_SOC_DAPM_DAC("DAC Right", "Playback", DA9055_DAC_R_CTRL, 7, 0), + +	/* Output Mixers */ +	SND_SOC_DAPM_MIXER("Out Mixer Left", SND_SOC_NOPM, 0, 0, +			   &da9055_dapm_mixoutl_controls[0], +			   ARRAY_SIZE(da9055_dapm_mixoutl_controls)), +	SND_SOC_DAPM_MIXER("Out Mixer Right", SND_SOC_NOPM, 0, 0, +			   &da9055_dapm_mixoutr_controls[0], +			   ARRAY_SIZE(da9055_dapm_mixoutr_controls)), + +	/* Output PGAs */ +	SND_SOC_DAPM_PGA("MIXOUT Left", DA9055_MIXOUT_L_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("MIXOUT Right", DA9055_MIXOUT_R_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Lineout", DA9055_LINE_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Headphone Left", DA9055_HP_L_CTRL, 7, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Headphone Right", DA9055_HP_R_CTRL, 7, 0, NULL, 0), + +	/* Output Lines */ +	SND_SOC_DAPM_OUTPUT("HPL"), +	SND_SOC_DAPM_OUTPUT("HPR"), +	SND_SOC_DAPM_OUTPUT("LINE"), +}; + +/* DAPM audio route definition */ +static const struct snd_soc_dapm_route da9055_audio_map[] = { +	/* Dest       Connecting Widget    source */ + +	/* Input path */ +	{"Mic Left Source", "MIC1_P_N", "MIC1"}, +	{"Mic Left Source", "MIC1_P", "MIC1"}, +	{"Mic Left Source", "MIC1_N", "MIC1"}, +	{"Mic Left Source", "MIC2_L", "MIC2"}, + +	{"Mic Right Source", "MIC2_R_L", "MIC2"}, +	{"Mic Right Source", "MIC2_R", "MIC2"}, +	{"Mic Right Source", "MIC2_L", "MIC2"}, + +	{"Mic Left", NULL, "Mic Left Source"}, +	{"Mic Right", NULL, "Mic Right Source"}, + +	{"Aux Left", NULL, "AUXL"}, +	{"Aux Right", NULL, "AUXR"}, + +	{"In Mixer Left", "Mic Left Switch", "Mic Left"}, +	{"In Mixer Left", "Mic Right Switch", "Mic Right"}, +	{"In Mixer Left", "Aux Left Switch", "Aux Left"}, + +	{"In Mixer Right", "Mic Right Switch", "Mic Right"}, +	{"In Mixer Right", "Mic Left Switch", "Mic Left"}, +	{"In Mixer Right", "Aux Right Switch", "Aux Right"}, +	{"In Mixer Right", "Mixin Left Switch", "MIXIN Left"}, + +	{"MIXIN Left", NULL, "In Mixer Left"}, +	{"ADC Left", NULL, "MIXIN Left"}, + +	{"MIXIN Right", NULL, "In Mixer Right"}, +	{"ADC Right", NULL, "MIXIN Right"}, + +	{"ADC Left", NULL, "AIF"}, +	{"ADC Right", NULL, "AIF"}, + +	/* Output path */ +	{"AIFIN Left", NULL, "AIF"}, +	{"AIFIN Right", NULL, "AIF"}, + +	{"DAC Left Source", "ADC output left", "ADC Left"}, +	{"DAC Left Source", "ADC output right", "ADC Right"}, +	{"DAC Left Source", "AIF input left", "AIFIN Left"}, +	{"DAC Left Source", "AIF input right", "AIFIN Right"}, + +	{"DAC Right Source", "ADC output left", "ADC Left"}, +	{"DAC Right Source", "ADC output right", "ADC Right"}, +	{"DAC Right Source", "AIF input left", "AIFIN Left"}, +	{"DAC Right Source", "AIF input right", "AIFIN Right"}, + +	{"DAC Left", NULL, "DAC Left Source"}, +	{"DAC Right", NULL, "DAC Right Source"}, + +	{"Out Mixer Left", "Aux Left Switch", "Aux Left"}, +	{"Out Mixer Left", "Mixin Left Switch", "MIXIN Left"}, +	{"Out Mixer Left", "Mixin Right Switch", "MIXIN Right"}, +	{"Out Mixer Left", "Aux Left Invert Switch", "Aux Left"}, +	{"Out Mixer Left", "Mixin Left Invert Switch", "MIXIN Left"}, +	{"Out Mixer Left", "Mixin Right Invert Switch", "MIXIN Right"}, +	{"Out Mixer Left", "DAC Left Switch", "DAC Left"}, + +	{"Out Mixer Right", "Aux Right Switch", "Aux Right"}, +	{"Out Mixer Right", "Mixin Right Switch", "MIXIN Right"}, +	{"Out Mixer Right", "Mixin Left Switch", "MIXIN Left"}, +	{"Out Mixer Right", "Aux Right Invert Switch", "Aux Right"}, +	{"Out Mixer Right", "Mixin Right Invert Switch", "MIXIN Right"}, +	{"Out Mixer Right", "Mixin Left Invert Switch", "MIXIN Left"}, +	{"Out Mixer Right", "DAC Right Switch", "DAC Right"}, + +	{"MIXOUT Left", NULL, "Out Mixer Left"}, +	{"Headphone Left", NULL, "MIXOUT Left"}, +	{"Headphone Left", NULL, "Charge Pump"}, +	{"HPL", NULL, "Headphone Left"}, + +	{"MIXOUT Right", NULL, "Out Mixer Right"}, +	{"Headphone Right", NULL, "MIXOUT Right"}, +	{"Headphone Right", NULL, "Charge Pump"}, +	{"HPR", NULL, "Headphone Right"}, + +	{"MIXOUT Right", NULL, "Out Mixer Right"}, +	{"Lineout", NULL, "MIXOUT Right"}, +	{"LINE", NULL, "Lineout"}, +}; + +/* Codec private data */ +struct da9055_priv { +	struct regmap *regmap; +	unsigned int mclk_rate; +	int master; +	struct da9055_platform_data *pdata; +}; + +static struct reg_default da9055_reg_defaults[] = { +	{ 0x21, 0x10 }, +	{ 0x22, 0x0A }, +	{ 0x23, 0x00 }, +	{ 0x24, 0x00 }, +	{ 0x25, 0x00 }, +	{ 0x26, 0x00 }, +	{ 0x27, 0x0C }, +	{ 0x28, 0x01 }, +	{ 0x29, 0x08 }, +	{ 0x2A, 0x32 }, +	{ 0x2B, 0x00 }, +	{ 0x30, 0x35 }, +	{ 0x31, 0x35 }, +	{ 0x32, 0x00 }, +	{ 0x33, 0x00 }, +	{ 0x34, 0x03 }, +	{ 0x35, 0x03 }, +	{ 0x36, 0x6F }, +	{ 0x37, 0x6F }, +	{ 0x38, 0x80 }, +	{ 0x39, 0x01 }, +	{ 0x3A, 0x01 }, +	{ 0x40, 0x00 }, +	{ 0x41, 0x88 }, +	{ 0x42, 0x88 }, +	{ 0x43, 0x08 }, +	{ 0x44, 0x80 }, +	{ 0x45, 0x6F }, +	{ 0x46, 0x6F }, +	{ 0x47, 0x61 }, +	{ 0x48, 0x35 }, +	{ 0x49, 0x35 }, +	{ 0x4A, 0x35 }, +	{ 0x4B, 0x00 }, +	{ 0x4C, 0x00 }, +	{ 0x60, 0x44 }, +	{ 0x61, 0x44 }, +	{ 0x62, 0x00 }, +	{ 0x63, 0x40 }, +	{ 0x64, 0x40 }, +	{ 0x65, 0x40 }, +	{ 0x66, 0x40 }, +	{ 0x67, 0x40 }, +	{ 0x68, 0x40 }, +	{ 0x69, 0x48 }, +	{ 0x6A, 0x40 }, +	{ 0x6B, 0x41 }, +	{ 0x6C, 0x40 }, +	{ 0x6D, 0x40 }, +	{ 0x6E, 0x10 }, +	{ 0x6F, 0x10 }, +	{ 0x90, 0x80 }, +	{ 0x92, 0x02 }, +	{ 0x93, 0x00 }, +	{ 0x99, 0x00 }, +	{ 0x9A, 0x00 }, +	{ 0x9B, 0x00 }, +	{ 0x9C, 0x3F }, +	{ 0x9D, 0x00 }, +	{ 0x9E, 0x3F }, +	{ 0x9F, 0xFF }, +	{ 0xA0, 0x71 }, +	{ 0xA1, 0x00 }, +	{ 0xA2, 0x00 }, +	{ 0xA6, 0x00 }, +	{ 0xA7, 0x00 }, +	{ 0xAB, 0x00 }, +	{ 0xAC, 0x00 }, +	{ 0xAD, 0x00 }, +	{ 0xAF, 0x08 }, +	{ 0xB0, 0x00 }, +	{ 0xB1, 0x00 }, +	{ 0xB2, 0x00 }, +}; + +static bool da9055_volatile_register(struct device *dev, +				     unsigned int reg) +{ +	switch (reg) { +	case DA9055_STATUS1: +	case DA9055_PLL_STATUS: +	case DA9055_AUX_L_GAIN_STATUS: +	case DA9055_AUX_R_GAIN_STATUS: +	case DA9055_MIC_L_GAIN_STATUS: +	case DA9055_MIC_R_GAIN_STATUS: +	case DA9055_MIXIN_L_GAIN_STATUS: +	case DA9055_MIXIN_R_GAIN_STATUS: +	case DA9055_ADC_L_GAIN_STATUS: +	case DA9055_ADC_R_GAIN_STATUS: +	case DA9055_DAC_L_GAIN_STATUS: +	case DA9055_DAC_R_GAIN_STATUS: +	case DA9055_HP_L_GAIN_STATUS: +	case DA9055_HP_R_GAIN_STATUS: +	case DA9055_LINE_GAIN_STATUS: +	case DA9055_ALC_CIC_OP_LVL_DATA: +		return 1; +	default: +		return 0; +	} +} + +/* Set DAI word length */ +static int da9055_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 da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec); +	u8 aif_ctrl, fs; +	u32 sysclk; + +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S16_LE: +		aif_ctrl = DA9055_AIF_WORD_S16_LE; +		break; +	case SNDRV_PCM_FORMAT_S20_3LE: +		aif_ctrl = DA9055_AIF_WORD_S20_3LE; +		break; +	case SNDRV_PCM_FORMAT_S24_LE: +		aif_ctrl = DA9055_AIF_WORD_S24_LE; +		break; +	case SNDRV_PCM_FORMAT_S32_LE: +		aif_ctrl = DA9055_AIF_WORD_S32_LE; +		break; +	default: +		return -EINVAL; +	} + +	/* Set AIF format */ +	snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK, +			    aif_ctrl); + +	switch (params_rate(params)) { +	case 8000: +		fs		= DA9055_SR_8000; +		sysclk		= 3072000; +		break; +	case 11025: +		fs		= DA9055_SR_11025; +		sysclk		= 2822400; +		break; +	case 12000: +		fs		= DA9055_SR_12000; +		sysclk		= 3072000; +		break; +	case 16000: +		fs		= DA9055_SR_16000; +		sysclk		= 3072000; +		break; +	case 22050: +		fs		= DA9055_SR_22050; +		sysclk		= 2822400; +		break; +	case 32000: +		fs		= DA9055_SR_32000; +		sysclk		= 3072000; +		break; +	case 44100: +		fs		= DA9055_SR_44100; +		sysclk		= 2822400; +		break; +	case 48000: +		fs		= DA9055_SR_48000; +		sysclk		= 3072000; +		break; +	case 88200: +		fs		= DA9055_SR_88200; +		sysclk		= 2822400; +		break; +	case 96000: +		fs		= DA9055_SR_96000; +		sysclk		= 3072000; +		break; +	default: +		return -EINVAL; +	} + +	if (da9055->mclk_rate) { +		/* PLL Mode, Write actual FS */ +		snd_soc_write(codec, DA9055_SR, fs); +	} else { +		/* +		 * Non-PLL Mode +		 * When PLL is bypassed, chip assumes constant MCLK of +		 * 12.288MHz and uses sample rate value to divide this MCLK +		 * to derive its sys clk. As sys clk has to be 256 * Fs, we +		 * need to write constant sample rate i.e. 48KHz. +		 */ +		snd_soc_write(codec, DA9055_SR, DA9055_SR_48000); +	} + +	if (da9055->mclk_rate && (da9055->mclk_rate != sysclk)) { +		/* PLL Mode */ +		if (!da9055->master) { +			/* PLL slave mode, enable PLL and also SRM */ +			snd_soc_update_bits(codec, DA9055_PLL_CTRL, +					    DA9055_PLL_EN | DA9055_PLL_SRM_EN, +					    DA9055_PLL_EN | DA9055_PLL_SRM_EN); +		} else { +			/* PLL master mode, only enable PLL */ +			snd_soc_update_bits(codec, DA9055_PLL_CTRL, +					    DA9055_PLL_EN, DA9055_PLL_EN); +		} +	} else { +		/* Non PLL Mode, disable PLL */ +		snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0); +	} + +	return 0; +} + +/* Set DAI mode and Format */ +static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec); +	u8 aif_clk_mode, aif_ctrl, mode; + +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBM_CFM: +		/* DA9055 in I2S Master Mode */ +		mode = 1; +		aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE; +		break; +	case SND_SOC_DAIFMT_CBS_CFS: +		/* DA9055 in I2S Slave Mode */ +		mode = 0; +		aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE; +		break; +	default: +		return -EINVAL; +	} + +	/* Don't allow change of mode if PLL is enabled */ +	if ((snd_soc_read(codec, DA9055_PLL_CTRL) & DA9055_PLL_EN) && +	    (da9055->master != mode)) +		return -EINVAL; + +	da9055->master = mode; + +	/* Only I2S is supported */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_I2S: +		aif_ctrl = DA9055_AIF_FORMAT_I2S_MODE; +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		aif_ctrl = DA9055_AIF_FORMAT_LEFT_J; +		break; +	case SND_SOC_DAIFMT_RIGHT_J: +		aif_ctrl = DA9055_AIF_FORMAT_RIGHT_J; +		break; +	default: +		return -EINVAL; +	} + +	/* By default only 32 BCLK per WCLK is supported */ +	aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32; + +	snd_soc_update_bits(codec, DA9055_AIF_CLK_MODE, +			    (DA9055_AIF_CLK_MODE_MASK | DA9055_AIF_BCLK_MASK), +			    aif_clk_mode); +	snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK, +			    aif_ctrl); +	return 0; +} + +static int da9055_mute(struct snd_soc_dai *dai, int mute) +{ +	struct snd_soc_codec *codec = dai->codec; + +	if (mute) { +		snd_soc_update_bits(codec, DA9055_DAC_L_CTRL, +				    DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN); +		snd_soc_update_bits(codec, DA9055_DAC_R_CTRL, +				    DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN); +	} else { +		snd_soc_update_bits(codec, DA9055_DAC_L_CTRL, +				    DA9055_DAC_L_MUTE_EN, 0); +		snd_soc_update_bits(codec, DA9055_DAC_R_CTRL, +				    DA9055_DAC_R_MUTE_EN, 0); +	} + +	return 0; +} + +#define DA9055_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ +			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai, +				 int clk_id, unsigned int freq, int dir) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec); + +	switch (clk_id) { +	case DA9055_CLKSRC_MCLK: +		switch (freq) { +		case 11289600: +		case 12000000: +		case 12288000: +		case 13000000: +		case 13500000: +		case 14400000: +		case 19200000: +		case 19680000: +		case 19800000: +			da9055->mclk_rate = freq; +			return 0; +		default: +			dev_err(codec_dai->dev, "Unsupported MCLK value %d\n", +				freq); +			return -EINVAL; +		} +		break; +	default: +		dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id); +		return -EINVAL; +	} +} + +/* + * da9055_set_dai_pll	: Configure the codec PLL + * @param codec_dai	: Pointer to codec DAI + * @param pll_id	: da9055 has only one pll, so pll_id is always zero + * @param fref		: Input MCLK frequency + * @param fout		: FsDM value + * @return int		: Zero for success, negative error code for error + * + * Note: Supported PLL input frequencies are 11.2896MHz, 12MHz, 12.288MHz, + *	 13MHz, 13.5MHz, 14.4MHz, 19.2MHz, 19.6MHz and 19.8MHz + */ +static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, +			      int source, unsigned int fref, unsigned int fout) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec); + +	u8 pll_frac_top, pll_frac_bot, pll_integer, cnt; + +	/* Disable PLL before setting the divisors */ +	snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0); + +	/* In slave mode, there is only one set of divisors */ +	if (!da9055->master && (fout != 2822400)) +		goto pll_err; + +	/* Search pll div array for correct divisors */ +	for (cnt = 0; cnt < ARRAY_SIZE(da9055_pll_div); cnt++) { +		/* Check fref, mode  and fout */ +		if ((fref == da9055_pll_div[cnt].fref) && +		    (da9055->master ==  da9055_pll_div[cnt].mode) && +		    (fout == da9055_pll_div[cnt].fout)) { +			/* All match, pick up divisors */ +			pll_frac_top = da9055_pll_div[cnt].frac_top; +			pll_frac_bot = da9055_pll_div[cnt].frac_bot; +			pll_integer = da9055_pll_div[cnt].integer; +			break; +		} +	} +	if (cnt >= ARRAY_SIZE(da9055_pll_div)) +		goto pll_err; + +	/* Write PLL dividers */ +	snd_soc_write(codec, DA9055_PLL_FRAC_TOP, pll_frac_top); +	snd_soc_write(codec, DA9055_PLL_FRAC_BOT, pll_frac_bot); +	snd_soc_write(codec, DA9055_PLL_INTEGER, pll_integer); + +	return 0; +pll_err: +	dev_err(codec_dai->dev, "Error in setting up PLL\n"); +	return -EINVAL; +} + +/* DAI operations */ +static const struct snd_soc_dai_ops da9055_dai_ops = { +	.hw_params	= da9055_hw_params, +	.set_fmt	= da9055_set_dai_fmt, +	.set_sysclk	= da9055_set_dai_sysclk, +	.set_pll	= da9055_set_dai_pll, +	.digital_mute	= da9055_mute, +}; + +static struct snd_soc_dai_driver da9055_dai = { +	.name = "da9055-hifi", +	/* Playback Capabilities */ +	.playback = { +		.stream_name = "Playback", +		.channels_min = 1, +		.channels_max = 2, +		.rates = SNDRV_PCM_RATE_8000_96000, +		.formats = DA9055_FORMATS, +	}, +	/* Capture Capabilities */ +	.capture = { +		.stream_name = "Capture", +		.channels_min = 1, +		.channels_max = 2, +		.rates = SNDRV_PCM_RATE_8000_96000, +		.formats = DA9055_FORMATS, +	}, +	.ops = &da9055_dai_ops, +	.symmetric_rates = 1, +}; + +static int da9055_set_bias_level(struct snd_soc_codec *codec, +				 enum snd_soc_bias_level level) +{ +	switch (level) { +	case SND_SOC_BIAS_ON: +	case SND_SOC_BIAS_PREPARE: +		break; +	case SND_SOC_BIAS_STANDBY: +		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { +			/* Enable VMID reference & master bias */ +			snd_soc_update_bits(codec, DA9055_REFERENCES, +					    DA9055_VMID_EN | DA9055_BIAS_EN, +					    DA9055_VMID_EN | DA9055_BIAS_EN); +		} +		break; +	case SND_SOC_BIAS_OFF: +		/* Disable VMID reference & master bias */ +		snd_soc_update_bits(codec, DA9055_REFERENCES, +				    DA9055_VMID_EN | DA9055_BIAS_EN, 0); +		break; +	} +	codec->dapm.bias_level = level; +	return 0; +} + +static int da9055_probe(struct snd_soc_codec *codec) +{ +	int ret; +	struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec); + +	codec->control_data = da9055->regmap; +	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); +	if (ret < 0) { +		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); +		return ret; +	} + +	/* Enable all Gain Ramps */ +	snd_soc_update_bits(codec, DA9055_AUX_L_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_AUX_R_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_ADC_L_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_ADC_R_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_DAC_L_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_DAC_R_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_HP_L_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_HP_R_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); +	snd_soc_update_bits(codec, DA9055_LINE_CTRL, +			    DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN); + +	/* +	 * There are two separate control bits for input and output mixers as +	 * well as headphone and line outs. +	 * One to enable corresponding amplifier and other to enable its +	 * output. As amplifier bits are related to power control, they are +	 * being managed by DAPM while other (non power related) bits are +	 * enabled here +	 */ +	snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL, +			    DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN); +	snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL, +			    DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN); + +	snd_soc_update_bits(codec, DA9055_MIXOUT_L_CTRL, +			    DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN); +	snd_soc_update_bits(codec, DA9055_MIXOUT_R_CTRL, +			    DA9055_MIXOUT_R_MIX_EN, DA9055_MIXOUT_R_MIX_EN); + +	snd_soc_update_bits(codec, DA9055_HP_L_CTRL, +			    DA9055_HP_L_AMP_OE, DA9055_HP_L_AMP_OE); +	snd_soc_update_bits(codec, DA9055_HP_R_CTRL, +			    DA9055_HP_R_AMP_OE, DA9055_HP_R_AMP_OE); + +	snd_soc_update_bits(codec, DA9055_LINE_CTRL, +			    DA9055_LINE_AMP_OE, DA9055_LINE_AMP_OE); + +	/* Set this as per your system configuration */ +	snd_soc_write(codec, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ); + +	/* Set platform data values */ +	if (da9055->pdata) { +		/* set mic bias source */ +		if (da9055->pdata->micbias_source) { +			snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT, +					    DA9055_MICBIAS2_EN, +					    DA9055_MICBIAS2_EN); +		} else { +			snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT, +					    DA9055_MICBIAS2_EN, 0); +		} +		/* set mic bias voltage */ +		switch (da9055->pdata->micbias) { +		case DA9055_MICBIAS_2_2V: +		case DA9055_MICBIAS_2_1V: +		case DA9055_MICBIAS_1_8V: +		case DA9055_MICBIAS_1_6V: +			snd_soc_update_bits(codec, DA9055_MIC_CONFIG, +					    DA9055_MICBIAS_LEVEL_MASK, +					    (da9055->pdata->micbias) << 4); +			break; +		} +	} +	return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_da9055 = { +	.probe			= da9055_probe, +	.set_bias_level		= da9055_set_bias_level, + +	.controls		= da9055_snd_controls, +	.num_controls		= ARRAY_SIZE(da9055_snd_controls), + +	.dapm_widgets		= da9055_dapm_widgets, +	.num_dapm_widgets	= ARRAY_SIZE(da9055_dapm_widgets), +	.dapm_routes		= da9055_audio_map, +	.num_dapm_routes	= ARRAY_SIZE(da9055_audio_map), +}; + +static const struct regmap_config da9055_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, + +	.reg_defaults = da9055_reg_defaults, +	.num_reg_defaults = ARRAY_SIZE(da9055_reg_defaults), +	.volatile_reg = da9055_volatile_register, +	.cache_type = REGCACHE_RBTREE, +}; + +static int __devinit da9055_i2c_probe(struct i2c_client *i2c, +				      const struct i2c_device_id *id) +{ +	struct da9055_priv *da9055; +	struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev); +	int ret; + +	da9055 = devm_kzalloc(&i2c->dev, sizeof(struct da9055_priv), +			      GFP_KERNEL); +	if (!da9055) +		return -ENOMEM; + +	if (pdata) +		da9055->pdata = pdata; + +	i2c_set_clientdata(i2c, da9055); + +	da9055->regmap = devm_regmap_init_i2c(i2c, &da9055_regmap_config); +	if (IS_ERR(da9055->regmap)) { +		ret = PTR_ERR(da9055->regmap); +		dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); +		return ret; +	} + +	ret = snd_soc_register_codec(&i2c->dev, +			&soc_codec_dev_da9055, &da9055_dai, 1); +	if (ret < 0) { +		dev_err(&i2c->dev, "Failed to register da9055 codec: %d\n", +			ret); +	} +	return ret; +} + +static int __devexit da9055_remove(struct i2c_client *client) +{ +	snd_soc_unregister_codec(&client->dev); +	return 0; +} + +static const struct i2c_device_id da9055_i2c_id[] = { +	{ "da9055", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); + +/* I2C codec control layer */ +static struct i2c_driver da9055_i2c_driver = { +	.driver = { +		.name = "da9055", +		.owner = THIS_MODULE, +	}, +	.probe		= da9055_i2c_probe, +	.remove		= __devexit_p(da9055_remove), +	.id_table	= da9055_i2c_id, +}; + +module_i2c_driver(da9055_i2c_driver); + +MODULE_DESCRIPTION("ASoC DA9055 Codec driver"); +MODULE_AUTHOR("David Chen, Ashish Chavan"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index f8d6c31db87..99afc003a08 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -168,7 +168,8 @@ static void wm0010_halt(struct snd_soc_codec *codec)  	case WM0010_STAGE2:  	case WM0010_FIRMWARE:  		/* Remember to put chip back into reset */ -		gpio_set_value(wm0010->gpio_reset, wm0010->gpio_reset_value); +		gpio_set_value_cansleep(wm0010->gpio_reset, +					wm0010->gpio_reset_value);  		/* Disable the regulators */  		regulator_disable(wm0010->dbvdd);  		regulator_bulk_disable(ARRAY_SIZE(wm0010->core_supplies), @@ -387,7 +388,7 @@ static int wm0010_boot(struct snd_soc_codec *codec)  	}  	/* Release reset */ -	gpio_set_value(wm0010->gpio_reset, !wm0010->gpio_reset_value); +	gpio_set_value_cansleep(wm0010->gpio_reset, !wm0010->gpio_reset_value);  	spin_lock_irqsave(&wm0010->irq_lock, flags);  	wm0010->state = WM0010_OUT_OF_RESET;  	spin_unlock_irqrestore(&wm0010->irq_lock, flags); @@ -809,7 +810,6 @@ static int wm0010_probe(struct snd_soc_codec *codec)  static int __devinit wm0010_spi_probe(struct spi_device *spi)  { -	unsigned long flags;  	unsigned long gpio_flags;  	int ret;  	int trigger; @@ -876,6 +876,8 @@ static int __devinit wm0010_spi_probe(struct spi_device *spi)  		return -EINVAL;  	} +	wm0010->state = WM0010_POWER_OFF; +  	irq = spi->irq;  	if (wm0010->pdata.irq_flags)  		trigger = wm0010->pdata.irq_flags; @@ -897,10 +899,6 @@ static int __devinit wm0010_spi_probe(struct spi_device *spi)  	else  		wm0010->board_max_spi_speed = 0; -	spin_lock_irqsave(&wm0010->irq_lock, flags); -	wm0010->state = WM0010_POWER_OFF; -	spin_unlock_irqrestore(&wm0010->irq_lock, flags); -  	ret = snd_soc_register_codec(&spi->dev,  				     &soc_codec_dev_wm0010, wm0010_dai,  				     ARRAY_SIZE(wm0010_dai)); @@ -916,10 +914,8 @@ static int __devexit wm0010_spi_remove(struct spi_device *spi)  	snd_soc_unregister_codec(&spi->dev); -	if (wm0010->gpio_reset) { -		/* Remember to put chip back into reset */ -		gpio_set_value(wm0010->gpio_reset, wm0010->gpio_reset_value); -	} +	gpio_set_value_cansleep(wm0010->gpio_reset, +				wm0010->gpio_reset_value);  	if (wm0010->irq)  		free_irq(wm0010->irq, wm0010); diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 89cd6fcad01..b723e910fcd 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -31,6 +31,7 @@  #include <linux/i2c.h>  #include <linux/regmap.h>  #include <linux/debugfs.h> +#include <linux/regulator/consumer.h>  #include <linux/slab.h>  #include <sound/core.h>  #include <sound/pcm.h> @@ -43,6 +44,14 @@  #include "wm2000.h" +#define WM2000_NUM_SUPPLIES 3 + +static const char *wm2000_supplies[WM2000_NUM_SUPPLIES] = { +	"SPKVDD", +	"DBVDD", +	"DCVDD", +}; +  enum wm2000_anc_mode {  	ANC_ACTIVE = 0,  	ANC_BYPASS = 1, @@ -54,6 +63,8 @@ struct wm2000_priv {  	struct i2c_client *i2c;  	struct regmap *regmap; +	struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES]; +  	enum wm2000_anc_mode anc_mode;  	unsigned int anc_active:1; @@ -126,6 +137,12 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)  	dev_dbg(&i2c->dev, "Beginning power up\n"); +	ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); +	if (ret != 0) { +		dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); +		return ret; +	} +  	if (!wm2000->mclk_div) {  		dev_dbg(&i2c->dev, "Disabling MCLK divider\n");  		wm2000_write(i2c, WM2000_REG_SYS_CTL2, @@ -143,12 +160,14 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)  	if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,  			     WM2000_ANC_ENG_IDLE)) {  		dev_err(&i2c->dev, "ANC engine failed to reset\n"); +		regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);  		return -ETIMEDOUT;  	}  	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,  			     WM2000_STATUS_BOOT_COMPLETE)) {  		dev_err(&i2c->dev, "ANC engine failed to initialise\n"); +		regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);  		return -ETIMEDOUT;  	} @@ -163,11 +182,13 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)  			      wm2000->anc_download_size);  	if (ret < 0) {  		dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret); +		regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);  		return ret;  	}  	if (ret != wm2000->anc_download_size) {  		dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n",  			ret, wm2000->anc_download_size); +		regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);  		return -EIO;  	} @@ -201,6 +222,7 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)  	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,  			     WM2000_STATUS_MOUSE_ACTIVE)) {  		dev_err(&i2c->dev, "Timed out waiting for device\n"); +		regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies);  		return -ETIMEDOUT;  	} @@ -238,6 +260,8 @@ static int wm2000_power_down(struct i2c_client *i2c, int analogue)  		return -ETIMEDOUT;  	} +	regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); +  	dev_dbg(&i2c->dev, "powered off\n");  	wm2000->anc_mode = ANC_OFF; @@ -747,7 +771,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,  	struct wm2000_platform_data *pdata;  	const char *filename;  	const struct firmware *fw = NULL; -	int ret; +	int ret, i;  	int reg;  	u16 id; @@ -760,7 +784,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,  	dev_set_drvdata(&i2c->dev, wm2000); -	wm2000->regmap = regmap_init_i2c(i2c, &wm2000_regmap); +	wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap);  	if (IS_ERR(wm2000->regmap)) {  		ret = PTR_ERR(wm2000->regmap);  		dev_err(&i2c->dev, "Failed to allocate register map: %d\n", @@ -768,6 +792,22 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,  		goto out;  	} +	for (i = 0; i < WM2000_NUM_SUPPLIES; i++) +		wm2000->supplies[i].supply = wm2000_supplies[i]; + +	ret = devm_regulator_bulk_get(&i2c->dev, WM2000_NUM_SUPPLIES, +				      wm2000->supplies); +	if (ret != 0) { +		dev_err(&i2c->dev, "Failed to get supplies: %d\n", ret); +		return ret; +	} + +	ret = regulator_bulk_enable(WM2000_NUM_SUPPLIES, wm2000->supplies); +	if (ret != 0) { +		dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); +		return ret; +	} +  	/* Verify that this is a WM2000 */  	reg = wm2000_read(i2c, WM2000_REG_ID1);  	id = reg << 8; @@ -777,7 +817,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,  	if (id != 0x2000) {  		dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);  		ret = -ENODEV; -		goto out_regmap_exit; +		goto err_supplies;  	}  	reg = wm2000_read(i2c, WM2000_REG_REVISON); @@ -796,7 +836,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,  	ret = request_firmware(&fw, filename, &i2c->dev);  	if (ret != 0) {  		dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret); -		goto out_regmap_exit; +		goto err_supplies;  	}  	/* Pre-cook the concatenation of the register address onto the image */ @@ -807,7 +847,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,  	if (wm2000->anc_download == NULL) {  		dev_err(&i2c->dev, "Out of memory\n");  		ret = -ENOMEM; -		goto out_regmap_exit; +		goto err_supplies;  	}  	wm2000->anc_download[0] = 0x80; @@ -822,11 +862,10 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,  	wm2000_reset(wm2000);  	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0); -	if (!ret) -		goto out; -out_regmap_exit: -	regmap_exit(wm2000->regmap); +err_supplies: +	regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); +  out:  	release_firmware(fw);  	return ret; @@ -834,10 +873,7 @@ out:  static __devexit int wm2000_i2c_remove(struct i2c_client *i2c)  { -	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); -  	snd_soc_unregister_codec(&i2c->dev); -	regmap_exit(wm2000->regmap);  	return 0;  } diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 4a2db4e1088..1722b586bdb 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -308,7 +308,7 @@ SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,  SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),  SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),  SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), -SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),  SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),  SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index bf47914234b..9211e4192f7 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -153,6 +153,15 @@ SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),  SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),  SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), +ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP2L", ARIZONA_DSP2LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP2R", ARIZONA_DSP2RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3L", ARIZONA_DSP3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP3R", ARIZONA_DSP3RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP4L", ARIZONA_DSP4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DSP5R", ARIZONA_DSP4RMIX_INPUT_1_SOURCE), +  ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), @@ -163,7 +172,8 @@ ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE), -ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT3L", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT3R", ARIZONA_OUT3RMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),  ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), @@ -175,7 +185,7 @@ SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L,  	   ARIZONA_OUT1_OSR_SHIFT, 1, 0),  SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,  	   ARIZONA_OUT2_OSR_SHIFT, 1, 0), -SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L, +SOC_SINGLE("OUT3 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,  	   ARIZONA_OUT3_OSR_SHIFT, 1, 0),  SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,  	   ARIZONA_OUT4_OSR_SHIFT, 1, 0), @@ -188,8 +198,8 @@ SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,  	     ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),  SOC_DOUBLE_R("OUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,  	     ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1), -SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, -	   ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("OUT3 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, +	     ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),  SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,  	     ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),  SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, @@ -203,8 +213,9 @@ SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,  SOC_DOUBLE_R_TLV("OUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,  		 ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,  		 0xbf, 0, digital_tlv), -SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, -	       ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("OUT3 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, +		 ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_OUT3L_VOL_SHIFT, +		 0xbf, 0, digital_tlv),  SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,  		 ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,  		 0xbf, 0, digital_tlv), @@ -223,8 +234,9 @@ SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L,  		       ARIZONA_OUTPUT_PATH_CONFIG_2R,  		       ARIZONA_OUT2L_PGA_VOL_SHIFT,  		       0x34, 0x40, 0, ana_tlv), -SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L, -		     ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv), +SOC_DOUBLE_R_RANGE_TLV("OUT3 Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L, +		       ARIZONA_OUTPUT_PATH_CONFIG_3R, +		       ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),  SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,  	   ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), @@ -272,7 +284,8 @@ ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE); -ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3L, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3R, ARIZONA_OUT3RMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); @@ -300,6 +313,26 @@ ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);  ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); +static const char *wm5110_aec_loopback_texts[] = { +	"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R", +	"SPKOUTL", "SPKOUTR", "SPKDAT1L", "SPKDAT1R", "SPKDAT2L", "SPKDAT2R", +}; + +static const unsigned int wm5110_aec_loopback_values[] = { +	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +static const struct soc_enum wm5110_aec_loopback = +	SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, +			      ARIZONA_AEC_LOOPBACK_SRC_SHIFT, +			      ARIZONA_AEC_LOOPBACK_SRC_MASK, +			      ARRAY_SIZE(wm5110_aec_loopback_texts), +			      wm5110_aec_loopback_texts, +			      wm5110_aec_loopback_values); + +static const struct snd_kcontrol_new wm5110_aec_loopback_mux = +	SOC_DAPM_VALUE_ENUM("AEC Loopback", wm5110_aec_loopback); +  static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {  SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,  		    0, NULL, 0), @@ -313,7 +346,7 @@ SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,  SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),  SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),  SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), -SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),  SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),  SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0), @@ -409,6 +442,9 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,  SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,  		 NULL, 0), +SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, +		       ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5110_aec_loopback_mux), +  SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,  		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),  SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, @@ -478,6 +514,9 @@ SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,  SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,  		   ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,  		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3R", ARIZONA_OUTPUT_ENABLES_1, +		   ARIZONA_OUT3R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, +		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),  SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1,  		   ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,  		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), @@ -522,7 +561,8 @@ ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),  ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),  ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),  ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"), -ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(OUT3L, "HPOUT3L"), +ARIZONA_MIXER_WIDGETS(OUT3R, "HPOUT3R"),  ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),  ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),  ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), @@ -554,8 +594,8 @@ SND_SOC_DAPM_OUTPUT("HPOUT1L"),  SND_SOC_DAPM_OUTPUT("HPOUT1R"),  SND_SOC_DAPM_OUTPUT("HPOUT2L"),  SND_SOC_DAPM_OUTPUT("HPOUT2R"), -SND_SOC_DAPM_OUTPUT("EPOUTN"), -SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("HPOUT3L"), +SND_SOC_DAPM_OUTPUT("HPOUT3R"),  SND_SOC_DAPM_OUTPUT("SPKOUTLN"),  SND_SOC_DAPM_OUTPUT("SPKOUTLP"),  SND_SOC_DAPM_OUTPUT("SPKOUTRN"), @@ -570,6 +610,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT2R"),  	{ name, "Noise Generator", "Noise Generator" }, \  	{ name, "Tone Generator 1", "Tone Generator 1" }, \  	{ name, "Tone Generator 2", "Tone Generator 2" }, \ +	{ name, "AEC", "AEC Loopback" }, \  	{ name, "IN1L", "IN1L PGA" }, \  	{ name, "IN1R", "IN1R PGA" }, \  	{ name, "IN2L", "IN2L PGA" }, \ @@ -620,6 +661,7 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {  	{ "OUT2L", NULL, "CPVDD" },  	{ "OUT2R", NULL, "CPVDD" },  	{ "OUT3L", NULL, "CPVDD" }, +	{ "OUT3R", NULL, "CPVDD" },  	{ "OUT4L", NULL, "SPKVDDL" },  	{ "OUT4R", NULL, "SPKVDDR" }, @@ -701,7 +743,8 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {  	ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),  	ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),  	ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"), -	ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"), +	ARIZONA_MIXER_ROUTES("OUT3L", "HPOUT3L"), +	ARIZONA_MIXER_ROUTES("OUT3R", "HPOUT3R"),  	ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),  	ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"), @@ -754,8 +797,8 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {  	{ "HPOUT2L", NULL, "OUT2L" },  	{ "HPOUT2R", NULL, "OUT2R" }, -	{ "EPOUTN", NULL, "OUT3L" }, -	{ "EPOUTP", NULL, "OUT3L" }, +	{ "HPOUT3L", NULL, "OUT3L" }, +	{ "HPOUT3R", NULL, "OUT3L" },  	{ "SPKOUTLN", NULL, "OUT4L" },  	{ "SPKOUTLP", NULL, "OUT4L" }, @@ -873,6 +916,8 @@ static unsigned int wm5110_digital_vu[] = {  	ARIZONA_ADC_DIGITAL_VOLUME_2R,  	ARIZONA_ADC_DIGITAL_VOLUME_3L,  	ARIZONA_ADC_DIGITAL_VOLUME_3R, +	ARIZONA_ADC_DIGITAL_VOLUME_4L, +	ARIZONA_ADC_DIGITAL_VOLUME_4R,  	ARIZONA_DAC_DIGITAL_VOLUME_1L,  	ARIZONA_DAC_DIGITAL_VOLUME_1R, @@ -884,6 +929,8 @@ static unsigned int wm5110_digital_vu[] = {  	ARIZONA_DAC_DIGITAL_VOLUME_4R,  	ARIZONA_DAC_DIGITAL_VOLUME_5L,  	ARIZONA_DAC_DIGITAL_VOLUME_5R, +	ARIZONA_DAC_DIGITAL_VOLUME_6L, +	ARIZONA_DAC_DIGITAL_VOLUME_6R,  };  static struct snd_soc_codec_driver soc_codec_dev_wm5110 = { diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 7a773a835b8..867ae97ddce 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -681,6 +681,11 @@ void wm_hubs_update_class_w(struct snd_soc_codec *codec)  	snd_soc_update_bits(codec, WM8993_CLASS_W_0,  			    WM8993_CP_DYN_V | WM8993_CP_DYN_FREQ, enable); + +	snd_soc_write(codec, WM8993_LEFT_OUTPUT_VOLUME, +		      snd_soc_read(codec, WM8993_LEFT_OUTPUT_VOLUME)); +	snd_soc_write(codec, WM8993_RIGHT_OUTPUT_VOLUME, +		      snd_soc_read(codec, WM8993_RIGHT_OUTPUT_VOLUME));  }  EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index efb9ede0120..267d5b4b63c 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -93,9 +93,7 @@ static struct snd_soc_card eukrea_tlv320 = {  	.num_links	= 1,  }; -static struct platform_device *eukrea_tlv320_snd_device; - -static int __init eukrea_tlv320_init(void) +static int __devinit eukrea_tlv320_probe(struct platform_device *pdev)  {  	int ret;  	int int_port = 0, ext_port; @@ -136,29 +134,32 @@ static int __init eukrea_tlv320_init(void)  		return 0;  	} -	eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1); -	if (!eukrea_tlv320_snd_device) -		return -ENOMEM; - -	platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320); -	ret = platform_device_add(eukrea_tlv320_snd_device); - -	if (ret) { -		printk(KERN_ERR "ASoC: Platform device allocation failed\n"); -		platform_device_put(eukrea_tlv320_snd_device); -	} +	eukrea_tlv320.dev = &pdev->dev; +	ret = snd_soc_register_card(&eukrea_tlv320); +	if (ret) +		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);  	return ret;  } -static void __exit eukrea_tlv320_exit(void) +static int __devexit eukrea_tlv320_remove(struct platform_device *pdev)  { -	platform_device_unregister(eukrea_tlv320_snd_device); +	snd_soc_unregister_card(&eukrea_tlv320); + +	return 0;  } -module_init(eukrea_tlv320_init); -module_exit(eukrea_tlv320_exit); +static struct platform_driver eukrea_tlv320_driver = { +	.driver = { +		.name = "eukrea_tlv320", +		.owner = THIS_MODULE, +	}, +	.probe = eukrea_tlv320_probe, +	.remove = __devexit_p(eukrea_tlv320_remove),}; + +module_platform_driver(eukrea_tlv320_driver);  MODULE_AUTHOR("Eric Bénard <eric@eukrea.com>");  MODULE_DESCRIPTION("CPUIMX ALSA SoC driver");  MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:eukrea_tlv320"); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 873e6e76ee8..d0a4be38dc0 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1017,10 +1017,29 @@ EXPORT_SYMBOL_GPL(dapm_reg_event);  int dapm_regulator_event(struct snd_soc_dapm_widget *w,  		   struct snd_kcontrol *kcontrol, int event)  { -	if (SND_SOC_DAPM_EVENT_ON(event)) +	int ret; + +	if (SND_SOC_DAPM_EVENT_ON(event)) { +		if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { +			ret = regulator_allow_bypass(w->regulator, true); +			if (ret != 0) +				dev_warn(w->dapm->dev, +					 "Failed to bypass %s: %d\n", +					 w->name, ret); +		} +  		return regulator_enable(w->regulator); -	else +	} else { +		if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { +			ret = regulator_allow_bypass(w->regulator, false); +			if (ret != 0) +				dev_warn(w->dapm->dev, +					 "Failed to unbypass %s: %d\n", +					 w->name, ret); +		} +  		return regulator_disable_deferred(w->regulator, w->shift); +	}  }  EXPORT_SYMBOL_GPL(dapm_regulator_event); diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 31c4d26d035..356611d9654 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -16,6 +16,7 @@  #include <linux/module.h>  #include <linux/io.h>  #include <linux/spi/spi.h> +#include <linux/of.h>  #include <sound/soc.h>  #include <sound/initval.h> @@ -56,16 +57,47 @@ static struct snd_soc_card mop500_card = {  	.num_links = ARRAY_SIZE(mop500_dai_links),  }; +static int __devinit mop500_of_probe(struct platform_device *pdev, +				struct device_node *np) +{ +	struct device_node *codec_np, *msp_np[2]; +	int i; + +	msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0); +	msp_np[1] = of_parse_phandle(np, "stericsson,cpu-dai", 1); +	codec_np  = of_parse_phandle(np, "stericsson,audio-codec", 0); + +	if (!(msp_np[0] && msp_np[1] && codec_np)) { +		dev_err(&pdev->dev, "Phandle missing or invalid\n"); +		return -EINVAL; +	} + +	for (i = 0; i < 2; i++) { +		mop500_dai_links[i].cpu_of_node = msp_np[i]; +		mop500_dai_links[i].cpu_dai_name = NULL; +		mop500_dai_links[i].codec_of_node = codec_np; +		mop500_dai_links[i].codec_name = NULL; +	} + +	snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name"); + +	return 0; +}  static int __devinit mop500_probe(struct platform_device *pdev)  { +	struct device_node *np = pdev->dev.of_node;  	int ret; -	pr_debug("%s: Enter.\n", __func__); -  	dev_dbg(&pdev->dev, "%s: Enter.\n", __func__);  	mop500_card.dev = &pdev->dev; +	if (np) { +		ret = mop500_of_probe(pdev, np); +		if (ret) +			return ret; +	} +  	dev_dbg(&pdev->dev, "%s: Card %s: Set platform drvdata.\n",  		__func__, mop500_card.name);  	platform_set_drvdata(pdev, &mop500_card); @@ -83,8 +115,7 @@ static int __devinit mop500_probe(struct platform_device *pdev)  	ret = snd_soc_register_card(&mop500_card);  	if (ret)  		dev_err(&pdev->dev, -			"Error: snd_soc_register_card failed (%d)!\n", -			ret); +			"Error: snd_soc_register_card failed (%d)!\n", ret);  	return ret;  } @@ -97,14 +128,20 @@ static int __devexit mop500_remove(struct platform_device *pdev)  	snd_soc_unregister_card(mop500_card);  	mop500_ab8500_remove(mop500_card); -	 +  	return 0;  } +static const struct of_device_id snd_soc_mop500_match[] = { +	{ .compatible = "stericsson,snd-soc-mop500", }, +	{}, +}; +  static struct platform_driver snd_soc_mop500_driver = {  	.driver = {  		.owner = THIS_MODULE,  		.name = "snd-soc-mop500", +		.of_match_table = snd_soc_mop500_match,  	},  	.probe = mop500_probe,  	.remove = __devexit_p(mop500_remove), diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 772cb19d2fb..be94bf9bf94 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -833,10 +833,16 @@ static int __devexit ux500_msp_drv_remove(struct platform_device *pdev)  	return 0;  } +static const struct of_device_id ux500_msp_i2s_match[] = { +	{ .compatible = "stericsson,ux500-msp-i2s", }, +	{}, +}; +  static struct platform_driver msp_i2s_driver = {  	.driver = {  		.name = "ux500-msp-i2s",  		.owner = THIS_MODULE, +		.of_match_table = ux500_msp_i2s_match,  	},  	.probe = ux500_msp_drv_probe,  	.remove = ux500_msp_drv_remove, diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index 1b7c2f58ce1..b7c996e7757 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -15,8 +15,10 @@  #include <linux/module.h>  #include <linux/platform_device.h> +#include <linux/pinctrl/consumer.h>  #include <linux/delay.h>  #include <linux/slab.h> +#include <linux/of.h>  #include <mach/hardware.h>  #include <mach/msp.h> @@ -25,6 +27,9 @@  #include "ux500_msp_i2s.h" +/* MSP1/3 Tx/Rx usage protection */ +static DEFINE_SPINLOCK(msp_rxtx_lock); +   /* Protocol desciptors */  static const struct msp_protdesc prot_descs[] = {  	{ /* I2S */ @@ -352,17 +357,23 @@ static int configure_multichannel(struct ux500_msp *msp,  static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)  { -	int status = 0; +	int status = 0, retval = 0;  	u32 reg_val_DMACR, reg_val_GCR; +	unsigned long flags;  	/* Check msp state whether in RUN or CONFIGURED Mode */ -	if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) { -		status = msp->plat_init(); -		if (status) { -			dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n", -				__func__, status); -			return status; +	if (msp->msp_state == MSP_STATE_IDLE) { +		spin_lock_irqsave(&msp_rxtx_lock, flags); +		if (msp->pinctrl_rxtx_ref == 0 && +			!(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_def))) { +			retval = pinctrl_select_state(msp->pinctrl_p, +						msp->pinctrl_def); +			if (retval) +				pr_err("could not set MSP defstate\n");  		} +		if (!retval) +			msp->pinctrl_rxtx_ref++; +		spin_unlock_irqrestore(&msp_rxtx_lock, flags);  	}  	/* Configure msp with protocol dependent settings */ @@ -620,7 +631,8 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)  int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)  { -	int status = 0; +	int status = 0, retval = 0; +	unsigned long flags;  	dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir); @@ -631,12 +643,19 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)  		writel((readl(msp->registers + MSP_GCR) &  			       (~(FRAME_GEN_ENABLE | SRG_ENABLE))),  			      msp->registers + MSP_GCR); -		if (msp->plat_exit) -			status = msp->plat_exit(); -			if (status) -				dev_warn(msp->dev, -					"%s: WARN: ux500_msp_i2s_exit failed (%d)!\n", -					__func__, status); + +		spin_lock_irqsave(&msp_rxtx_lock, flags); +		WARN_ON(!msp->pinctrl_rxtx_ref); +		msp->pinctrl_rxtx_ref--; +		if (msp->pinctrl_rxtx_ref == 0 && +			!(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_sleep))) { +			retval = pinctrl_select_state(msp->pinctrl_p, +						msp->pinctrl_sleep); +			if (retval) +				pr_err("could not set MSP sleepstate\n"); +		} +		spin_unlock_irqrestore(&msp_rxtx_lock, flags); +  		writel(0, msp->registers + MSP_GCR);  		writel(0, msp->registers + MSP_TCF);  		writel(0, msp->registers + MSP_RCF); @@ -665,20 +684,33 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,  {  	struct resource *res = NULL;  	struct i2s_controller *i2s_cont; +	struct device_node *np = pdev->dev.of_node;  	struct ux500_msp *msp; -	dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__, -		pdev->name, platform_data->id); -  	*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);  	msp = *msp_p;  	if (!msp)  		return -ENOMEM; +	if (np) { +		if (!platform_data) { +			platform_data = devm_kzalloc(&pdev->dev, +				sizeof(struct msp_i2s_platform_data), GFP_KERNEL); +			if (!platform_data) +				ret = -ENOMEM; +		} +	} else +		if (!platform_data) +			ret = -EINVAL; + +	if (ret) +		goto err_res; + +	dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__, +		pdev->name, platform_data->id); +  	msp->id = platform_data->id;  	msp->dev = &pdev->dev; -	msp->plat_init = platform_data->msp_i2s_init; -	msp->plat_exit = platform_data->msp_i2s_exit;  	msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;  	msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx; @@ -715,6 +747,25 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,  	dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name);  	msp->i2s_cont = i2s_cont; +	msp->pinctrl_p = pinctrl_get(msp->dev); +	if (IS_ERR(msp->pinctrl_p)) +		dev_err(&pdev->dev, "could not get MSP pinctrl\n"); +	else { +		msp->pinctrl_def = pinctrl_lookup_state(msp->pinctrl_p, +						PINCTRL_STATE_DEFAULT); +		if (IS_ERR(msp->pinctrl_def)) { +			dev_err(&pdev->dev, +				"could not get MSP defstate (%li)\n", +				PTR_ERR(msp->pinctrl_def)); +		} +		msp->pinctrl_sleep = pinctrl_lookup_state(msp->pinctrl_p, +						PINCTRL_STATE_SLEEP); +		if (IS_ERR(msp->pinctrl_sleep)) +			dev_err(&pdev->dev, +				"could not get MSP idlestate (%li)\n", +				PTR_ERR(msp->pinctrl_def)); +	} +  	return 0;  } diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index 2d9136da986..1311c0df762 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -524,14 +524,18 @@ struct ux500_msp {  	struct dma_chan *rx_pipeid;  	enum msp_state msp_state;  	int (*transfer) (struct ux500_msp *msp, struct i2s_message *message); -	int (*plat_init) (void); -	int (*plat_exit) (void);  	struct timer_list notify_timer;  	int def_elem_len;  	unsigned int dir_busy;  	int loopback_enable;  	u32 backup_regs[MAX_MSP_BACKUP_REGS];  	unsigned int f_bitclk; +	/* Pin modes */ +	struct pinctrl *pinctrl_p; +	struct pinctrl_state *pinctrl_def; +	struct pinctrl_state *pinctrl_sleep; +	/* Reference Count */ +	int pinctrl_rxtx_ref;  };  struct ux500_msp_dma_params {  |