diff --git a/Documentation/devicetree/bindings/sound/cs35l41.txt b/Documentation/devicetree/bindings/sound/cs35l41.txt index 8c17467d9b45..479d916d4068 100644 --- a/Documentation/devicetree/bindings/sound/cs35l41.txt +++ b/Documentation/devicetree/bindings/sound/cs35l41.txt @@ -3,6 +3,7 @@ CS35L41 Speaker Amplifier Required properties: - compatible : "cirrus,cs35l41" + "cirrus,cs35l40" - reg : the SPI chip select line for the device @@ -10,6 +11,28 @@ Required properties: as covered in Documentation/devicetree/bindings/regulator/regulator.txt. + + Boost hardware configuration: + + These three properties should be used together to specify the external + component configuration of the part. See section 4.3.6 of the datasheet + for details regarding how these values are used to configure the + digital boost converter's control loop. + + - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. + Configures the peak current by monitoring the current through the boost FET. + Range starts at 1600 mA and goes to a maximum of 4500 mA with increments + of 50 mA. + (Default) 4.50 Amps + + - cirrus,boost-ind-nanohenry : Boost inductor value, expressed in nH. Valid + values include 1000, 1200, 1500 and 2200. + + - cirrus,boost-cap-microfarad : Total equivalent boost capacitance on the VBST + and VAMP pins, derated at 11 volts DC. The value must be rounded to the + nearest integer and expressed in uF. + + Optional properties: - cirrus,sclk-force-output : Audio serial port SCLK force output control. Forces the SCLK to continue to drive even @@ -30,12 +53,6 @@ Optional properties: 11000 mV with increments of 50 mV. (Default) VP - - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA. - Configures the peak current by monitoring the current through the boost FET. - Range starts at 1600 mA and goes to a maximum of 4500 mA with increments - of 50 mA. - (Default) 4.50 Amps - - cirrus,amp-gain-zc : Boolean to determine whether to use the amplifier gain-change zero-crossing feature. If the feature is enabled, any user-controlled amplifier gain change will occur on a zero-crossing point. @@ -49,7 +66,6 @@ Optional properties: 2 = (Default) Logic 0 during unused slots, but Hi-Z while all transmit channels disabled 3 = Hi-Z during unused slots and while all transmit channels disabled - - cirrus,temp-warn-threshold : Amplifier overtemperature warning threshold. Configures the threshold at which the overtemperature warning condition occurs. When the threshold is met, the ovetemperature warning attenuation is applied @@ -61,46 +77,10 @@ Optional properties: 2 = 125C (Default) 3 = 135C - - cirrus,hw-noise-gate-select : Hardware Noise Gate feature. Each bit selects - a different block for detection. - - 000000 - All noise gating functionality disbled - xxxxx1 - Amplifier noise gating detection enabled - xxxx1x - Boost converter DCM detection enabled - xxx1xx - VMON low power mode detection enabled - xx1xxx - IMON low power mode detection enabled - x1xxxx - VP brownout prevention detection enabled - 1xxxxx - VBST brownout prevention detection enabled - - - cirrus,hw-noise-gate-delay : Time that the incoming audio signal must be - below the noise gate threshold prior to entering a noise gated state - - 0 = 5 ms - 1 = 10 ms - 2 = 25 ms - 3 = 50 ms (default) - 4 = 100 ms - 5 = 250 ms - 6 = 500 ms - 7 = 1 s - - - cirrus,hw-noise-gate-threshold : Threshold of audio signal input which the - noise gate considers the input audio to be at a low enough level to be - valid to enter a noise gating state of operation. - - 0 = 0.654 mVpk - 1 = 0.328 mVpk - 2 = 0.164 mVpk - 3 = 0.082 mVpk (default) - 4 = 0.041 mVpk - 5 = 0.021 mVpk - 6 = 0.010 mVpk - 7 = True zero data (no LSB dither) - - - cirrus,dsp-noise-gate-enable : DSP Noise Gate feature. If present, noise + - cirrus,noise-gate-enable : DSP Noise Gate feature. If present, noise gate feature will be enabled. - - cirrus,dsp-noise-gate-threshold : Threshold of audio signal input which the + - cirrus,noise-gate-threshold : Threshold of audio signal input which the noise gate considers the input audio to be at a low enough level to be valid to enter a noise gating state of operation. @@ -113,7 +93,7 @@ Optional properties: 6 = -102 dBFS 7 = -108 dBFS - - cirrus,dsp-noise-gate-delay : Time that the incoming audio signal must be + - cirrus,noise-gate-delay : Time that the incoming audio signal must be below the noise gate threshold prior to entering a noise gated state 0 = 5 ms @@ -211,17 +191,8 @@ cs35l41: cs35l41@2 { reg = <2>; VA-supply = <&dummy_vreg>; VP-supply = <&dummy_vreg>; + + cirrus,boost-peak-milliamp = <4500>; + cirrus,boost-ind-nanohenry = <1000>; + cirrus,boost-cap-microfarad = <15>; }; - -Optional Fixed HW Params sub-node: - -The cs35l41 node can have a single "cirrus,fixed-hw-params" sub-node for -setting the HW params to a fixed rate, width, and word-length. - -Example: - - cirrus,fixed-hw-params { - cirrus,fixed-rate = <48000>; - cirrus,fixed-width = <16>; - cirrus,fixed-wl = <16>; - }; \ No newline at end of file diff --git a/drivers/mfd/Android.mk b/drivers/mfd/Android.mk index 5afec49a24e3..a9865e4a80ac 100644 --- a/drivers/mfd/Android.mk +++ b/drivers/mfd/Android.mk @@ -7,8 +7,3 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk -include $(CLEAR_VARS) -LOCAL_MODULE := cirrus_cs35l41-mfd.ko -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/mfd/Kbuild b/drivers/mfd/Kbuild index 5c1f8322234f..67ff2cd39094 100644 --- a/drivers/mfd/Kbuild +++ b/drivers/mfd/Kbuild @@ -10,7 +10,3 @@ cirrus_madera-core-objs = madera-core.o madera-spi.o madera-core.o madera-spi.o endif obj-m += cirrus_madera-core.o - -EXTRA_CFLAGS += -DCONFIG_MFD_CS35L41_I2C -cirrus_cs35l41-mfd-objs = cs35l41-core.o cs35l41-i2c.o cs35l41-tables.o -obj-m += cirrus_cs35l41-mfd.o diff --git a/drivers/mfd/cs35l41-core.c b/drivers/mfd/cs35l41-core.c deleted file mode 100644 index 8a0f4587e95f..000000000000 --- a/drivers/mfd/cs35l41-core.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Core MFD support for Cirrus Logic CS35L41 codec - * - * Copyright 2017 Cirrus Logic - * - * Author: David Rhodes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define CS35L41_MFD_SYSFS_CLASS_NAME "cirrus" - -static const struct mfd_cell cs35l41_devs[] = { - { .name = "cs35l41-codec", }, -}; - -static const char * const cs35l41_supplies[] = { - "VA", - "VP", -}; - -int cs35l41_dev_init(struct cs35l41_data *cs35l41) -{ - int ret, i; - - dev_set_drvdata(cs35l41->dev, cs35l41); - dev_info(cs35l41->dev, "Prince MFD core probe\n"); - - if (dev_get_platdata(cs35l41->dev)) - memcpy(&cs35l41->pdata, dev_get_platdata(cs35l41->dev), - sizeof(cs35l41->pdata)); - - cs35l41->mfd_class = class_create(THIS_MODULE, - dev_name(cs35l41->dev)); - if (IS_ERR(cs35l41->mfd_class)) { - ret = PTR_ERR(cs35l41->mfd_class); - dev_err(cs35l41->dev, "err class create\n"); - return -EINVAL; - } - - for (i = 0; i < ARRAY_SIZE(cs35l41_supplies); i++) - cs35l41->supplies[i].supply = cs35l41_supplies[i]; - - cs35l41->num_supplies = ARRAY_SIZE(cs35l41_supplies); - - ret = devm_regulator_bulk_get(cs35l41->dev, cs35l41->num_supplies, - cs35l41->supplies); - if (ret != 0) { - dev_err(cs35l41->dev, - "Failed to request core supplies: %d\n", - ret); - return ret; - } - - ret = regulator_bulk_enable(cs35l41->num_supplies, cs35l41->supplies); - if (ret != 0) { - dev_err(cs35l41->dev, - "Failed to enable core supplies: %d\n", ret); - return ret; - } - - /* returning NULL can be an option if in stereo mode */ - cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(cs35l41->reset_gpio)) { - ret = PTR_ERR(cs35l41->reset_gpio); - cs35l41->reset_gpio = NULL; - if (ret == -EBUSY) { - dev_info(cs35l41->dev, - "Reset line busy, assuming shared reset\n"); - } else { - dev_err(cs35l41->dev, - "Failed to get reset GPIO: %d\n", ret); - goto err; - } - } - - if (cs35l41->reset_gpio) { - usleep_range(1000, 1100); - gpiod_set_value_cansleep(cs35l41->reset_gpio, 1); - } - - usleep_range(2000, 2100); - - ret = mfd_add_devices(cs35l41->dev, PLATFORM_DEVID_AUTO, cs35l41_devs, - ARRAY_SIZE(cs35l41_devs), - NULL, 0, NULL); - if (ret) { - dev_err(cs35l41->dev, "Failed to add subdevices: %d\n", ret); - ret = -EINVAL; - goto err; - } - - return 0; - -err: - regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies); - return ret; -} - -int cs35l41_dev_exit(struct cs35l41_data *cs35l41) -{ - mfd_remove_devices(cs35l41->dev); - regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies); - return 0; -} diff --git a/drivers/mfd/cs35l41.h b/drivers/mfd/cs35l41.h deleted file mode 100644 index 041d66f51ba9..000000000000 --- a/drivers/mfd/cs35l41.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * cs35l41.h -- MFD internals for Cirrus Logic CS35L41 codec - * - * Copyright 2017 Cirrus Logic - * - * Author: David Rhodes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - - - -#ifndef CS35L41_MFD_H -#define CS35L41_MFD_H - -#include -#include - -#include - -int cs35l41_dev_init(struct cs35l41_data *cs35l41); -int cs35l41_dev_exit(struct cs35l41_data *cs35l41); - -#endif \ No newline at end of file diff --git a/include/linux/mfd/cs35l41/core.h b/include/linux/mfd/cs35l41/core.h deleted file mode 100644 index 3956a8237477..000000000000 --- a/include/linux/mfd/cs35l41/core.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * core.h -- MFD includes for Cirrus Logic CS35L41 codecs - * - * Copyright 2017 Cirrus Logic - * - * Author: David Rhodes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CS35L41_MFD_CORE_H -#define CS35L41_MFD_CORE_H - -#include -#include - -struct cs35l41_data { - struct cs35l41_platform_data *pdata; - struct device *dev; - struct regmap *regmap; - struct class *mfd_class; - struct regulator_bulk_data supplies[2]; - struct gpio_desc *reset_gpio; - int num_supplies; - int irq; -}; - -#endif diff --git a/include/sound/cs35l41.h b/include/sound/cs35l41.h index 591bf39fe06d..872fe4bb826d 100644 --- a/include/sound/cs35l41.h +++ b/include/sound/cs35l41.h @@ -1,7 +1,7 @@ /* * linux/sound/cs35l41.h -- Platform data for CS35L41 * - * Copyright (c) 2016 Cirrus Logic Inc. + * Copyright (c) 2018 Cirrus Logic Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,7 +22,7 @@ struct classh_cfg { int classh_wk_fet_thld; }; -struct cs35l41_irq_cfg { +struct irq_cfg { bool is_present; bool irq_pol_inv; bool irq_out_en; @@ -34,25 +34,46 @@ struct cs35l41_platform_data { bool lrclk_frc; bool right_channel; bool amp_gain_zc; - bool dsp_ng_enable; + bool ng_enable; int bst_ind; int bst_vctrl; int bst_ipk; + int bst_cap; int temp_warn_thld; - int dsp_ng_pcm_thld; - int dsp_ng_delay; + int ng_pcm_thld; + int ng_delay; int dout_hiz; - unsigned int hw_ng_sel; - unsigned int hw_ng_delay; - unsigned int hw_ng_thld; - unsigned int fixed_rate; - unsigned int fixed_width; - unsigned int fixed_wl; - bool fixed_params; - const char * dsp_part_name; - struct cs35l41_irq_cfg irq_config1; - struct cs35l41_irq_cfg irq_config2; + struct irq_cfg irq_config1; + struct irq_cfg irq_config2; struct classh_cfg classh_config; }; +struct cs35l41_private { + struct wm_adsp dsp; /* needs to be first member */ + struct snd_soc_codec *codec; + struct cs35l41_platform_data pdata; + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[2]; + int num_supplies; + int irq; + int clksrc; + int extclk_freq; + int extclk_cfg; + int sclk; + bool tdm_mode; + bool i2s_mode; + bool swire_mode; + bool halo_booted; + bool bus_spi; + struct mutex rate_lock; + /* GPIO for /RST */ + struct gpio_desc *reset_gpio; + struct completion global_pup_done; + struct completion global_pdn_done; +}; + +int cs35l41_probe(struct cs35l41_private *cs35l41, + struct cs35l41_platform_data *pdata); + #endif /* __CS35L41_H */ diff --git a/sound/soc/codecs/Android.mk b/sound/soc/codecs/Android.mk index eee9385557e6..1c0f47eac296 100644 --- a/sound/soc/codecs/Android.mk +++ b/sound/soc/codecs/Android.mk @@ -7,6 +7,12 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk +include $(CLEAR_VARS) +LOCAL_MODULE := cirrus_wm_adsp.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + include $(CLEAR_VARS) LOCAL_MODULE := cirrus_cs35l35.ko LOCAL_MODULE_TAGS := optional @@ -19,12 +25,6 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk -include $(CLEAR_VARS) -LOCAL_MODULE := cirrus_cs35l41.ko -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/AndroidKernelModule.mk - include $(CLEAR_VARS) LOCAL_MODULE := cirrus_cs47l35.ko LOCAL_MODULE_TAGS := optional @@ -49,5 +49,14 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk - +include $(CLEAR_VARS) +ifeq ($(CIRRUS_AMP_CODEC),cs35l41_spi) +KERNEL_CFLAGS += CONFIG_SND_SOC_CS35L41_SPI=y +else +KERNEL_CFLAGS += CONFIG_SND_SOC_CS35L41_I2C=y +endif +LOCAL_MODULE := cirrus_cs35l41.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/sound/soc/codecs/Kbuild b/sound/soc/codecs/Kbuild index 483fe94faa2d..a54cefd5a30b 100644 --- a/sound/soc/codecs/Kbuild +++ b/sound/soc/codecs/Kbuild @@ -16,9 +16,12 @@ endif EXTRA_CFLAGS += -DCONFIG_SND_SOC_AOV_TRIGGER -DCONFIG_SND_SOC_OPALUM -cirrus_madera-objs = madera.o madera-slimbus.o wm_adsp.o +cirrus_madera-objs = madera.o madera-slimbus.o obj-m += cirrus_madera.o +cirrus_wm_adsp-objs = wm_adsp.o +obj-m += cirrus_wm_adsp.o + cirrus_cs35l35-objs = cs35l35.o obj-m += cirrus_cs35l35.o @@ -28,9 +31,6 @@ obj-m += cirrus_cs35l36.o cirrus_cs47l35-objs = cs47l35.o obj-m += cirrus_cs47l35.o -cirrus_cs35l41-objs = cs35l41.o -obj-m += cirrus_cs35l41.o - cirrus_cs47l90-objs = cs47l90.o obj-m += cirrus_cs47l90.o @@ -38,3 +38,12 @@ cirrus_ospl-objs = ospl2xx.o obj-m += cirrus_ospl.o obj-m += aov_trigger.o + +cirrus_cs35l41-objs = cs35l41.o cs35l41-tables.o +obj-m += cirrus_cs35l41.o + +ifneq ($(filter m y,$(CONFIG_SND_SOC_CS35L41_SPI)),) + cirrus_cs35l41-objs += cs35l41-spi.o +else + cirrus_cs35l41-objs += cs35l41-i2c.o +endif diff --git a/drivers/mfd/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c similarity index 69% rename from drivers/mfd/cs35l41-i2c.c rename to sound/soc/codecs/cs35l41-i2c.c index 5b970745380a..2d6e5905d760 100644 --- a/drivers/mfd/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -1,26 +1,36 @@ /* - * I2C bus interface to Cirrus Logic CS35L41 codec + * cs35l41-i2c.c -- CS35l41 I2C driver * - * Copyright 2017 Cirrus Logic + * Copyright 2017 Cirrus Logic, Inc. * * Author: David Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * */ -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include -#include -#include "sound/cs35l41.h" +#include "wm_adsp.h" #include "cs35l41.h" - +#include static struct regmap_config cs35l41_regmap_i2c = { .reg_bits = 32, @@ -36,13 +46,6 @@ static struct regmap_config cs35l41_regmap_i2c = { .cache_type = REGCACHE_RBTREE, }; -static const struct of_device_id cs35l41_of_match[] = { - {.compatible = "cirrus,cs35l40"}, - {.compatible = "cirrus,cs35l41"}, - {}, -}; -MODULE_DEVICE_TABLE(of, cs35l41_of_match); - static const struct i2c_device_id cs35l41_id_i2c[] = { {"cs35l40", 0}, {"cs35l41", 0}, @@ -54,20 +57,20 @@ MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c); static int cs35l41_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct cs35l41_private *cs35l41; struct device *dev = &client->dev; struct cs35l41_platform_data *pdata = dev_get_platdata(dev); const struct regmap_config *regmap_config = &cs35l41_regmap_i2c; - struct cs35l41_data *cs35l41; int ret; - cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_data), GFP_KERNEL); + cs35l41 = devm_kzalloc(dev, sizeof(struct cs35l41_private), GFP_KERNEL); if (cs35l41 == NULL) return -ENOMEM; cs35l41->dev = dev; cs35l41->irq = client->irq; - cs35l41->pdata = pdata; + cs35l41->bus_spi = false; i2c_set_clientdata(client, cs35l41); cs35l41->regmap = devm_regmap_init_i2c(client, regmap_config); @@ -78,16 +81,27 @@ static int cs35l41_i2c_probe(struct i2c_client *client, return ret; } - return cs35l41_dev_init(cs35l41); + return cs35l41_probe(cs35l41, pdata); } static int cs35l41_i2c_remove(struct i2c_client *client) { - struct cs35l41_data *cs35l41 = i2c_get_clientdata(client); + struct cs35l41_private *cs35l41 = i2c_get_clientdata(client); - return cs35l41_dev_exit(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); + wm_adsp2_remove(&cs35l41->dsp); + regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies); + snd_soc_unregister_codec(cs35l41->dev); + return 0; } +static const struct of_device_id cs35l41_of_match[] = { + {.compatible = "cirrus,cs35l40"}, + {.compatible = "cirrus,cs35l41"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l41_of_match); + static struct i2c_driver cs35l41_i2c_driver = { .driver = { .name = "cs35l41", diff --git a/drivers/mfd/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c similarity index 73% rename from drivers/mfd/cs35l41-spi.c rename to sound/soc/codecs/cs35l41-spi.c index c78494a00ef0..f4bf63264973 100644 --- a/drivers/mfd/cs35l41-spi.c +++ b/sound/soc/codecs/cs35l41-spi.c @@ -1,25 +1,31 @@ /* - * SPI bus interface to Cirrus Logic CS35L41 codec + * cs35l41-spi.c -- CS35l41 SPI driver * - * Copyright 2017 Cirrus Logic + * Copyright 2017 Cirrus Logic, Inc. * * Author: David Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. + * */ -#include -#include -#include #include +#include +#include +#include +#include +#include +#include #include -#include +#include +#include +#include -#include "sound/cs35l41.h" +#include "wm_adsp.h" #include "cs35l41.h" -#include +#include static struct regmap_config cs35l41_regmap_spi = { .reg_bits = 32, @@ -36,13 +42,6 @@ static struct regmap_config cs35l41_regmap_spi = { .cache_type = REGCACHE_RBTREE, }; -static const struct of_device_id cs35l41_of_match[] = { - {.compatible = "cirrus,cs35l40"}, - {.compatible = "cirrus,cs35l41"}, - {}, -}; -MODULE_DEVICE_TABLE(of, cs35l41_of_match); - static const struct spi_device_id cs35l41_id_spi[] = { {"cs35l40", 0}, {"cs35l41", 0}, @@ -56,11 +55,11 @@ static int cs35l41_spi_probe(struct spi_device *spi) const struct regmap_config *regmap_config = &cs35l41_regmap_spi; struct cs35l41_platform_data *pdata = dev_get_platdata(&spi->dev); - struct cs35l41_data *cs35l41; + struct cs35l41_private *cs35l41; int ret; cs35l41 = devm_kzalloc(&spi->dev, - sizeof(struct cs35l41_data), + sizeof(struct cs35l41_private), GFP_KERNEL); if (cs35l41 == NULL) return -ENOMEM; @@ -76,20 +75,29 @@ static int cs35l41_spi_probe(struct spi_device *spi) cs35l41->dev = &spi->dev; cs35l41->irq = spi->irq; - cs35l41->pdata = pdata; + cs35l41->bus_spi = true; - dev_info(&spi->dev, "CS35L41 SPI MFD probe\n"); - - return cs35l41_dev_init(cs35l41); + return cs35l41_probe(cs35l41, pdata); } static int cs35l41_spi_remove(struct spi_device *spi) { - struct cs35l41_data *cs35l41 = spi_get_drvdata(spi); + struct cs35l41_private *cs35l41 = spi_get_drvdata(spi); - return cs35l41_dev_exit(cs35l41); + regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); + wm_adsp2_remove(&cs35l41->dsp); + regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies); + snd_soc_unregister_codec(cs35l41->dev); + return 0; } +static const struct of_device_id cs35l41_of_match[] = { + {.compatible = "cirrus,cs35l40"}, + {.compatible = "cirrus,cs35l41"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l41_of_match); + static struct spi_driver cs35l41_spi_driver = { .driver = { .name = "cs35l41", diff --git a/drivers/mfd/cs35l41-tables.c b/sound/soc/codecs/cs35l41-tables.c similarity index 98% rename from drivers/mfd/cs35l41-tables.c rename to sound/soc/codecs/cs35l41-tables.c index b2c104b4b2d6..e3e09f055067 100644 --- a/drivers/mfd/cs35l41-tables.c +++ b/sound/soc/codecs/cs35l41-tables.c @@ -1,7 +1,7 @@ /* * cs35l41-tables.c -- CS35L41 ALSA SoC audio driver * - * Copyright 2016 Cirrus Logic, Inc. + * Copyright 2018 Cirrus Logic, Inc. * * Author: Brian Austin * David Rhodes @@ -12,7 +12,7 @@ * */ -#include +#include "cs35l41.h" const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG] = { {CS35L41_TEST_KEY_CTL, 0x00000000}, @@ -665,13 +665,15 @@ bool cs35l41_volatile_reg(struct device *dev, unsigned int reg) case CS35L41_DSP1_YMEM_UNPACK24_0 ... CS35L41_DSP1_YMEM_UNPACK24_2045: case CS35L41_DSP1_PMEM_0 ... CS35L41_DSP1_PMEM_5114: case CS35L41_DSP1_CCM_CORE_CTRL ... CS35L41_DSP1_WDT_STATUS: + case CS35L41_OTP_MEM0 ... CS35L41_OTP_MEM31: return true; default: return false; } } -static const struct otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = { +static const struct cs35l41_otp_packed_element_t + otp_map_1[CS35L41_NUM_OTP_ELEM] = { /* addr shift size */ {0x00002030, 0, 4}, /*TRIM_OSC_FREQ_TRIM*/ {0x00002030, 7, 1}, /*TRIM_OSC_TRIM_DONE*/ @@ -774,7 +776,8 @@ static const struct otp_packed_element_t otp_map_1[CS35L41_NUM_OTP_ELEM] = { {0x00017044, 0, 24}, /*LOT_NUMBER*/ }; -static const struct otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = { +static const struct cs35l41_otp_packed_element_t + otp_map_2[CS35L41_NUM_OTP_ELEM] = { /* addr shift size */ {0x00002030, 0, 4}, /*TRIM_OSC_FREQ_TRIM*/ {0x00002030, 7, 1}, /*TRIM_OSC_TRIM_DONE*/ @@ -877,20 +880,34 @@ static const struct otp_packed_element_t otp_map_2[CS35L41_NUM_OTP_ELEM] = { {0x00017044, 0, 24}, /*LOT_NUMBER*/ }; -const struct otp_map_element_t otp_map_map[3] = { +const struct cs35l41_otp_map_element_t + cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS] = { { .id = 0x01, .map = otp_map_1, .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, }, { .id = 0x02, .map = otp_map_2, .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, }, { .id = 0x03, .map = otp_map_2, .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, + }, + { + .id = 0x06, + .map = otp_map_2, + .num_elements = CS35L41_NUM_OTP_ELEM, + .bit_offset = 16, + .word_offset = 2, }, }; diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c index e5e1e6e26420..09bf36acc551 100644 --- a/sound/soc/codecs/cs35l41.c +++ b/sound/soc/codecs/cs35l41.c @@ -1,7 +1,7 @@ /* * cs35l41.c -- CS35l41 ALSA SoC audio driver * - * Copyright 2017 Cirrus Logic, Inc. + * Copyright 2018 Cirrus Logic, Inc. * * Author: David Rhodes * Brian Austin @@ -35,38 +35,18 @@ #include #include #include -#include #include #include #include +#include -#include -#include #include "wm_adsp.h" +#include "cs35l41.h" +#include -struct cs35l41_private { - struct wm_adsp dsp; /* needs to be first member */ - struct snd_soc_codec *codec; - struct cs35l41_platform_data pdata; - struct device *dev; - struct regmap *regmap; - struct regulator_bulk_data supplies[2]; - int num_supplies; - int irq; - int clksrc; - int extclk_freq; - int extclk_cfg; - int sclk; - bool tdm_mode; - bool i2s_mode; - bool swire_mode; - bool halo_booted; - /* GPIO for /RST */ - struct gpio_desc *reset_gpio; - struct completion global_pup_done; - struct completion global_pdn_done; - - struct mutex rate_lock; +static const char * const cs35l41_supplies[] = { + "VA", + "VP", }; struct cs35l41_pll_sysclk_config { @@ -141,6 +121,23 @@ static const struct cs35l41_pll_sysclk_config cs35l41_pll_sysclk[] = { { 27000000, 0x3F }, }; +static const unsigned char cs35l41_bst_k1_table[4][5] = { + {0x24, 0x32, 0x32, 0x4F, 0x57}, + {0x24, 0x32, 0x32, 0x4F, 0x57}, + {0x40, 0x32, 0x32, 0x4F, 0x57}, + {0x40, 0x32, 0x32, 0x4F, 0x57} +}; + +static const unsigned char cs35l41_bst_k2_table[4][5] = { + {0x24, 0x49, 0x66, 0xA3, 0xEA}, + {0x24, 0x49, 0x66, 0xA3, 0xEA}, + {0x48, 0x49, 0x66, 0xA3, 0xEA}, + {0x48, 0x49, 0x66, 0xA3, 0xEA} +}; + +static const unsigned char cs35l41_bst_slope_table[4] = { + 0x75, 0x6B, 0x3B, 0x28}; + static int cs35l41_dsp_power_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -183,7 +180,8 @@ static int cs35l41_dsp_load_ev(struct snd_soc_dapm_widget *w, return 0; case SND_SOC_DAPM_PRE_PMD: - regmap_write(cs35l41->regmap, CS35L41_CSPL_COMMAND, CS35L41_CSPL_CMD_MUTE); + regmap_write(cs35l41->regmap, CS35L41_CSPL_COMMAND, + CS35L41_CSPL_CMD_MUTE); default: return 0; } @@ -211,11 +209,9 @@ static int cs35l41_halo_booted_put(struct snd_kcontrol *kcontrol, return 0; } +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 25, 0); static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1); -static const struct snd_kcontrol_new amp_enable_ctrl = - SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); - static const struct snd_kcontrol_new dre_ctrl = SOC_DAPM_SINGLE("DRE Switch", CS35L41_PWR_CTRL3, 20, 1, 0); @@ -226,18 +222,6 @@ static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L41_AMP_DIG_VOL_CTRL, 0, cs35l41_pcm_sftramp_text); -static const char * const cs35l41_amp_mute_text[] = {"Muted", "Unmuted"}; -static const unsigned int cs35l41_amp_mute_values[] = { - CS35L41_AMP_VOL_PCM_MUTE, - CS35L41_AMP_VOL_PCM_DEFAULT}; - -static SOC_VALUE_ENUM_SINGLE_DECL(amp_mute_ctl, - CS35L41_AMP_DIG_VOL_CTRL, - CS35L41_AMP_VOL_PCM_SHIFT, - CS35L41_AMP_VOL_PCM_MASK, - cs35l41_amp_mute_text, - cs35l41_amp_mute_values); - static const char * const cs35l41_pcm_source_texts[] = {"ASP", "DSP"}; static const unsigned int cs35l41_pcm_source_values[] = {0x08, 0x32}; static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum, @@ -246,16 +230,22 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_pcm_source_enum, cs35l41_pcm_source_texts, cs35l41_pcm_source_values); - static const struct snd_kcontrol_new pcm_source_mux = SOC_DAPM_ENUM("PCM Source", cs35l41_pcm_source_enum); -static const char * const cs35l41_tx_input_texts[] = {"Zero", "ASPRX1", "ASPRX2", "VMON", - "IMON", "VPMON", "DSPTX1", "DSPTX2"}; -static const unsigned int cs35l41_tx_input_values[] = {0x00, CS35L41_INPUT_SRC_ASPRX1, CS35L41_INPUT_SRC_ASPRX2, - CS35L41_INPUT_SRC_VMON, CS35L41_INPUT_SRC_IMON, - CS35L41_INPUT_SRC_VPMON, CS35L41_INPUT_DSP_TX1, - CS35L41_INPUT_DSP_TX2}; +static const char * const cs35l41_tx_input_texts[] = {"Zero", "ASPRX1", + "ASPRX2", "VMON", + "IMON", "VPMON", + "DSPTX1", "DSPTX2"}; +static const unsigned int cs35l41_tx_input_values[] = {0x00, + CS35L41_INPUT_SRC_ASPRX1, + CS35L41_INPUT_SRC_ASPRX2, + CS35L41_INPUT_SRC_VMON, + CS35L41_INPUT_SRC_IMON, + CS35L41_INPUT_SRC_VPMON, + CS35L41_INPUT_DSP_TX1, + CS35L41_INPUT_DSP_TX2}; + static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx1_enum, CS35L41_ASP_TX1_SRC, 0, CS35L41_ASP_SOURCE_MASK, @@ -292,16 +282,9 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_asptx4_enum, static const struct snd_kcontrol_new asp_tx4_mux = SOC_DAPM_ENUM("ASPTX4 SRC", cs35l41_asptx4_enum); -static SOC_VALUE_ENUM_SINGLE_DECL(cs35l41_dsprx2_enum, - CS35L41_DSP1_RX2_SRC, - 0, CS35L41_ASP_SOURCE_MASK, - cs35l41_tx_input_texts, - cs35l41_tx_input_values); - -static const struct snd_kcontrol_new dsp_rx2_mux = - SOC_DAPM_ENUM("DSPRX2 SRC", cs35l41_dsprx2_enum); - static const struct snd_kcontrol_new cs35l41_aud_controls[] = { + SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L41_AMP_DIG_VOL_CTRL, + 3, 0x4D0, 0x390, dig_vol_tlv), SOC_SINGLE_TLV("AMP PCM Gain", CS35L41_AMP_GAIN_CTRL, 5, 0x14, 0, amp_gain_tlv), SOC_SINGLE_RANGE("ASPTX1 Slot Position", CS35L41_SP_FRAME_TX_SLOT, 0, @@ -313,19 +296,18 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = { SOC_SINGLE_RANGE("ASPTX4 Slot Position", CS35L41_SP_FRAME_TX_SLOT, 24, 0, 7, 0), SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp), - SOC_ENUM("AMP Mute", amp_mute_ctl), SOC_SINGLE_EXT("DSP Booted", SND_SOC_NOPM, 0, 1, 0, cs35l41_halo_booted_get, cs35l41_halo_booted_put), WM_ADSP2_PRELOAD_SWITCH("DSP1", 1), }; -static const struct otp_map_element_t *find_otp_map(u32 otp_id) +static const struct cs35l41_otp_map_element_t *cs35l41_find_otp_map(u32 otp_id) { int i; - for (i = 0; i < ARRAY_SIZE(otp_map_map); i++) { - if (otp_map_map[i].id == otp_id) - return &otp_map_map[i]; + for (i = 0; i < ARRAY_SIZE(cs35l41_otp_map_map); i++) { + if (cs35l41_otp_map_map[i].id == otp_id) + return &cs35l41_otp_map_map[i]; } return NULL; @@ -336,38 +318,22 @@ static int cs35l41_otp_unpack(void *data) struct cs35l41_private *cs35l41 = data; u32 otp_mem[32]; int i; - /* unpack area starts at byte 10 (0-indexed) */ - int bit_offset = 16, array_offset = 2; + int bit_offset, word_offset; unsigned int bit_sum = 8; u32 otp_val, otp_id_reg; - const struct otp_map_element_t *otp_map_match; - const struct otp_packed_element_t *otp_map; + const struct cs35l41_otp_map_element_t *otp_map_match; + const struct cs35l41_otp_packed_element_t *otp_map; + int ret; + struct spi_device *spi = NULL; + u32 orig_spi_freq = 0; - /* - * We need to make sure we are using the bus - * for these reads and writes so bypass - * cache completely to ensure we hit the - * registers correctly - */ - regcache_cache_bypass(cs35l41->regmap, true); - - regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00005555); - regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x0000AAAA); - - regmap_read(cs35l41->regmap, CS35L41_OTPID, &otp_id_reg); - /* Read from OTP_MEM_IF */ - for (i = 0; i < 32; i++) { - regmap_read(cs35l41->regmap, CS35L41_OTP_MEM0 + i * 4, &(otp_mem[i])); - usleep_range(1, 10); - } - - if (((otp_mem[1] & CS35L41_OTP_HDR_MASK_1) != CS35L41_OTP_HDR_VAL_1) - || (otp_mem[2] & CS35L41_OTP_HDR_MASK_2) != CS35L41_OTP_HDR_VAL_2) { - dev_err(cs35l41->dev, "Bad OTP header vals\n"); + ret = regmap_read(cs35l41->regmap, CS35L41_OTPID, &otp_id_reg); + if (ret < 0) { + dev_err(cs35l41->dev, "Read OTP ID failed\n"); return -EINVAL; } - otp_map_match = find_otp_map(otp_id_reg); + otp_map_match = cs35l41_find_otp_map(otp_id_reg); if (otp_map_match == NULL) { dev_err(cs35l41->dev, "OTP Map matching ID %d not found\n", @@ -375,23 +341,56 @@ static int cs35l41_otp_unpack(void *data) return -EINVAL; } + if (cs35l41->bus_spi) { + spi = to_spi_device(cs35l41->dev); + orig_spi_freq = spi->max_speed_hz; + spi->max_speed_hz = CS35L41_SPI_MAX_FREQ_OTP; + spi_setup(spi); + } + + ret = regmap_bulk_read(cs35l41->regmap, CS35L41_OTP_MEM0, otp_mem, + CS35L41_OTP_SIZE_WORDS); + if (ret < 0) { + dev_err(cs35l41->dev, "Read OTP Mem failed\n"); + return -EINVAL; + } + + if (cs35l41->bus_spi) { + spi->max_speed_hz = orig_spi_freq; + spi_setup(spi); + } + otp_map = otp_map_match->map; + bit_offset = otp_map_match->bit_offset; + word_offset = otp_map_match->word_offset; + + ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000055); + if (ret < 0) { + dev_err(cs35l41->dev, "Write Unlock key failed 1/2\n"); + return -EINVAL; + } + ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000AA); + if (ret < 0) { + dev_err(cs35l41->dev, "Write Unlock key failed 2/2\n"); + return -EINVAL; + } + for (i = 0; i < otp_map_match->num_elements; i++) { - dev_dbg(cs35l41->dev, "bitoffset= %d, array_offset=%d, bit_sum mod 32=%d\n", - bit_offset, array_offset, bit_sum % 32); + dev_dbg(cs35l41->dev, "bitoffset= %d, word_offset=%d, bit_sum mod 32=%d\n", + bit_offset, word_offset, bit_sum % 32); if (bit_offset + otp_map[i].size - 1 >= 32) { - otp_val = (otp_mem[array_offset] & + otp_val = (otp_mem[word_offset] & GENMASK(31, bit_offset)) >> bit_offset; - otp_val |= (otp_mem[++array_offset] & + otp_val |= (otp_mem[++word_offset] & GENMASK(bit_offset + otp_map[i].size - 33, 0)) << (32 - bit_offset); bit_offset += otp_map[i].size - 32; } else { - otp_val = (otp_mem[array_offset] & + otp_val = (otp_mem[word_offset] & GENMASK(bit_offset + otp_map[i].size - 1, bit_offset)) >> bit_offset; bit_offset += otp_map[i].size; @@ -400,21 +399,33 @@ static int cs35l41_otp_unpack(void *data) if (bit_offset == 32) { bit_offset = 0; - array_offset++; + word_offset++; } - if (otp_map[i].reg != 0) - regmap_update_bits(cs35l41->regmap, otp_map[i].reg, - GENMASK(otp_map[i].shift + + if (otp_map[i].reg != 0) { + ret = regmap_update_bits(cs35l41->regmap, + otp_map[i].reg, + GENMASK(otp_map[i].shift + otp_map[i].size - 1, otp_map[i].shift), - otp_val << otp_map[i].shift); + otp_val << otp_map[i].shift); + if (ret < 0) { + dev_err(cs35l41->dev, "Write OTP val failed\n"); + return -EINVAL; + } + } } - regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x0000CCCC); - regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00003333); - - regcache_cache_bypass(cs35l41->regmap, false); + ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x000000CC); + if (ret < 0) { + dev_err(cs35l41->dev, "Write Lock key failed 1/2\n"); + return -EINVAL; + } + ret = regmap_write(cs35l41->regmap, CS35L41_TEST_KEY_CTL, 0x00000033); + if (ret < 0) { + dev_err(cs35l41->dev, "Write Lock key failed 2/2\n"); + return -EINVAL; + } return 0; } @@ -547,19 +558,19 @@ static irqreturn_t cs35l41_irq(int irq, void *data) } static const struct reg_sequence cs35l41_pup_patch[] = { - {0x00000040, 0x00005555}, - {0x00000040, 0x0000AAAA}, - {0x00002084, 0x002F1AA0}, - {0x00000040, 0x0000CCCC}, - {0x00000040, 0x00003333}, + {0x00000040, 0x00000055}, + {0x00000040, 0x000000AA}, + {0x00002084, 0x002F1AA0}, + {0x00000040, 0x000000CC}, + {0x00000040, 0x00000033}, }; static const struct reg_sequence cs35l41_pdn_patch[] = { - {0x00000040, 0x00005555}, - {0x00000040, 0x0000AAAA}, - {0x00002084, 0x002F1AA3}, - {0x00000040, 0x0000CCCC}, - {0x00000040, 0x00003333}, + {0x00000040, 0x00000055}, + {0x00000040, 0x000000AA}, + {0x00002084, 0x002F1AA3}, + {0x00000040, 0x000000CC}, + {0x00000040, 0x00000033}, }; static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, @@ -572,9 +583,9 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - regmap_register_patch(cs35l41->regmap, - cs35l41_pup_patch, - ARRAY_SIZE(cs35l41_pup_patch)); + regmap_multi_reg_write_bypassed(cs35l41->regmap, + cs35l41_pup_patch, + ARRAY_SIZE(cs35l41_pup_patch)); regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK, @@ -584,7 +595,7 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, regmap_read(cs35l41->regmap, CS35L41_IRQ1_RAW_STATUS3, ®); if (reg & CS35L41_PLL_UNLOCK) - dev_crit(cs35l41->dev, "PLL Unlocked\n"); + dev_warn(cs35l41->dev, "PLL Unlocked\n"); break; case SND_SOC_DAPM_POST_PMD: regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL1, @@ -592,9 +603,9 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, usleep_range(1000, 1100); - regmap_register_patch(cs35l41->regmap, - cs35l41_pdn_patch, - ARRAY_SIZE(cs35l41_pdn_patch)); + regmap_multi_reg_write_bypassed(cs35l41->regmap, + cs35l41_pdn_patch, + ARRAY_SIZE(cs35l41_pdn_patch)); break; default: dev_err(codec->dev, "Invalid event = 0x%x\n", event); @@ -606,15 +617,13 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_SPK("DSP1 Preload", NULL), - { .id = snd_soc_dapm_supply, .name = "DSP1 Preloader", - .reg = SND_SOC_NOPM, .shift = 0, .event = cs35l41_dsp_power_ev, - .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD, - .subseq = 100,}, - { .id = snd_soc_dapm_out_drv, .name = "DSP1", - .reg = SND_SOC_NOPM, .shift = 0, .event = cs35l41_dsp_load_ev, - .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }, - - SND_SOC_DAPM_OUTPUT("AMP SPK"), + SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, + SND_SOC_NOPM, 0, 0, cs35l41_dsp_power_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, + cs35l41_dsp_load_ev, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("SPK"), SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, CS35L41_SP_ENABLES, 16, 0), SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, CS35L41_SP_ENABLES, 17, 0), @@ -644,28 +653,17 @@ static const struct snd_soc_dapm_widget cs35l41_dapm_widgets[] = { SND_SOC_DAPM_MUX("ASP TX2 Source", SND_SOC_NOPM, 0, 0, &asp_tx2_mux), SND_SOC_DAPM_MUX("ASP TX3 Source", SND_SOC_NOPM, 0, 0, &asp_tx3_mux), SND_SOC_DAPM_MUX("ASP TX4 Source", SND_SOC_NOPM, 0, 0, &asp_tx4_mux), - SND_SOC_DAPM_MUX("DSP RX2 Source", SND_SOC_NOPM, 0, 0, &dsp_rx2_mux), SND_SOC_DAPM_MUX("PCM Source", SND_SOC_NOPM, 0, 0, &pcm_source_mux), SND_SOC_DAPM_SWITCH("DRE", SND_SOC_NOPM, 0, 0, &dre_ctrl), - SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, &_enable_ctrl), }; static const struct snd_soc_dapm_route cs35l41_audio_map[] = { { "DSP1", NULL, "ASPRX1" }, + { "DSP1", NULL, "ASPRX2" }, { "DSP1", NULL, "DSP1 Preloader" }, { "DSP1 Preload", NULL, "DSP1 Preloader" }, - {"DSP RX2 Source", "VMON", "VMON ADC"}, - {"DSP RX2 Source", "IMON", "IMON ADC"}, - {"DSP RX2 Source", "VPMON", "VPMON ADC"}, - {"DSP RX2 Source", "DSPTX1", "DSP1"}, - {"DSP RX2 Source", "DSPTX2", "DSP1"}, - {"DSP RX2 Source", "ASPRX1", "ASPRX1" }, - {"DSP RX2 Source", "ASPRX2", "ASPRX2" }, - {"DSP RX2 Source", "Zero", "ASPRX1" }, - {"DSP1", NULL, "DSP RX2 Source"}, - {"ASP TX1 Source", "VMON", "VMON ADC"}, {"ASP TX1 Source", "IMON", "IMON ADC"}, {"ASP TX1 Source", "VPMON", "VPMON ADC"}, @@ -676,7 +674,7 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"ASP TX1 Source", "Zero", "ASPRX1" }, {"ASP TX2 Source", "VMON", "VMON ADC"}, {"ASP TX2 Source", "IMON", "IMON ADC"}, - {"ASP TX2 Source", "IMON", "VPMON ADC"}, + {"ASP TX2 Source", "VPMON", "VPMON ADC"}, {"ASP TX2 Source", "DSPTX1", "DSP1"}, {"ASP TX2 Source", "DSPTX2", "DSP1"}, {"ASP TX2 Source", "ASPRX1", "ASPRX1" }, @@ -719,13 +717,12 @@ static const struct snd_soc_dapm_route cs35l41_audio_map[] = { {"DSP1", NULL, "VPMON ADC"}, {"DSP1", NULL, "TEMPMON ADC"}, - {"AMP Enable", "Switch", "AMP Playback"}, - {"ASPRX1", NULL, "AMP Enable"}, - {"ASPRX2", NULL, "AMP Enable"}, + {"ASPRX1", NULL, "AMP Playback"}, + {"ASPRX2", NULL, "AMP Playback"}, {"DRE", "DRE Switch", "CLASS H"}, {"Main AMP", NULL, "CLASS H"}, {"Main AMP", NULL, "DRE"}, - {"AMP SPK", NULL, "Main AMP"}, + {"SPK", NULL, "Main AMP"}, {"PCM Source", "ASP", "ASPRX1"}, {"PCM Source", "DSP", "DSP1"}, @@ -845,28 +842,20 @@ static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream, { struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(dai->codec); int i; - unsigned int rate; + unsigned int rate = params_rate(params); u8 asp_width, asp_wl; - if (cs35l41->pdata.fixed_params) { - rate = cs35l41->pdata.fixed_rate; - asp_width = cs35l41->pdata.fixed_width; - asp_wl = cs35l41->pdata.fixed_wl; - } else { - rate = params_rate(params); - asp_width = params_physical_width(params); - asp_wl = params_width(params); - } - for (i = 0; i < ARRAY_SIZE(cs35l41_fs_rates); i++) { if (rate == cs35l41_fs_rates[i].rate) break; } - if (i < ARRAY_SIZE(cs35l41_fs_rates)) - regmap_update_bits(cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL, + regmap_update_bits(cs35l41->regmap, CS35L41_GLOBAL_CLK_CTRL, CS35L41_GLOBAL_FS_MASK, cs35l41_fs_rates[i].fs_cfg << CS35L41_GLOBAL_FS_SHIFT); + asp_wl = params_width(params); + asp_width = params_physical_width(params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT, CS35L41_ASP_WIDTH_RX_MASK, @@ -874,13 +863,6 @@ static int cs35l41_pcm_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(cs35l41->regmap, CS35L41_SP_RX_WL, CS35L41_ASP_RX_WL_MASK, asp_wl << CS35L41_ASP_RX_WL_SHIFT); - /* setup TX for DSP inputs */ - regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT, - CS35L41_ASP_WIDTH_TX_MASK, - asp_width << CS35L41_ASP_WIDTH_TX_SHIFT); - regmap_update_bits(cs35l41->regmap, CS35L41_SP_TX_WL, - CS35L41_ASP_TX_WL_MASK, - asp_wl << CS35L41_ASP_TX_WL_SHIFT); if (cs35l41->i2s_mode || cs35l41->tdm_mode) { regmap_update_bits(cs35l41->regmap, CS35L41_SP_FRAME_RX_SLOT, @@ -973,10 +955,6 @@ static int cs35l41_codec_set_sysclk(struct snd_soc_codec *codec, return -EINVAL; } - if (cs35l41->clksrc == CS35L41_PLLSRC_SCLK) - regmap_update_bits(cs35l41->regmap, CS35L41_SP_RATE_CTRL, - 0x3F, cs35l41->extclk_cfg); - regmap_update_bits(cs35l41->regmap, CS35L41_PLL_CLK_CTRL, CS35L41_PLL_OPENLOOP_MASK, 1 << CS35L41_PLL_OPENLOOP_SHIFT); @@ -1015,12 +993,126 @@ static int cs35l41_dai_set_sysclk(struct snd_soc_dai *dai, return 0; } +static int cs35l41_boost_config(struct cs35l41_private *cs35l41, + int boost_ind, int boost_cap, int boost_ipk) +{ + int ret; + unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled; + struct regmap *regmap = cs35l41->regmap; + struct device *dev = cs35l41->dev; + + switch (boost_ind) { + case 1000: /* 1.0 uH */ + bst_lbst_val = 0; + break; + case 1200: /* 1.2 uH */ + bst_lbst_val = 1; + break; + case 1500: /* 1.5 uH */ + bst_lbst_val = 2; + break; + case 2200: /* 2.2 uH */ + bst_lbst_val = 3; + break; + default: + dev_err(dev, "Invalid boost inductor value: %d nH\n", + boost_ind); + return -EINVAL; + } + + switch (boost_cap) { + case 0 ... 19: + bst_cbst_range = 0; + break; + case 20 ... 50: + bst_cbst_range = 1; + break; + case 51 ... 100: + bst_cbst_range = 2; + break; + case 101 ... 200: + bst_cbst_range = 3; + break; + default: /* 201 uF and greater */ + bst_cbst_range = 4; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF, + CS35L41_BST_K1_MASK, + cs35l41_bst_k1_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K1_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost K1 coefficient\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_COEFF, + CS35L41_BST_K2_MASK, + cs35l41_bst_k2_table[bst_lbst_val][bst_cbst_range] + << CS35L41_BST_K2_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost K2 coefficient\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST, + CS35L41_BST_SLOPE_MASK, + cs35l41_bst_slope_table[bst_lbst_val] + << CS35L41_BST_SLOPE_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost slope coefficient\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_SLOPE_LBST, + CS35L41_BST_LBST_VAL_MASK, + bst_lbst_val << CS35L41_BST_LBST_VAL_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost inductor value\n"); + return ret; + } + + if ((boost_ipk < 1600) || (boost_ipk > 4500)) { + dev_err(dev, "Invalid boost inductor peak current: %d mA\n", + boost_ipk); + return -EINVAL; + } + bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10; + + ret = regmap_update_bits(regmap, CS35L41_BSTCVRT_PEAK_CUR, + CS35L41_BST_IPK_MASK, + bst_ipk_scaled << CS35L41_BST_IPK_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost inductor peak current\n"); + return ret; + } + + return 0; +} + static int cs35l41_codec_probe(struct snd_soc_codec *codec) { struct cs35l41_private *cs35l41 = snd_soc_codec_get_drvdata(codec); struct classh_cfg *classh = &cs35l41->pdata.classh_config; + int ret; /* Set Platform Data */ + /* Required */ + if (cs35l41->pdata.bst_ipk && + cs35l41->pdata.bst_ind && cs35l41->pdata.bst_cap) { + ret = cs35l41_boost_config(cs35l41, cs35l41->pdata.bst_ind, + cs35l41->pdata.bst_cap, + cs35l41->pdata.bst_ipk); + if (ret) { + dev_err(cs35l41->dev, "Error in Boost DT config\n"); + return ret; + } + } else { + dev_err(cs35l41->dev, "Incomplete Boost component DT config\n"); + return -EINVAL; + } + + /* Optional */ if (cs35l41->pdata.sclk_frc) regmap_update_bits(cs35l41->regmap, CS35L41_SP_FORMAT, CS35L41_SCLK_FRC_MASK, @@ -1043,10 +1135,6 @@ static int cs35l41_codec_probe(struct snd_soc_codec *codec) regmap_update_bits(cs35l41->regmap, CS35L41_BSTCVRT_VCTRL1, CS35L41_BST_CTL_MASK, cs35l41->pdata.bst_vctrl); - if (cs35l41->pdata.bst_ipk) - regmap_update_bits(cs35l41->regmap, CS35L41_BSTCVRT_PEAK_CUR, - CS35L41_BST_IPK_MASK, cs35l41->pdata.bst_ipk); - if (cs35l41->pdata.temp_warn_thld) regmap_update_bits(cs35l41->regmap, CS35L41_DTEMP_WARN_THLD, CS35L41_TEMP_THLD_MASK, @@ -1058,62 +1146,41 @@ static int cs35l41_codec_probe(struct snd_soc_codec *codec) CS35L41_ASP_DOUT_HIZ_MASK, cs35l41->pdata.dout_hiz); - if (cs35l41->pdata.dsp_ng_enable) { + if (cs35l41->pdata.ng_enable) { regmap_update_bits(cs35l41->regmap, CS35L41_MIXER_NGATE_CH1_CFG, - CS35L41_DSP_NG_ENABLE_MASK, - CS35L41_DSP_NG_ENABLE_MASK); + CS35L41_NG_ENABLE_MASK, + CS35L41_NG_ENABLE_MASK); regmap_update_bits(cs35l41->regmap, CS35L41_MIXER_NGATE_CH2_CFG, - CS35L41_DSP_NG_ENABLE_MASK, - CS35L41_DSP_NG_ENABLE_MASK); + CS35L41_NG_ENABLE_MASK, + CS35L41_NG_ENABLE_MASK); - if (cs35l41->pdata.dsp_ng_pcm_thld) { + if (cs35l41->pdata.ng_pcm_thld) { regmap_update_bits(cs35l41->regmap, CS35L41_MIXER_NGATE_CH1_CFG, - CS35L41_DSP_NG_THLD_MASK, - cs35l41->pdata.dsp_ng_pcm_thld); + CS35L41_NG_THLD_MASK, + cs35l41->pdata.ng_pcm_thld); regmap_update_bits(cs35l41->regmap, CS35L41_MIXER_NGATE_CH2_CFG, - CS35L41_DSP_NG_THLD_MASK, - cs35l41->pdata.dsp_ng_pcm_thld); + CS35L41_NG_THLD_MASK, + cs35l41->pdata.ng_pcm_thld); } - if (cs35l41->pdata.dsp_ng_delay) { + if (cs35l41->pdata.ng_delay) { regmap_update_bits(cs35l41->regmap, CS35L41_MIXER_NGATE_CH1_CFG, - CS35L41_DSP_NG_DELAY_MASK, - cs35l41->pdata.dsp_ng_delay << - CS35L41_DSP_NG_DELAY_SHIFT); + CS35L41_NG_DELAY_MASK, + cs35l41->pdata.ng_delay << + CS35L41_NG_DELAY_SHIFT); regmap_update_bits(cs35l41->regmap, CS35L41_MIXER_NGATE_CH2_CFG, - CS35L41_DSP_NG_DELAY_MASK, - cs35l41->pdata.dsp_ng_delay << - CS35L41_DSP_NG_DELAY_SHIFT); + CS35L41_NG_DELAY_MASK, + cs35l41->pdata.ng_delay << + CS35L41_NG_DELAY_SHIFT); } } - if (cs35l41->pdata.hw_ng_sel) - regmap_update_bits(cs35l41->regmap, - CS35L41_NG_CFG, - CS35L41_HW_NG_SEL_MASK, - cs35l41->pdata.hw_ng_sel << - CS35L41_HW_NG_SEL_SHIFT); - - if (cs35l41->pdata.hw_ng_thld) - regmap_update_bits(cs35l41->regmap, - CS35L41_NG_CFG, - CS35L41_HW_NG_THLD_MASK, - cs35l41->pdata.hw_ng_thld << - CS35L41_HW_NG_THLD_SHIFT); - - if (cs35l41->pdata.hw_ng_delay) - regmap_update_bits(cs35l41->regmap, - CS35L41_NG_CFG, - CS35L41_HW_NG_DLY_MASK, - cs35l41->pdata.hw_ng_delay << - CS35L41_HW_NG_DLY_SHIFT); - if (classh->classh_algo_enable) { if (classh->classh_bst_override) regmap_update_bits(cs35l41->regmap, @@ -1158,16 +1225,16 @@ static int cs35l41_codec_probe(struct snd_soc_codec *codec) CS35L41_CH_WKFET_THLD_SHIFT); } - wm_adsp2_codec_probe(&cs35l41->dsp, codec, false); + wm_adsp2_codec_probe(&cs35l41->dsp, codec); return 0; } static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41) { - struct cs35l41_irq_cfg *irq_gpio_cfg1 = &cs35l41->pdata.irq_config1; - struct cs35l41_irq_cfg *irq_gpio_cfg2 = &cs35l41->pdata.irq_config2; - int irq_pol = 0; + struct irq_cfg *irq_gpio_cfg1 = &cs35l41->pdata.irq_config1; + struct irq_cfg *irq_gpio_cfg2 = &cs35l41->pdata.irq_config2; + int irq_pol = IRQF_TRIGGER_NONE; if (irq_gpio_cfg1->is_present) { if (irq_gpio_cfg1->irq_pol_inv) @@ -1205,7 +1272,6 @@ static int cs35l41_irq_gpio_config(struct cs35l41_private *cs35l41) CS35L41_GPIO2_CTRL_MASK, irq_gpio_cfg2->irq_src_sel << CS35L41_GPIO2_CTRL_SHIFT); - } if (irq_gpio_cfg2->irq_src_sel == CS35L41_GPIO_CTRL_ACTV_LO) @@ -1250,23 +1316,13 @@ static struct snd_soc_dai_driver cs35l41_dai[] = { .formats = CS35L41_TX_FORMATS, }, .ops = &cs35l41_ops, + .symmetric_rates = 1, }, }; -static struct regmap *cs35l41_get_regmap(struct device *dev) -{ - struct cs35l41_private *cs35l41 = dev_get_drvdata(dev); - - return cs35l41->regmap; -} - static struct snd_soc_codec_driver soc_codec_dev_cs35l41 = { .probe = cs35l41_codec_probe, .remove = cs35l41_codec_remove, - .get_regmap = cs35l41_get_regmap, - - .set_sysclk = cs35l41_codec_set_sysclk, - .component_driver = { .dapm_widgets = cs35l41_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(cs35l41_dapm_widgets), @@ -1276,8 +1332,8 @@ static struct snd_soc_codec_driver soc_codec_dev_cs35l41 = { .controls = cs35l41_aud_controls, .num_controls = ARRAY_SIZE(cs35l41_aud_controls), }, - - .ignore_pmdown_time = true, + .set_sysclk = cs35l41_codec_set_sysclk, + .ignore_pmdown_time = false, }; static int cs35l41_handle_of_data(struct device *dev, @@ -1286,10 +1342,10 @@ static int cs35l41_handle_of_data(struct device *dev, struct device_node *np = dev->of_node; unsigned int val; int ret; - struct device_node *classh, *irq_gpio1, *irq_gpio2, *fixed_params; + struct device_node *sub_node; struct classh_cfg *classh_config = &pdata->classh_config; - struct cs35l41_irq_cfg *irq_gpio1_config = &pdata->irq_config1; - struct cs35l41_irq_cfg *irq_gpio2_config = &pdata->irq_config2; + struct irq_cfg *irq_gpio1_config = &pdata->irq_config1; + struct irq_cfg *irq_gpio2_config = &pdata->irq_config2; if (!np) return 0; @@ -1317,14 +1373,16 @@ static int cs35l41_handle_of_data(struct device *dev, } ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val); - if (ret >= 0) { - if (val < 1600 || val > 4500) { - dev_err(dev, - "Invalid Boost Peak Current %u mA\n", val); - return -EINVAL; - } - pdata->bst_ipk = ((val - 1600) / 50) + 0x10; - } + if (ret >= 0) + pdata->bst_ipk = val; + + ret = of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val); + if (ret >= 0) + pdata->bst_ind = val; + + ret = of_property_read_u32(np, "cirrus,boost-cap-microfarad", &val); + if (ret >= 0) + pdata->bst_cap = val; ret = of_property_read_u32(np, "cirrus,asp-sdout-hiz", &val); if (ret >= 0) @@ -1332,28 +1390,22 @@ static int cs35l41_handle_of_data(struct device *dev, else pdata->dout_hiz = -1; - pdata->dsp_ng_enable = of_property_read_bool(np, - "cirrus,dsp-noise-gate-enable"); - if (of_property_read_u32(np, "cirrus,dsp-noise-gate-threshold", &val) >= 0) - pdata->dsp_ng_pcm_thld = val | CS35L41_VALID_PDATA; - if (of_property_read_u32(np, "cirrus,dsp-noise-gate-delay", &val) >= 0) - pdata->dsp_ng_delay = val | CS35L41_VALID_PDATA; + pdata->ng_enable = of_property_read_bool(np, + "cirrus,noise-gate-enable"); + if (of_property_read_u32(np, "cirrus,noise-gate-threshold", &val) >= 0) + pdata->ng_pcm_thld = val | CS35L41_VALID_PDATA; + if (of_property_read_u32(np, "cirrus,noise-gate-delay", &val) >= 0) + pdata->ng_delay = val | CS35L41_VALID_PDATA; - if (of_property_read_u32(np, "cirrus,hw-noise-gate-select", &val) >= 0) - pdata->hw_ng_sel = val | CS35L41_VALID_PDATA; - if (of_property_read_u32(np, "cirrus,hw-noise-gate-threshold", &val) >= 0) - pdata->hw_ng_thld = val | CS35L41_VALID_PDATA; - if (of_property_read_u32(np, "cirrus,hw-noise-gate-delay", &val) >= 0) - pdata->hw_ng_delay = val | CS35L41_VALID_PDATA; - - classh = of_get_child_by_name(np, "cirrus,classh-internal-algo"); - classh_config->classh_algo_enable = classh ? true : false; + sub_node = of_get_child_by_name(np, "cirrus,classh-internal-algo"); + classh_config->classh_algo_enable = sub_node ? true : false; if (classh_config->classh_algo_enable) { classh_config->classh_bst_override = - of_property_read_bool(classh, "cirrus,classh-bst-overide"); + of_property_read_bool(sub_node, + "cirrus,classh-bst-overide"); - ret = of_property_read_u32(classh, + ret = of_property_read_u32(sub_node, "cirrus,classh-bst-max-limit", &val); if (ret >= 0) { @@ -1361,127 +1413,76 @@ static int cs35l41_handle_of_data(struct device *dev, classh_config->classh_bst_max_limit = val; } - ret = of_property_read_u32(classh, "cirrus,classh-mem-depth", + ret = of_property_read_u32(sub_node, "cirrus,classh-mem-depth", &val); if (ret >= 0) { val |= CS35L41_VALID_PDATA; classh_config->classh_mem_depth = val; } - ret = of_property_read_u32(classh, "cirrus,classh-release-rate", - &val); + ret = of_property_read_u32(sub_node, + "cirrus,classh-release-rate", &val); if (ret >= 0) classh_config->classh_release_rate = val; - ret = of_property_read_u32(classh, "cirrus,classh-headroom", + ret = of_property_read_u32(sub_node, "cirrus,classh-headroom", &val); if (ret >= 0) { val |= CS35L41_VALID_PDATA; classh_config->classh_headroom = val; } - ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay", - &val); + ret = of_property_read_u32(sub_node, + "cirrus,classh-wk-fet-delay", &val); if (ret >= 0) { val |= CS35L41_VALID_PDATA; classh_config->classh_wk_fet_delay = val; } - ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld", - &val); + ret = of_property_read_u32(sub_node, + "cirrus,classh-wk-fet-thld", &val); if (ret >= 0) classh_config->classh_wk_fet_thld = val; } - of_node_put(classh); + of_node_put(sub_node); /* GPIO1 Pin Config */ - irq_gpio1 = of_get_child_by_name(np, "cirrus,gpio-config1"); - irq_gpio1_config->is_present = irq_gpio1 ? true : false; + sub_node = of_get_child_by_name(np, "cirrus,gpio-config1"); + irq_gpio1_config->is_present = sub_node ? true : false; if (irq_gpio1_config->is_present) { - irq_gpio1_config->irq_pol_inv = of_property_read_bool(irq_gpio1, + irq_gpio1_config->irq_pol_inv = of_property_read_bool(sub_node, "cirrus,gpio-polarity-invert"); - irq_gpio1_config->irq_out_en = of_property_read_bool(irq_gpio1, + irq_gpio1_config->irq_out_en = of_property_read_bool(sub_node, "cirrus,gpio-output-enable"); - ret = of_property_read_u32(irq_gpio1, "cirrus,gpio-src-select", + ret = of_property_read_u32(sub_node, "cirrus,gpio-src-select", &val); if (ret >= 0) { val |= CS35L41_VALID_PDATA; irq_gpio1_config->irq_src_sel = val; } } - of_node_put(irq_gpio1); + of_node_put(sub_node); /* GPIO2 Pin Config */ - irq_gpio2 = of_get_child_by_name(np, "cirrus,gpio-config2"); - irq_gpio2_config->is_present = irq_gpio2 ? true : false; + sub_node = of_get_child_by_name(np, "cirrus,gpio-config2"); + irq_gpio2_config->is_present = sub_node ? true : false; if (irq_gpio2_config->is_present) { - irq_gpio2_config->irq_pol_inv = of_property_read_bool(irq_gpio2, + irq_gpio2_config->irq_pol_inv = of_property_read_bool(sub_node, "cirrus,gpio-polarity-invert"); - irq_gpio2_config->irq_out_en = of_property_read_bool(irq_gpio2, + irq_gpio2_config->irq_out_en = of_property_read_bool(sub_node, "cirrus,gpio-output-enable"); - ret = of_property_read_u32(irq_gpio2, "cirrus,gpio-src-select", + ret = of_property_read_u32(sub_node, "cirrus,gpio-src-select", &val); if (ret >= 0) { val |= CS35L41_VALID_PDATA; irq_gpio2_config->irq_src_sel = val; } } - of_node_put(irq_gpio2); - - fixed_params = of_get_child_by_name(np, "cirrus,fixed-hw-params"); - pdata->fixed_params = fixed_params ? true : false; - if (fixed_params) { - ret = of_property_read_u32(fixed_params, - "cirrus,fixed-rate", - &val); - if (ret >= 0) - pdata->fixed_rate = val; - - ret = of_property_read_u32(fixed_params, - "cirrus,fixed-width", - &val); - if (ret >= 0) - pdata->fixed_width = val; - - ret = of_property_read_u32(fixed_params, - "cirrus,fixed-wl", - &val); - if (ret >= 0) - pdata->fixed_wl = val; - } - of_node_put(fixed_params); - - ret = of_property_read_string(np, "cirrus,dsp-part-name", - &pdata->dsp_part_name); - if (ret < 0) - pdata->dsp_part_name = "cs35l41"; + of_node_put(sub_node); return 0; } -static const struct of_device_id cs35l41_of_match[] = { - {.compatible = "cirrus,cs35l40"}, - {.compatible = "cirrus,cs35l41"}, - {}, -}; -MODULE_DEVICE_TABLE(of, cs35l41_of_match); - -static const struct spi_device_id cs35l41_id_spi[] = { - {"cs35l40", 0}, - {"cs35l41", 0}, - {} -}; - -MODULE_DEVICE_TABLE(spi, cs35l41_id_spi); - -static const struct i2c_device_id cs35l41_id_i2c[] = { - {"cs35l40", 0}, - {"cs35l41", 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c); - static const struct reg_sequence cs35l41_reva0_errata_patch[] = { {0x00000040, 0x00005555}, {0x00000040, 0x0000AAAA}, @@ -1502,9 +1503,8 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) int ret; dsp = &cs35l41->dsp; - dsp->part = cs35l41->pdata.dsp_part_name; + dsp->part = "cs35l41"; dsp->num = 1; - dsp->suffix = ""; dsp->type = WMFW_HALO; dsp->rev = 0; dsp->dev = cs35l41->dev; @@ -1514,50 +1514,50 @@ static int cs35l41_dsp_init(struct cs35l41_private *cs35l41) dsp->base_sysinfo = CS35L41_DSP1_SYS_ID; dsp->mem = cs35l41_dsp1_regions; dsp->num_mems = ARRAY_SIZE(cs35l41_dsp1_regions); - cs35l41->halo_booted = false; dsp->unlock_all = true; dsp->n_rx_channels = CS35L41_DSP_N_RX_RATES; dsp->n_tx_channels = CS35L41_DSP_N_TX_RATES; ret = wm_halo_init(dsp, &cs35l41->rate_lock); + cs35l41->halo_booted = false; - regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); - regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, CS35L41_INPUT_SRC_CLASSH); - regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, CS35L41_INPUT_SRC_TEMPMON); - regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC, CS35L41_INPUT_SRC_RSVD); + regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, + CS35L41_INPUT_SRC_VPMON); + regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, + CS35L41_INPUT_SRC_CLASSH); + regmap_write(cs35l41->regmap, CS35L41_DSP1_RX7_SRC, + CS35L41_INPUT_SRC_TEMPMON); + regmap_write(cs35l41->regmap, CS35L41_DSP1_RX8_SRC, + CS35L41_INPUT_SRC_RSVD); return ret; } -static int cs35l41_probe(struct platform_device *pdev) +int cs35l41_probe(struct cs35l41_private *cs35l41, + struct cs35l41_platform_data *pdata) { - struct cs35l41_data *cs35l41_mfd = dev_get_drvdata(pdev->dev.parent); - struct cs35l41_private *cs35l41; int ret; u32 regid, reg_revid, i, mtl_revid, int_status, chipid_match; int timeout = 100; - int irq_pol = IRQF_TRIGGER_HIGH; + int irq_pol = 0; - cs35l41 = devm_kzalloc(&pdev->dev, sizeof(struct cs35l41_private), GFP_KERNEL); - if (!cs35l41) - return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(cs35l41_supplies); i++) + cs35l41->supplies[i].supply = cs35l41_supplies[i]; - platform_set_drvdata(pdev, cs35l41); + cs35l41->num_supplies = ARRAY_SIZE(cs35l41_supplies); - mutex_init(&cs35l41->rate_lock); + ret = devm_regulator_bulk_get(cs35l41->dev, cs35l41->num_supplies, + cs35l41->supplies); + if (ret != 0) { + dev_err(cs35l41->dev, + "Failed to request core supplies: %d\n", + ret); + return ret; + } - cs35l41->regmap = cs35l41_mfd->regmap; - cs35l41->dev = cs35l41_mfd->dev; - cs35l41->irq = cs35l41_mfd->irq; - cs35l41->num_supplies = cs35l41_mfd->num_supplies; - cs35l41->reset_gpio = cs35l41_mfd->reset_gpio; - - for (i = 0; i < cs35l41->num_supplies; i++) - cs35l41->supplies[i].supply = cs35l41_mfd->supplies[i].supply; - - pdev->dev.of_node = cs35l41->dev->of_node; - - if (cs35l41->dev->of_node) { + if (pdata) { + cs35l41->pdata = *pdata; + } else if (cs35l41->dev->of_node) { ret = cs35l41_handle_of_data(cs35l41->dev, &cs35l41->pdata); if (ret != 0) return ret; @@ -1566,6 +1566,36 @@ static int cs35l41_probe(struct platform_device *pdev) goto err; } + ret = regulator_bulk_enable(cs35l41->num_supplies, cs35l41->supplies); + if (ret != 0) { + dev_err(cs35l41->dev, + "Failed to enable core supplies: %d\n", ret); + return ret; + } + + /* returning NULL can be an option if in stereo mode */ + cs35l41->reset_gpio = devm_gpiod_get_optional(cs35l41->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l41->reset_gpio)) { + ret = PTR_ERR(cs35l41->reset_gpio); + cs35l41->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(cs35l41->dev, + "Reset line busy, assuming shared reset\n"); + } else { + dev_err(cs35l41->dev, + "Failed to get reset GPIO: %d\n", ret); + goto err; + } + } + if (cs35l41->reset_gpio) { + /* satisfy minimum reset pulse width spec */ + usleep_range(2000, 2100); + gpiod_set_value_cansleep(cs35l41->reset_gpio, 1); + } + + usleep_range(2000, 2100); + ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, ®id); if (ret < 0) { dev_err(cs35l41->dev, "Get Device ID failed\n"); @@ -1597,8 +1627,8 @@ static int cs35l41_probe(struct platform_device *pdev) init_completion(&cs35l41->global_pup_done); ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, - cs35l41_irq, IRQF_ONESHOT | IRQF_SHARED | - irq_pol, "cs35l41", cs35l41); + cs35l41_irq, IRQF_ONESHOT | irq_pol, + "cs35l41", cs35l41); /* CS35L41 needs INT for PDN_DONE */ if (ret != 0) { @@ -1610,18 +1640,14 @@ static int cs35l41_probe(struct platform_device *pdev) regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, CS35L41_INT1_MASK_DEFAULT); - cs35l41_dsp_init(cs35l41); - - if (ret != 0) - goto err; - switch (reg_revid) { case CS35L41_REVID_A0: ret = regmap_register_patch(cs35l41->regmap, cs35l41_reva0_errata_patch, ARRAY_SIZE(cs35l41_reva0_errata_patch)); if (ret < 0) { - dev_err(cs35l41->dev, "Failed to apply A0 errata patch %d\n", ret); + dev_err(cs35l41->dev, + "Failed to apply A0 errata patch %d\n", ret); goto err; } } @@ -1630,6 +1656,7 @@ static int cs35l41_probe(struct platform_device *pdev) if (timeout == 0) { dev_err(cs35l41->dev, "Timeout waiting for OTP_BOOT_DONE\n"); + ret = -EBUSY; goto err; } usleep_range(1000, 1100); @@ -1637,44 +1664,38 @@ static int cs35l41_probe(struct platform_device *pdev) timeout--; } while (!(int_status & CS35L41_OTP_BOOT_DONE)); - cs35l41_otp_unpack(cs35l41); + regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_status); + if (int_status & CS35L41_OTP_BOOT_ERR) { + dev_err(cs35l41->dev, "OTP Boot error\n"); + ret = -EINVAL; + goto err; + } - ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs35l41, - cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); + ret = cs35l41_otp_unpack(cs35l41); + if (ret < 0) { + dev_err(cs35l41->dev, "OTP Unpack failed\n"); + goto err; + } + + mutex_init(&cs35l41->rate_lock); + + cs35l41_dsp_init(cs35l41); + + ret = snd_soc_register_codec(cs35l41->dev, &soc_codec_dev_cs35l41, + cs35l41_dai, ARRAY_SIZE(cs35l41_dai)); if (ret < 0) { dev_err(cs35l41->dev, "%s: Register codec failed\n", __func__); goto err; } dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", - regid, reg_revid >> 8); + regid, reg_revid); err: + regulator_bulk_disable(cs35l41->num_supplies, cs35l41->supplies); return ret; } -static int cs35l41_remove(struct platform_device *pdev) -{ - struct cs35l41_private *cs35l41 = platform_get_drvdata(pdev); - - regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF); - wm_adsp2_remove(&cs35l41->dsp); - snd_soc_unregister_codec(&pdev->dev); - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver cs35l41_platform_driver = { - .driver = { - .name = "cs35l41-codec", - .owner = THIS_MODULE, - }, - .probe = cs35l41_probe, - .remove = cs35l41_remove, -}; - -module_platform_driver(cs35l41_platform_driver); - MODULE_DESCRIPTION("ASoC CS35L41 driver"); MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, "); MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/cs35l41/registers.h b/sound/soc/codecs/cs35l41.h similarity index 96% rename from include/linux/mfd/cs35l41/registers.h rename to sound/soc/codecs/cs35l41.h index 0e5f83096f85..a51cf7d21c95 100644 --- a/include/linux/mfd/cs35l41/registers.h +++ b/sound/soc/codecs/cs35l41.h @@ -1,7 +1,7 @@ /* - * cs35l41.h -- CS35L41 definitions + * cs35l41.h -- CS35L41 ALSA SoC audio driver * - * Copyright 2016 Cirrus Logic, Inc. + * Copyright 2018 Cirrus Logic, Inc. * * Author: Brian Austin * David Rhodes @@ -536,6 +536,7 @@ #define CS35L41_MAX_CACHE_REG 0x0000006F #define CS35L41_OTP_SIZE_WORDS 32 #define CS35L41_NUM_OTP_ELEM 100 +#define CS35L41_NUM_OTP_MAPS 4 #define CS35L41_VALID_PDATA 0x80000000 @@ -560,8 +561,17 @@ #define CS35L41_BST_CTL_SEL_REG 0x00 #define CS35L41_BST_CTL_SEL_CLASSH 0x01 #define CS35L41_BST_IPK_MASK 0x7F +#define CS35L41_BST_IPK_SHIFT 0 #define CS35L41_BST_LIM_MASK 0x4 #define CS35L41_BST_LIM_SHIFT 2 +#define CS35L41_BST_K1_MASK 0x000000FF +#define CS35L41_BST_K1_SHIFT 0 +#define CS35L41_BST_K2_MASK 0x0000FF00 +#define CS35L41_BST_K2_SHIFT 8 +#define CS35L41_BST_SLOPE_MASK 0x0000FF00 +#define CS35L41_BST_SLOPE_SHIFT 8 +#define CS35L41_BST_LBST_VAL_MASK 0x00000003 +#define CS35L41_BST_LBST_VAL_SHIFT 0 #define CS35L41_TEMP_THLD_MASK 0x03 #define CS35L41_VMON_IMON_VOL_MASK 0x07FF07FF @@ -579,19 +589,12 @@ #define CS35L41_CH_WKFET_THLD_MASK 0x0F00 #define CS35L41_CH_WKFET_THLD_SHIFT 8 -#define CS35L41_HW_NG_SEL_MASK 0x3F00 -#define CS35L41_HW_NG_SEL_SHIFT 8 -#define CS35L41_HW_NG_DLY_MASK 0x0070 -#define CS35L41_HW_NG_DLY_SHIFT 4 -#define CS35L41_HW_NG_THLD_MASK 0x0007 -#define CS35L41_HW_NG_THLD_SHIFT 0 - -#define CS35L41_DSP_NG_ENABLE_MASK 0x00010000 -#define CS35L41_DSP_NG_ENABLE_SHIFT 16 -#define CS35L41_DSP_NG_THLD_MASK 0x7 -#define CS35L41_DSP_NG_THLD_SHIFT 0 -#define CS35L41_DSP_NG_DELAY_MASK 0x0F00 -#define CS35L41_DSP_NG_DELAY_SHIFT 8 +#define CS35L41_NG_ENABLE_MASK 0x00010000 +#define CS35L41_NG_ENABLE_SHIFT 16 +#define CS35L41_NG_THLD_MASK 0x7 +#define CS35L41_NG_THLD_SHIFT 0 +#define CS35L41_NG_DELAY_MASK 0x0F00 +#define CS35L41_NG_DELAY_SHIFT 8 #define CS35L41_ASP_FMT_MASK 0x0700 #define CS35L41_ASP_FMT_SHIFT 8 @@ -655,11 +658,6 @@ #define CS35L36_PUP_DONE_IRQ_UNMASK 0x5F #define CS35L36_PUP_DONE_IRQ_MASK 0xBF -#define CS35L41_AMP_VOL_PCM_SHIFT 0x03 -#define CS35L41_AMP_VOL_PCM_MASK 0x7FF -#define CS35L41_AMP_VOL_PCM_DEFAULT 0x0000 -#define CS35L41_AMP_VOL_PCM_MUTE 0x0400 - #define CS35L41_AMP_SHORT_ERR 0x80000000 #define CS35L41_BST_SHORT_ERR 0x0100 #define CS35L41_TEMP_WARN 0x8000 @@ -667,7 +665,8 @@ #define CS35L41_BST_OVP_ERR 0x40 #define CS35L41_BST_DCM_UVP_ERR 0x80 #define CS35L41_OTP_BOOT_DONE 0x02 -#define CS35L41_PLL_UNLOCK 0x10 +#define CS35L41_PLL_UNLOCK 0x10 +#define CS35L41_OTP_BOOT_ERR 0x80000000 #define CS35L41_AMP_SHORT_ERR_RLS 0x02 #define CS35L41_BST_SHORT_ERR_RLS 0x04 @@ -699,6 +698,8 @@ #define CS35L41_DSP_N_TX_RATES 8 #define CS35L41_HALO_CORE_RESET 0x00000200 +#define CS35L41_SPI_MAX_FREQ_OTP 4000000 + #define CS35L41_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) #define CS35L41_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ | SNDRV_PCM_FMTBIT_S32_LE) @@ -706,25 +707,23 @@ bool cs35l41_readable_reg(struct device *dev, unsigned int reg); bool cs35l41_volatile_reg(struct device *dev, unsigned int reg); -struct otp_packed_element_t { +struct cs35l41_otp_packed_element_t { u32 reg; u8 shift; u8 size; }; -struct otp_map_element_t { +struct cs35l41_otp_map_element_t { u32 id; u32 num_elements; - const struct otp_packed_element_t *map; + const struct cs35l41_otp_packed_element_t *map; + u32 bit_offset; + u32 word_offset; }; extern const struct reg_default cs35l41_reg[CS35L41_MAX_CACHE_REG]; -extern const struct otp_map_element_t otp_map_map[3]; - -#define CS35L41_OTP_HDR_MASK_1 0xFFFFFFFE -#define CS35L41_OTP_HDR_MASK_2 0x0000FFFF -#define CS35L41_OTP_HDR_VAL_1 0x00001500 -#define CS35L41_OTP_HDR_VAL_2 0x00000300 +extern const struct cs35l41_otp_map_element_t + cs35l41_otp_map_map[CS35L41_NUM_OTP_MAPS]; #define CS35L41_CSPL_CMD_NONE 0 #define CS35L41_CSPL_CMD_MUTE 1 @@ -736,5 +735,4 @@ extern const struct otp_map_element_t otp_map_map[3]; #define CS35L41_CSPL_CAL_STRUCT_ADDR 0x02800224 #define CS35L41_AMB_TEMP_DEFAULT 25 - #endif /*__CS35L41_H__*/