Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Compile Fedora 33 kernel 5.11 with bytcr-wm5102 (including jack detect support) for Lenovo's Yoga Tablet 2
From b2df58f937bf6d9ea3891361e5edc037672b20f9 Mon Sep 17 00:00:00 2001
From: root <root@localhost.localdomain>
Date: Sat, 6 Mar 2021 19:39:50 +0100
Subject: [PATCH] Compile with support for bytcr-wm5102+jack-detect
---
0001-YogaTablet2.patch | 2467 +++++++++++++++++++++++++++++++++++++++
filter-x86_64.sh.fedora | 4 +-
kernel-local | 18 +
kernel.spec | 4 +-
4 files changed, 2491 insertions(+), 2 deletions(-)
create mode 100644 0001-YogaTablet2.patch
diff --git a/0001-YogaTablet2.patch b/0001-YogaTablet2.patch
new file mode 100644
index 000000000..2b6c9160d
--- /dev/null
+++ b/0001-YogaTablet2.patch
@@ -0,0 +1,2467 @@
+diff --git a/MAINTAINERS b/MAINTAINERS
+index bfc1b86e3..9c33595d4 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -19237,7 +19237,6 @@ F: Documentation/devicetree/bindings/sound/wlf,arizona.yaml
+ F: Documentation/hwmon/wm83??.rst
+ F: arch/arm/mach-s3c/mach-crag6410*
+ F: drivers/clk/clk-wm83*.c
+-F: drivers/extcon/extcon-arizona.c
+ F: drivers/gpio/gpio-*wm*.c
+ F: drivers/gpio/gpio-arizona.c
+ F: drivers/hwmon/wm83??-hwmon.c
+@@ -19261,7 +19260,7 @@ F: include/linux/mfd/wm8400*
+ F: include/linux/regulator/arizona*
+ F: include/linux/wm97xx.h
+ F: include/sound/wm????.h
+-F: sound/soc/codecs/arizona.?
++F: sound/soc/codecs/arizona*
+ F: sound/soc/codecs/cs47l24*
+ F: sound/soc/codecs/wm*
+
+diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
+index af58ebca2..e3db936be 100644
+--- a/drivers/extcon/Kconfig
++++ b/drivers/extcon/Kconfig
+@@ -21,14 +21,6 @@ config EXTCON_ADC_JACK
+ help
+ Say Y here to enable extcon device driver based on ADC values.
+
+-config EXTCON_ARIZONA
+- tristate "Wolfson Arizona EXTCON support"
+- depends on MFD_ARIZONA && INPUT && SND_SOC
+- help
+- Say Y here to enable support for external accessory detection
+- with Wolfson Arizona devices. These are audio CODECs with
+- advanced audio accessory detection support.
+-
+ config EXTCON_AXP288
+ tristate "X-Power AXP288 EXTCON support"
+ depends on MFD_AXP20X && USB_SUPPORT && X86 && ACPI
+diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
+index fe10a1b7d..1b390d934 100644
+--- a/drivers/extcon/Makefile
++++ b/drivers/extcon/Makefile
+@@ -6,7 +6,6 @@
+ obj-$(CONFIG_EXTCON) += extcon-core.o
+ extcon-core-objs += extcon.o devres.o
+ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
+-obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
+ obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o
+ obj-$(CONFIG_EXTCON_FSA9480) += extcon-fsa9480.o
+ obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
+diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c
+index cb6ec59a0..c61fe8423 100644
+--- a/drivers/input/misc/soc_button_array.c
++++ b/drivers/input/misc/soc_button_array.c
+@@ -94,6 +94,17 @@ static const struct dmi_system_id dmi_use_low_level_irq[] = {
+ DMI_MATCH(DMI_PRODUCT_VERSION, "1051L"),
+ },
+ },
++ {
++ /*
++ * Lenovo Yoga Tab2 1051F, something messes with the home-button
++ * IRQ settings, leading to a non working home-button.
++ */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "60073"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "1051F"),
++ },
++ },
+ {} /* Terminating entry */
+ };
+
+diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
+index 000cb8202..ce6fe6de3 100644
+--- a/drivers/mfd/arizona-core.c
++++ b/drivers/mfd/arizona-core.c
+@@ -797,17 +797,6 @@ const struct dev_pm_ops arizona_pm_ops = {
+ EXPORT_SYMBOL_GPL(arizona_pm_ops);
+
+ #ifdef CONFIG_OF
+-unsigned long arizona_of_get_type(struct device *dev)
+-{
+- const struct of_device_id *id = of_match_device(arizona_of_match, dev);
+-
+- if (id)
+- return (unsigned long)id->data;
+- else
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(arizona_of_get_type);
+-
+ static int arizona_of_get_core_pdata(struct arizona *arizona)
+ {
+ struct arizona_pdata *pdata = &arizona->pdata;
+@@ -892,11 +881,6 @@ static const char * const wm5102_supplies[] = {
+ static const struct mfd_cell wm5102_devs[] = {
+ { .name = "arizona-micsupp" },
+ { .name = "arizona-gpio" },
+- {
+- .name = "arizona-extcon",
+- .parent_supplies = wm5102_supplies,
+- .num_parent_supplies = 1, /* We only need MICVDD */
+- },
+ { .name = "arizona-haptics" },
+ { .name = "arizona-pwm" },
+ {
+@@ -909,11 +893,6 @@ static const struct mfd_cell wm5102_devs[] = {
+ static const struct mfd_cell wm5110_devs[] = {
+ { .name = "arizona-micsupp" },
+ { .name = "arizona-gpio" },
+- {
+- .name = "arizona-extcon",
+- .parent_supplies = wm5102_supplies,
+- .num_parent_supplies = 1, /* We only need MICVDD */
+- },
+ { .name = "arizona-haptics" },
+ { .name = "arizona-pwm" },
+ {
+@@ -950,11 +929,6 @@ static const char * const wm8997_supplies[] = {
+ static const struct mfd_cell wm8997_devs[] = {
+ { .name = "arizona-micsupp" },
+ { .name = "arizona-gpio" },
+- {
+- .name = "arizona-extcon",
+- .parent_supplies = wm8997_supplies,
+- .num_parent_supplies = 1, /* We only need MICVDD */
+- },
+ { .name = "arizona-haptics" },
+ { .name = "arizona-pwm" },
+ {
+@@ -967,11 +941,6 @@ static const struct mfd_cell wm8997_devs[] = {
+ static const struct mfd_cell wm8998_devs[] = {
+ { .name = "arizona-micsupp" },
+ { .name = "arizona-gpio" },
+- {
+- .name = "arizona-extcon",
+- .parent_supplies = wm5102_supplies,
+- .num_parent_supplies = 1, /* We only need MICVDD */
+- },
+ { .name = "arizona-haptics" },
+ { .name = "arizona-pwm" },
+ {
+diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c
+index 4b58e3ad6..5e83b730c 100644
+--- a/drivers/mfd/arizona-i2c.c
++++ b/drivers/mfd/arizona-i2c.c
+@@ -23,14 +23,16 @@
+ static int arizona_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+ {
++ const void *match_data;
+ struct arizona *arizona;
+ const struct regmap_config *regmap_config = NULL;
+- unsigned long type;
++ unsigned long type = 0;
+ int ret;
+
+- if (i2c->dev.of_node)
+- type = arizona_of_get_type(&i2c->dev);
+- else
++ match_data = device_get_match_data(&i2c->dev);
++ if (match_data)
++ type = (unsigned long)match_data;
++ else if (id)
+ type = id->driver_data;
+
+ switch (type) {
+@@ -115,6 +117,7 @@ static struct i2c_driver arizona_i2c_driver = {
+
+ module_i2c_driver(arizona_i2c_driver);
+
++MODULE_SOFTDEP("pre: arizona_ldo1");
+ MODULE_DESCRIPTION("Arizona I2C bus interface");
+ MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c
+index 2633e147b..24a2c75d6 100644
+--- a/drivers/mfd/arizona-spi.c
++++ b/drivers/mfd/arizona-spi.c
+@@ -7,7 +7,10 @@
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ */
+
++#include <linux/acpi.h>
+ #include <linux/err.h>
++#include <linux/gpio/consumer.h>
++#include <linux/gpio/machine.h>
+ #include <linux/module.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/regmap.h>
+@@ -15,22 +18,141 @@
+ #include <linux/slab.h>
+ #include <linux/spi/spi.h>
+ #include <linux/of.h>
++#include <uapi/linux/input-event-codes.h>
+
+ #include <linux/mfd/arizona/core.h>
+
+ #include "arizona.h"
+
++#ifdef CONFIG_ACPI
++const struct acpi_gpio_params reset_gpios = { 1, 0, false };
++const struct acpi_gpio_params ldoena_gpios = { 2, 0, false };
++
++static const struct acpi_gpio_mapping arizona_acpi_gpios[] = {
++ { "reset-gpios", &reset_gpios, 1, },
++ { "wlf,ldoena-gpios", &ldoena_gpios, 1 },
++ { }
++};
++
++/*
++ * The ACPI resources for the device only describe external GPIO-s. They do
++ * not provide mappings for the GPIO-s coming from the Arizona codec itself.
++ */
++static const struct gpiod_lookup arizona_soc_gpios[] = {
++ { "arizona", 2, "wlf,spkvdd-ena", 0, GPIO_ACTIVE_HIGH },
++ { "arizona", 4, "wlf,micd-pol", 0, GPIO_ACTIVE_LOW },
++};
++
++/*
++ * The AOSP 3.5 mm Headset: Accessory Specification gives the following values:
++ * Function A Play/Pause: 0 ohm
++ * Function D Voice assistant: 135 ohm
++ * Function B Volume Up 240 ohm
++ * Function C Volume Down 470 ohm
++ * Minimum Mic DC resistance 1000 ohm
++ * Minimum Ear speaker impedance 16 ohm
++ * Note the first max value below must be less then the min. speaker impedance,
++ * to allow CTIA/OMTP detection to work. The other max values are the closest
++ * value from extcon-arizona.c:arizona_micd_levels halfway 2 button resistances.
++ */
++static const struct arizona_micd_range arizona_micd_aosp_ranges[] = {
++ { .max = 11, .key = KEY_PLAYPAUSE },
++ { .max = 186, .key = KEY_VOICECOMMAND },
++ { .max = 348, .key = KEY_VOLUMEUP },
++ { .max = 752, .key = KEY_VOLUMEDOWN },
++};
++
++static void arizona_spi_acpi_remove_lookup(void *lookup)
++{
++ gpiod_remove_lookup_table(lookup);
++}
++
++static int arizona_spi_acpi_probe(struct arizona *arizona)
++{
++ struct gpiod_lookup_table *lookup;
++ acpi_status status;
++ int ret;
++
++ /* Add mappings for the 2 ACPI declared GPIOs used for reset and ldo-ena */
++ devm_acpi_dev_add_driver_gpios(arizona->dev, arizona_acpi_gpios);
++
++ /* Add lookups for the SoCs own GPIOs used for micdet-polarity and spkVDD-enable */
++ lookup = devm_kzalloc(arizona->dev,
++ struct_size(lookup, table, ARRAY_SIZE(arizona_soc_gpios) + 1),
++ GFP_KERNEL);
++ if (!lookup)
++ return -ENOMEM;
++
++ lookup->dev_id = dev_name(arizona->dev);
++ memcpy(lookup->table, arizona_soc_gpios, sizeof(arizona_soc_gpios));
++
++ gpiod_add_lookup_table(lookup);
++ ret = devm_add_action_or_reset(arizona->dev, arizona_spi_acpi_remove_lookup, lookup);
++ if (ret)
++ return ret;
++
++ /* Enable 32KHz clock from SoC to codec for jack-detect */
++ status = acpi_evaluate_object(ACPI_HANDLE(arizona->dev), "CLKE", NULL, NULL);
++ if (ACPI_FAILURE(status))
++ dev_warn(arizona->dev, "Failed to enable 32KHz clk ACPI error %d\n", status);
++
++ /*
++ * Some DSDTs wrongly declare the IRQ trigger-type as IRQF_TRIGGER_FALLING
++ * The IRQ line will stay low when a new IRQ event happens between reading
++ * the IRQ status flags and acknowledging them. When the IRQ line stays
++ * low like this the IRQ will never trigger again when its type is set
++ * to IRQF_TRIGGER_FALLING. Correct the IRQ trigger-type to fix this.
++ *
++ * Note theoretically it is possible that some boards are not capable
++ * of handling active low level interrupts. In that case setting the
++ * flag to IRQF_TRIGGER_FALLING would not be a bug (and we would need
++ * to work around this) but so far all known usages of IRQF_TRIGGER_FALLING
++ * are a bug in the board's DSDT.
++ */
++ arizona->pdata.irq_flags = IRQF_TRIGGER_LOW;
++
++ /* Wait 200 ms after jack insertion */
++ arizona->pdata.micd_detect_debounce = 200;
++
++ /* Use standard AOSP values for headset-button mappings */
++ arizona->pdata.micd_ranges = arizona_micd_aosp_ranges;
++ arizona->pdata.num_micd_ranges = ARRAY_SIZE(arizona_micd_aosp_ranges);
++
++ return 0;
++}
++
++static const struct acpi_device_id arizona_acpi_match[] = {
++ {
++ .id = "WM510204",
++ .driver_data = WM5102,
++ },
++ {
++ .id = "WM510205",
++ .driver_data = WM5102,
++ },
++ { }
++};
++MODULE_DEVICE_TABLE(acpi, arizona_acpi_match);
++#else
++static int arizona_spi_acpi_probe(struct arizona *arizona)
++{
++ return -ENODEV;
++}
++#endif
++
+ static int arizona_spi_probe(struct spi_device *spi)
+ {
+ const struct spi_device_id *id = spi_get_device_id(spi);
++ const void *match_data;
+ struct arizona *arizona;
+ const struct regmap_config *regmap_config = NULL;
+- unsigned long type;
++ unsigned long type = 0;
+ int ret;
+
+- if (spi->dev.of_node)
+- type = arizona_of_get_type(&spi->dev);
+- else
++ match_data = device_get_match_data(&spi->dev);
++ if (match_data)
++ type = (unsigned long)match_data;
++ else if (id)
+ type = id->driver_data;
+
+ switch (type) {
+@@ -75,6 +197,12 @@ static int arizona_spi_probe(struct spi_device *spi)
+ arizona->dev = &spi->dev;
+ arizona->irq = spi->irq;
+
++ if (has_acpi_companion(&spi->dev)) {
++ ret = arizona_spi_acpi_probe(arizona);
++ if (ret)
++ return ret;
++ }
++
+ return arizona_dev_init(arizona);
+ }
+
+@@ -102,6 +230,7 @@ static struct spi_driver arizona_spi_driver = {
+ .name = "arizona",
+ .pm = &arizona_pm_ops,
+ .of_match_table = of_match_ptr(arizona_of_match),
++ .acpi_match_table = ACPI_PTR(arizona_acpi_match),
+ },
+ .probe = arizona_spi_probe,
+ .remove = arizona_spi_remove,
+@@ -110,6 +239,7 @@ static struct spi_driver arizona_spi_driver = {
+
+ module_spi_driver(arizona_spi_driver);
+
++MODULE_SOFTDEP("pre: arizona_ldo1");
+ MODULE_DESCRIPTION("Arizona SPI bus interface");
+ MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h
+index 995efc6d7..801cbbcd7 100644
+--- a/drivers/mfd/arizona.h
++++ b/drivers/mfd/arizona.h
+@@ -50,13 +50,4 @@ int arizona_dev_exit(struct arizona *arizona);
+ int arizona_irq_init(struct arizona *arizona);
+ int arizona_irq_exit(struct arizona *arizona);
+
+-#ifdef CONFIG_OF
+-unsigned long arizona_of_get_type(struct device *dev);
+-#else
+-static inline unsigned long arizona_of_get_type(struct device *dev)
+-{
+- return 0;
+-}
+-#endif
+-
+ #endif
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index d277f0366..2e976cfaa 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -43,7 +43,7 @@ snd-soc-ak4642-objs := ak4642.o
+ snd-soc-ak4671-objs := ak4671.o
+ snd-soc-ak5386-objs := ak5386.o
+ snd-soc-ak5558-objs := ak5558.o
+-snd-soc-arizona-objs := arizona.o
++snd-soc-arizona-objs := arizona.o arizona-jack.o
+ snd-soc-bd28623-objs := bd28623.o
+ snd-soc-bt-sco-objs := bt-sco.o
+ snd-soc-cpcap-objs := cpcap.o
+diff --git a/drivers/extcon/extcon-arizona.c b/sound/soc/codecs/arizona-jack.c
+similarity index 76%
+rename from drivers/extcon/extcon-arizona.c
+rename to sound/soc/codecs/arizona-jack.c
+index aae82db54..9c15ddba6 100644
+--- a/drivers/extcon/extcon-arizona.c
++++ b/sound/soc/codecs/arizona-jack.c
+@@ -7,19 +7,17 @@
+
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+-#include <linux/i2c.h>
+ #include <linux/slab.h>
+ #include <linux/interrupt.h>
+ #include <linux/err.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/gpio.h>
+ #include <linux/input.h>
+-#include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/property.h>
+ #include <linux/regulator/consumer.h>
+-#include <linux/extcon-provider.h>
+
++#include <sound/jack.h>
+ #include <sound/soc.h>
+
+ #include <linux/mfd/arizona/core.h>
+@@ -27,8 +25,16 @@
+ #include <linux/mfd/arizona/registers.h>
+ #include <dt-bindings/mfd/arizona.h>
+
++#include "arizona.h"
++
+ #define ARIZONA_MAX_MICD_RANGE 8
+
++/*
++ * The hardware supports 8 ranges / buttons, but the snd-jack interface
++ * only supports 6 buttons (button 0-5).
++ */
++#define ARIZONA_MAX_MICD_BUTTONS 6
++
+ #define ARIZONA_MICD_CLAMP_MODE_JDL 0x4
+ #define ARIZONA_MICD_CLAMP_MODE_JDH 0x5
+ #define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
+@@ -61,47 +67,6 @@
+
+ #define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8)
+
+-struct arizona_extcon_info {
+- struct device *dev;
+- struct arizona *arizona;
+- struct mutex lock;
+- struct regulator *micvdd;
+- struct input_dev *input;
+-
+- u16 last_jackdet;
+-
+- int micd_mode;
+- const struct arizona_micd_config *micd_modes;
+- int micd_num_modes;
+-
+- const struct arizona_micd_range *micd_ranges;
+- int num_micd_ranges;
+-
+- bool micd_reva;
+- bool micd_clamp;
+-
+- struct delayed_work hpdet_work;
+- struct delayed_work micd_detect_work;
+- struct delayed_work micd_timeout_work;
+-
+- bool hpdet_active;
+- bool hpdet_done;
+- bool hpdet_retried;
+-
+- int num_hpdet_res;
+- unsigned int hpdet_res[3];
+-
+- bool mic;
+- bool detecting;
+- int jack_flips;
+-
+- int hpdet_ip_version;
+-
+- struct extcon_dev *edev;
+-
+- struct gpio_desc *micd_pol_gpio;
+-};
+-
+ static const struct arizona_micd_config micd_default_modes[] = {
+ { ARIZONA_ACCDET_SRC, 1, 0 },
+ { 0, 2, 1 },
+@@ -127,17 +92,9 @@ static const int arizona_micd_levels[] = {
+ 1257, 30000,
+ };
+
+-static const unsigned int arizona_cable[] = {
+- EXTCON_MECHANICAL,
+- EXTCON_JACK_MICROPHONE,
+- EXTCON_JACK_HEADPHONE,
+- EXTCON_JACK_LINE_OUT,
+- EXTCON_NONE,
+-};
+-
+-static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
++static void arizona_start_hpdet_acc_id(struct arizona_priv *info);
+
+-static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
++static void arizona_extcon_hp_clamp(struct arizona_priv *info,
+ bool clamp)
+ {
+ struct arizona *arizona = info->arizona;
+@@ -166,9 +123,8 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
+ ARIZONA_HP_TEST_CTRL_1,
+ ARIZONA_HP1_TST_CAP_SEL_MASK,
+ cap_sel);
+- if (ret != 0)
+- dev_warn(arizona->dev,
+- "Failed to set TST_CAP_SEL: %d\n", ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to set TST_CAP_SEL: %d\n", ret);
+ break;
+ default:
+ mask = ARIZONA_RMV_SHRT_HP1L;
+@@ -187,24 +143,20 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, 0);
+- if (ret != 0)
+- dev_warn(arizona->dev,
+- "Failed to disable headphone outputs: %d\n",
+- ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to disable headphone outputs: %d\n", ret);
+ }
+
+ if (mask) {
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
+ mask, val);
+- if (ret != 0)
+- dev_warn(arizona->dev, "Failed to do clamp: %d\n",
+- ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret);
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
+ mask, val);
+- if (ret != 0)
+- dev_warn(arizona->dev, "Failed to do clamp: %d\n",
+- ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to do clamp: %d\n", ret);
+ }
+
+ /* Restore the desired state while not doing the clamp */
+@@ -213,16 +165,14 @@ static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
+ ARIZONA_OUTPUT_ENABLES_1,
+ ARIZONA_OUT1L_ENA |
+ ARIZONA_OUT1R_ENA, arizona->hp_ena);
+- if (ret != 0)
+- dev_warn(arizona->dev,
+- "Failed to restore headphone outputs: %d\n",
+- ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to restore headphone outputs: %d\n", ret);
+ }
+
+ snd_soc_dapm_mutex_unlock(arizona->dapm);
+ }
+
+-static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
++static void arizona_extcon_set_mode(struct arizona_priv *info, int mode)
+ {
+ struct arizona *arizona = info->arizona;
+
+@@ -243,7 +193,7 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
+ dev_dbg(arizona->dev, "Set jack polarity to %d\n", mode);
+ }
+
+-static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info)
++static const char *arizona_extcon_get_micbias(struct arizona_priv *info)
+ {
+ switch (info->micd_modes[0].bias) {
+ case 1:
+@@ -257,7 +207,7 @@ static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info)
+ }
+ }
+
+-static void arizona_extcon_pulse_micbias(struct arizona_extcon_info *info)
++static void arizona_extcon_pulse_micbias(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ const char *widget = arizona_extcon_get_micbias(info);
+@@ -266,23 +216,21 @@ static void arizona_extcon_pulse_micbias(struct arizona_extcon_info *info)
+ int ret;
+
+ ret = snd_soc_component_force_enable_pin(component, widget);
+- if (ret != 0)
+- dev_warn(arizona->dev, "Failed to enable %s: %d\n",
+- widget, ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to enable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+
+ if (!arizona->pdata.micd_force_micbias) {
+ ret = snd_soc_component_disable_pin(component, widget);
+- if (ret != 0)
+- dev_warn(arizona->dev, "Failed to disable %s: %d\n",
+- widget, ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+ }
+ }
+
+-static void arizona_start_mic(struct arizona_extcon_info *info)
++static void arizona_start_mic(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ bool change;
+@@ -290,22 +238,17 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
+ unsigned int mode;
+
+ /* Microphone detection can't use idle mode */
+- pm_runtime_get(info->dev);
++ pm_runtime_get_sync(arizona->dev);
+
+ if (info->detecting) {
+ ret = regulator_allow_bypass(info->micvdd, false);
+- if (ret != 0) {
+- dev_err(arizona->dev,
+- "Failed to regulate MICVDD: %d\n",
+- ret);
+- }
++ if (ret)
++ dev_err(arizona->dev, "Failed to regulate MICVDD: %d\n", ret);
+ }
+
+ ret = regulator_enable(info->micvdd);
+- if (ret != 0) {
+- dev_err(arizona->dev, "Failed to enable MICVDD: %d\n",
+- ret);
+- }
++ if (ret)
++ dev_err(arizona->dev, "Failed to enable MICVDD: %d\n", ret);
+
+ if (info->micd_reva) {
+ const struct reg_sequence reva[] = {
+@@ -335,11 +278,11 @@ static void arizona_start_mic(struct arizona_extcon_info *info)
+ dev_err(arizona->dev, "Failed to enable micd: %d\n", ret);
+ } else if (!change) {
+ regulator_disable(info->micvdd);
+- pm_runtime_put_autosuspend(info->dev);
++ pm_runtime_put_autosuspend(arizona->dev);
+ }
+ }
+
+-static void arizona_stop_mic(struct arizona_extcon_info *info)
++static void arizona_stop_mic(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ const char *widget = arizona_extcon_get_micbias(info);
+@@ -355,10 +298,8 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
+ dev_err(arizona->dev, "Failed to disable micd: %d\n", ret);
+
+ ret = snd_soc_component_disable_pin(component, widget);
+- if (ret != 0)
+- dev_warn(arizona->dev,
+- "Failed to disable %s: %d\n",
+- widget, ret);
++ if (ret)
++ dev_warn(arizona->dev, "Failed to disable %s: %d\n", widget, ret);
+
+ snd_soc_dapm_sync(dapm);
+
+@@ -373,15 +314,13 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
+ }
+
+ ret = regulator_allow_bypass(info->micvdd, true);
+- if (ret != 0) {
+- dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n",
+- ret);
+- }
++ if (ret)
++ dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret);
+
+ if (change) {
+ regulator_disable(info->micvdd);
+- pm_runtime_mark_last_busy(info->dev);
+- pm_runtime_put_autosuspend(info->dev);
++ pm_runtime_mark_last_busy(arizona->dev);
++ pm_runtime_put_autosuspend(arizona->dev);
+ }
+ }
+
+@@ -407,24 +346,22 @@ static struct {
+ { 1000, 10000 },
+ };
+
+-static int arizona_hpdet_read(struct arizona_extcon_info *info)
++static int arizona_hpdet_read(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ unsigned int val, range;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2, &val);
+- if (ret != 0) {
+- dev_err(arizona->dev, "Failed to read HPDET status: %d\n",
+- ret);
++ if (ret) {
++ dev_err(arizona->dev, "Failed to read HPDET status: %d\n", ret);
+ return ret;
+ }
+
+ switch (info->hpdet_ip_version) {
+ case 0:
+ if (!(val & ARIZONA_HP_DONE)) {
+- dev_err(arizona->dev, "HPDET did not complete: %x\n",
+- val);
++ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+@@ -433,15 +370,13 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
+
+ case 1:
+ if (!(val & ARIZONA_HP_DONE_B)) {
+- dev_err(arizona->dev, "HPDET did not complete: %x\n",
+- val);
++ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+ ret = regmap_read(arizona->regmap, ARIZONA_HP_DACVAL, &val);
+- if (ret != 0) {
+- dev_err(arizona->dev, "Failed to read HP value: %d\n",
+- ret);
++ if (ret) {
++ dev_err(arizona->dev, "Failed to read HP value: %d\n", ret);
+ return -EAGAIN;
+ }
+
+@@ -454,8 +389,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
+ (val < arizona_hpdet_b_ranges[range].threshold ||
+ val >= ARIZONA_HPDET_B_RANGE_MAX)) {
+ range++;
+- dev_dbg(arizona->dev, "Moving to HPDET range %d\n",
+- range);
++ dev_dbg(arizona->dev, "Moving to HPDET range %d\n", range);
+ regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_IMPEDANCE_RANGE_MASK,
+@@ -471,8 +405,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
+ return ARIZONA_HPDET_MAX;
+ }
+
+- dev_dbg(arizona->dev, "HPDET read %d in range %d\n",
+- val, range);
++ dev_dbg(arizona->dev, "HPDET read %d in range %d\n", val, range);
+
+ val = arizona_hpdet_b_ranges[range].factor_b
+ / ((val * 100) -
+@@ -481,8 +414,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
+
+ case 2:
+ if (!(val & ARIZONA_HP_DONE_B)) {
+- dev_err(arizona->dev, "HPDET did not complete: %x\n",
+- val);
++ dev_err(arizona->dev, "HPDET did not complete: %x\n", val);
+ return -EAGAIN;
+ }
+
+@@ -518,8 +450,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
+ break;
+
+ default:
+- dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n",
+- info->hpdet_ip_version);
++ dev_warn(arizona->dev, "Unknown HPDET IP revision %d\n", info->hpdet_ip_version);
+ return -EINVAL;
+ }
+
+@@ -527,7 +458,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
+ return val;
+ }
+
+-static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
++static int arizona_hpdet_do_id(struct arizona_priv *info, int *reading,
+ bool *mic)
+ {
+ struct arizona *arizona = info->arizona;
+@@ -573,7 +504,7 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
+ info->num_hpdet_res = 0;
+ info->hpdet_retried = true;
+ arizona_start_hpdet_acc_id(info);
+- pm_runtime_put(info->dev);
++ pm_runtime_put(arizona->dev);
+ return -EAGAIN;
+ }
+
+@@ -597,11 +528,10 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
+
+ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
+ {
+- struct arizona_extcon_info *info = data;
++ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+- unsigned int report = EXTCON_JACK_HEADPHONE;
+- int ret, reading;
++ int ret, reading, state, report;
+ bool mic = false;
+
+ mutex_lock(&info->lock);
+@@ -614,12 +544,8 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
+ }
+
+ /* If the cable was removed while measuring ignore the result */
+- ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
+- if (ret < 0) {
+- dev_err(arizona->dev, "Failed to check cable state: %d\n",
+- ret);
+- goto out;
+- } else if (!ret) {
++ state = info->jack->status & SND_JACK_MECHANICAL;
++ if (!state) {
+ dev_dbg(arizona->dev, "Ignoring HPDET for removed cable\n");
+ goto done;
+ }
+@@ -645,14 +571,11 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
+
+ /* Report high impedence cables as line outputs */
+ if (reading >= 5000)
+- report = EXTCON_JACK_LINE_OUT;
++ report = SND_JACK_LINEOUT;
+ else
+- report = EXTCON_JACK_HEADPHONE;
++ report = SND_JACK_HEADPHONE;
+
+- ret = extcon_set_state_sync(info->edev, report, true);
+- if (ret != 0)
+- dev_err(arizona->dev, "Failed to report HP/line: %d\n",
+- ret);
++ snd_soc_jack_report(info->jack, report, SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+ done:
+ /* Reset back to starting range */
+@@ -667,15 +590,17 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
+ gpio_set_value_cansleep(id_gpio, 0);
+
+ /* If we have a mic then reenable MICDET */
+- if (mic || info->mic)
++ if (state && (mic || info->mic))
+ arizona_start_mic(info);
+
+ if (info->hpdet_active) {
+- pm_runtime_put_autosuspend(info->dev);
++ pm_runtime_put_autosuspend(arizona->dev);
+ info->hpdet_active = false;
+ }
+
+- info->hpdet_done = true;
++ /* Do not set hp_det done when the cable has been unplugged */
++ if (state)
++ info->hpdet_done = true;
+
+ out:
+ mutex_unlock(&info->lock);
+@@ -683,7 +608,7 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
+ return IRQ_HANDLED;
+ }
+
+-static void arizona_identify_headphone(struct arizona_extcon_info *info)
++static void arizona_identify_headphone(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ int ret;
+@@ -694,7 +619,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
+ dev_dbg(arizona->dev, "Starting HPDET\n");
+
+ /* Make sure we keep the device enabled during the measurement */
+- pm_runtime_get(info->dev);
++ pm_runtime_get_sync(arizona->dev);
+
+ info->hpdet_active = true;
+
+@@ -713,9 +638,8 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+- if (ret != 0) {
+- dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n",
+- ret);
++ if (ret) {
++ dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret);
+ goto err;
+ }
+
+@@ -723,12 +647,11 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
+
+ err:
+ arizona_extcon_hp_clamp(info, false);
+- pm_runtime_put_autosuspend(info->dev);
++ pm_runtime_put_autosuspend(arizona->dev);
+
+ /* Just report headphone */
+- ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
+- if (ret != 0)
+- dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
++ snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
++ SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+ if (info->mic)
+ arizona_start_mic(info);
+@@ -736,7 +659,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
+ info->hpdet_active = false;
+ }
+
+-static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
++static void arizona_start_hpdet_acc_id(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ int hp_reading = 32;
+@@ -746,7 +669,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
+ dev_dbg(arizona->dev, "Starting identification via HPDET\n");
+
+ /* Make sure we keep the device enabled during the measurement */
+- pm_runtime_get_sync(info->dev);
++ pm_runtime_get_sync(arizona->dev);
+
+ info->hpdet_active = true;
+
+@@ -766,10 +689,8 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HEADPHONE_DETECT_1,
+ ARIZONA_HP_POLL, ARIZONA_HP_POLL);
+- if (ret != 0) {
+- dev_err(arizona->dev,
+- "Can't start HPDETL measurement: %d\n",
+- ret);
++ if (ret) {
++ dev_err(arizona->dev, "Can't start HPDETL measurement: %d\n", ret);
+ goto err;
+ }
+ } else {
+@@ -780,17 +701,16 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
+
+ err:
+ /* Just report headphone */
+- ret = extcon_set_state_sync(info->edev, EXTCON_JACK_HEADPHONE, true);
+- if (ret != 0)
+- dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
++ snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
++ SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+
+ info->hpdet_active = false;
+ }
+
+ static void arizona_micd_timeout_work(struct work_struct *work)
+ {
+- struct arizona_extcon_info *info = container_of(work,
+- struct arizona_extcon_info,
++ struct arizona_priv *info = container_of(work,
++ struct arizona_priv,
+ micd_timeout_work.work);
+
+ mutex_lock(&info->lock);
+@@ -804,7 +724,7 @@ static void arizona_micd_timeout_work(struct work_struct *work)
+ mutex_unlock(&info->lock);
+ }
+
+-static int arizona_micd_adc_read(struct arizona_extcon_info *info)
++static int arizona_micd_adc_read(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ unsigned int val;
+@@ -815,9 +735,8 @@ static int arizona_micd_adc_read(struct arizona_extcon_info *info)
+ ARIZONA_MICD_ENA, 0);
+
+ ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_4, &val);
+- if (ret != 0) {
+- dev_err(arizona->dev,
+- "Failed to read MICDET_ADCVAL: %d\n", ret);
++ if (ret) {
++ dev_err(arizona->dev, "Failed to read MICDET_ADCVAL: %d\n", ret);
+ return ret;
+ }
+
+@@ -841,7 +760,7 @@ static int arizona_micd_adc_read(struct arizona_extcon_info *info)
+ return val;
+ }
+
+-static int arizona_micd_read(struct arizona_extcon_info *info)
++static int arizona_micd_read(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ unsigned int val = 0;
+@@ -849,17 +768,15 @@ static int arizona_micd_read(struct arizona_extcon_info *info)
+
+ for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
+ ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
+- if (ret != 0) {
+- dev_err(arizona->dev,
+- "Failed to read MICDET: %d\n", ret);
++ if (ret) {
++ dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(arizona->dev, "MICDET: %x\n", val);
+
+ if (!(val & ARIZONA_MICD_VALID)) {
+- dev_warn(arizona->dev,
+- "Microphone detection state invalid\n");
++ dev_warn(arizona->dev, "Microphone detection state invalid\n");
+ return -EINVAL;
+ }
+ }
+@@ -874,7 +791,7 @@ static int arizona_micd_read(struct arizona_extcon_info *info)
+
+ static int arizona_micdet_reading(void *priv)
+ {
+- struct arizona_extcon_info *info = priv;
++ struct arizona_priv *info = priv;
+ struct arizona *arizona = info->arizona;
+ int ret, val;
+
+@@ -903,18 +820,12 @@ static int arizona_micdet_reading(void *priv)
+
+ arizona_identify_headphone(info);
+
+- ret = extcon_set_state_sync(info->edev,
+- EXTCON_JACK_MICROPHONE, true);
+- if (ret != 0)
+- dev_err(arizona->dev, "Headset report failed: %d\n",
+- ret);
++ snd_soc_jack_report(info->jack, SND_JACK_MICROPHONE, SND_JACK_MICROPHONE);
+
+ /* Don't need to regulate for button detection */
+ ret = regulator_allow_bypass(info->micvdd, true);
+- if (ret != 0) {
+- dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n",
+- ret);
+- }
++ if (ret)
++ dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", ret);
+
+ return 0;
+ }
+@@ -968,9 +879,9 @@ static int arizona_micdet_reading(void *priv)
+
+ static int arizona_button_reading(void *priv)
+ {
+- struct arizona_extcon_info *info = priv;
++ struct arizona_priv *info = priv;
+ struct arizona *arizona = info->arizona;
+- int val, key, lvl, i;
++ int val, key, lvl;
+
+ val = arizona_micd_read(info);
+ if (val < 0)
+@@ -987,27 +898,20 @@ static int arizona_button_reading(void *priv)
+ lvl = val & ARIZONA_MICD_LVL_MASK;
+ lvl >>= ARIZONA_MICD_LVL_SHIFT;
+
+- for (i = 0; i < info->num_micd_ranges; i++)
+- input_report_key(info->input,
+- info->micd_ranges[i].key, 0);
+-
+ if (lvl && ffs(lvl) - 1 < info->num_micd_ranges) {
+- key = info->micd_ranges[ffs(lvl) - 1].key;
+- input_report_key(info->input, key, 1);
+- input_sync(info->input);
++ key = ffs(lvl) - 1;
++ snd_soc_jack_report(info->jack,
++ SND_JACK_BTN_0 >> key,
++ info->micd_button_mask);
+ } else {
+ dev_err(arizona->dev, "Button out of range\n");
+ }
+ } else {
+- dev_warn(arizona->dev, "Button with no mic: %x\n",
+- val);
++ dev_warn(arizona->dev, "Button with no mic: %x\n", val);
+ }
+ } else {
+ dev_dbg(arizona->dev, "Mic button released\n");
+- for (i = 0; i < info->num_micd_ranges; i++)
+- input_report_key(info->input,
+- info->micd_ranges[i].key, 0);
+- input_sync(info->input);
++ snd_soc_jack_report(info->jack, 0, info->micd_button_mask);
+ arizona_extcon_pulse_micbias(info);
+ }
+
+@@ -1016,24 +920,17 @@ static int arizona_button_reading(void *priv)
+
+ static void arizona_micd_detect(struct work_struct *work)
+ {
+- struct arizona_extcon_info *info = container_of(work,
+- struct arizona_extcon_info,
++ struct arizona_priv *info = container_of(work,
++ struct arizona_priv,
+ micd_detect_work.work);
+ struct arizona *arizona = info->arizona;
+- int ret;
+
+ cancel_delayed_work_sync(&info->micd_timeout_work);
+
+ mutex_lock(&info->lock);
+
+ /* If the cable was removed while measuring ignore the result */
+- ret = extcon_get_state(info->edev, EXTCON_MECHANICAL);
+- if (ret < 0) {
+- dev_err(arizona->dev, "Failed to check cable state: %d\n",
+- ret);
+- mutex_unlock(&info->lock);
+- return;
+- } else if (!ret) {
++ if (!(info->jack->status & SND_JACK_MECHANICAL)) {
+ dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
+ mutex_unlock(&info->lock);
+ return;
+@@ -1044,13 +941,13 @@ static void arizona_micd_detect(struct work_struct *work)
+ else
+ arizona_button_reading(info);
+
+- pm_runtime_mark_last_busy(info->dev);
++ pm_runtime_mark_last_busy(arizona->dev);
+ mutex_unlock(&info->lock);
+ }
+
+ static irqreturn_t arizona_micdet(int irq, void *data)
+ {
+- struct arizona_extcon_info *info = data;
++ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ int debounce = arizona->pdata.micd_detect_debounce;
+
+@@ -1074,8 +971,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
+
+ static void arizona_hpdet_work(struct work_struct *work)
+ {
+- struct arizona_extcon_info *info = container_of(work,
+- struct arizona_extcon_info,
++ struct arizona_priv *info = container_of(work,
++ struct arizona_priv,
+ hpdet_work.work);
+
+ mutex_lock(&info->lock);
+@@ -1083,7 +980,7 @@ static void arizona_hpdet_work(struct work_struct *work)
+ mutex_unlock(&info->lock);
+ }
+
+-static int arizona_hpdet_wait(struct arizona_extcon_info *info)
++static int arizona_hpdet_wait(struct arizona_priv *info)
+ {
+ struct arizona *arizona = info->arizona;
+ unsigned int val;
+@@ -1093,8 +990,7 @@ static int arizona_hpdet_wait(struct arizona_extcon_info *info)
+ ret = regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_2,
+ &val);
+ if (ret) {
+- dev_err(arizona->dev,
+- "Failed to read HPDET state: %d\n", ret);
++ dev_err(arizona->dev, "Failed to read HPDET state: %d\n", ret);
+ return ret;
+ }
+
+@@ -1119,7 +1015,7 @@ static int arizona_hpdet_wait(struct arizona_extcon_info *info)
+
+ static irqreturn_t arizona_jackdet(int irq, void *data)
+ {
+- struct arizona_extcon_info *info = data;
++ struct arizona_priv *info = data;
+ struct arizona *arizona = info->arizona;
+ unsigned int val, present, mask;
+ bool cancelled_hp, cancelled_mic;
+@@ -1128,7 +1024,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
+ cancelled_hp = cancel_delayed_work_sync(&info->hpdet_work);
+ cancelled_mic = cancel_delayed_work_sync(&info->micd_timeout_work);
+
+- pm_runtime_get_sync(info->dev);
++ pm_runtime_get_sync(arizona->dev);
+
+ mutex_lock(&info->lock);
+
+@@ -1144,11 +1040,10 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
+ }
+
+ ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
+- if (ret != 0) {
+- dev_err(arizona->dev, "Failed to read jackdet status: %d\n",
+- ret);
++ if (ret) {
++ dev_err(arizona->dev, "Failed to read jackdet status: %d\n", ret);
+ mutex_unlock(&info->lock);
+- pm_runtime_put_autosuspend(info->dev);
++ pm_runtime_put_autosuspend(arizona->dev);
+ return IRQ_NONE;
+ }
+
+@@ -1174,12 +1069,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
+
+ if (info->last_jackdet == present) {
+ dev_dbg(arizona->dev, "Detected jack\n");
+- ret = extcon_set_state_sync(info->edev,
+- EXTCON_MECHANICAL, true);
+-
+- if (ret != 0)
+- dev_err(arizona->dev, "Mechanical report failed: %d\n",
+- ret);
++ snd_soc_jack_report(info->jack, SND_JACK_MECHANICAL, SND_JACK_MECHANICAL);
+
+ info->detecting = true;
+ info->mic = false;
+@@ -1210,18 +1100,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
+ info->hpdet_done = false;
+ info->hpdet_retried = false;
+
+- for (i = 0; i < info->num_micd_ranges; i++)
+- input_report_key(info->input,
+- info->micd_ranges[i].key, 0);
+- input_sync(info->input);
+-
+- for (i = 0; i < ARRAY_SIZE(arizona_cable) - 1; i++) {
+- ret = extcon_set_state_sync(info->edev,
+- arizona_cable[i], false);
+- if (ret != 0)
+- dev_err(arizona->dev,
+- "Removal report failed: %d\n", ret);
+- }
++ snd_soc_jack_report(info->jack, 0, ARIZONA_JACK_MASK | info->micd_button_mask);
+
+ /*
+ * If the jack was removed during a headphone detection we
+@@ -1248,8 +1127,8 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
+
+ mutex_unlock(&info->lock);
+
+- pm_runtime_mark_last_busy(info->dev);
+- pm_runtime_put_autosuspend(info->dev);
++ pm_runtime_mark_last_busy(arizona->dev);
++ pm_runtime_put_autosuspend(arizona->dev);
+
+ return IRQ_HANDLED;
+ }
+@@ -1332,8 +1211,7 @@ static int arizona_extcon_device_get_pdata(struct device *dev,
+ pdata->hpdet_channel = val;
+ break;
+ default:
+- dev_err(arizona->dev,
+- "Wrong wlf,hpdet-channel DT value %d\n", val);
++ dev_err(arizona->dev, "Wrong wlf,hpdet-channel DT value %d\n", val);
+ pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL;
+ }
+
+@@ -1375,41 +1253,24 @@ static int arizona_extcon_device_get_pdata(struct device *dev,
+ return 0;
+ }
+
+-static int arizona_extcon_probe(struct platform_device *pdev)
++int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev)
+ {
+- struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
++ struct arizona *arizona = info->arizona;
+ struct arizona_pdata *pdata = &arizona->pdata;
+- struct arizona_extcon_info *info;
+- unsigned int val;
+- unsigned int clamp_mode;
+- int jack_irq_fall, jack_irq_rise;
+- int ret, mode, i, j;
+-
+- if (!arizona->dapm || !arizona->dapm->card)
+- return -EPROBE_DEFER;
+-
+- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+- if (!info)
+- return -ENOMEM;
++ int ret, mode;
+
+ if (!dev_get_platdata(arizona->dev))
+- arizona_extcon_device_get_pdata(&pdev->dev, arizona);
++ arizona_extcon_device_get_pdata(dev, arizona);
+
+- info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD");
+- if (IS_ERR(info->micvdd)) {
+- ret = PTR_ERR(info->micvdd);
+- dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
+- return ret;
+- }
++ info->micvdd = devm_regulator_get(dev, "MICVDD");
++ if (IS_ERR(info->micvdd))
++ return dev_err_probe(arizona->dev, PTR_ERR(info->micvdd), "getting MICVDD\n");
+
+ mutex_init(&info->lock);
+- info->arizona = arizona;
+- info->dev = &pdev->dev;
+ info->last_jackdet = ~(ARIZONA_MICD_CLAMP_STS | ARIZONA_JD1_STS);
+ INIT_DELAYED_WORK(&info->hpdet_work, arizona_hpdet_work);
+ INIT_DELAYED_WORK(&info->micd_detect_work, arizona_micd_detect);
+ INIT_DELAYED_WORK(&info->micd_timeout_work, arizona_micd_timeout_work);
+- platform_set_drvdata(pdev, info);
+
+ switch (arizona->type) {
+ case WM5102:
+@@ -1443,29 +1304,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+ break;
+ }
+
+- info->edev = devm_extcon_dev_allocate(&pdev->dev, arizona_cable);
+- if (IS_ERR(info->edev)) {
+- dev_err(&pdev->dev, "failed to allocate extcon device\n");
+- return -ENOMEM;
+- }
+-
+- ret = devm_extcon_dev_register(&pdev->dev, info->edev);
+- if (ret < 0) {
+- dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
+- ret);
+- return ret;
+- }
+-
+- info->input = devm_input_allocate_device(&pdev->dev);
+- if (!info->input) {
+- dev_err(arizona->dev, "Can't allocate input dev\n");
+- ret = -ENOMEM;
+- return ret;
+- }
+-
+- info->input->name = "Headset";
+- info->input->phys = "arizona/extcon";
+-
+ if (!pdata->micd_timeout)
+ pdata->micd_timeout = DEFAULT_MICD_TIMEOUT;
+
+@@ -1487,7 +1325,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+ else
+ mode = GPIOF_OUT_INIT_LOW;
+
+- ret = devm_gpio_request_one(&pdev->dev, pdata->micd_pol_gpio,
++ ret = devm_gpio_request_one(dev, pdata->micd_pol_gpio,
+ mode, "MICD polarity");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+@@ -1509,28 +1347,47 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+ */
+ info->micd_pol_gpio = gpiod_get_optional(arizona->dev,
+ "wlf,micd-pol",
+- GPIOD_OUT_LOW);
++ mode);
+ if (IS_ERR(info->micd_pol_gpio)) {
+ ret = PTR_ERR(info->micd_pol_gpio);
+- dev_err(arizona->dev,
+- "Failed to get microphone polarity GPIO: %d\n",
+- ret);
++ dev_err_probe(arizona->dev, ret, "getting microphone polarity GPIO\n");
+ return ret;
+ }
+ }
+
+ if (arizona->pdata.hpdet_id_gpio > 0) {
+- ret = devm_gpio_request_one(&pdev->dev,
+- arizona->pdata.hpdet_id_gpio,
++ ret = devm_gpio_request_one(dev, arizona->pdata.hpdet_id_gpio,
+ GPIOF_OUT_INIT_LOW,
+ "HPDET");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
+ arizona->pdata.hpdet_id_gpio, ret);
+- goto err_gpio;
++ gpiod_put(info->micd_pol_gpio);
++ return ret;
+ }
+ }
+
++ return 0;
++}
++EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_probe);
++
++int arizona_jack_codec_dev_remove(struct arizona_priv *info)
++{
++ gpiod_put(info->micd_pol_gpio);
++ return 0;
++}
++EXPORT_SYMBOL_GPL(arizona_jack_codec_dev_remove);
++
++static int arizona_jack_enable_jack_detect(struct arizona_priv *info,
++ struct snd_soc_jack *jack)
++{
++ struct arizona *arizona = info->arizona;
++ struct arizona_pdata *pdata = &arizona->pdata;
++ unsigned int val;
++ unsigned int clamp_mode;
++ int jack_irq_fall, jack_irq_rise;
++ int ret, i, j;
++
+ if (arizona->pdata.micd_bias_start_time)
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
+ ARIZONA_MICD_BIAS_STARTTIME_MASK,
+@@ -1568,19 +1425,18 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+ info->num_micd_ranges = ARRAY_SIZE(micd_default_ranges);
+ }
+
+- if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_RANGE) {
+- dev_err(arizona->dev, "Too many MICD ranges: %d\n",
+- arizona->pdata.num_micd_ranges);
++ if (arizona->pdata.num_micd_ranges > ARIZONA_MAX_MICD_BUTTONS) {
++ dev_err(arizona->dev, "Too many MICD ranges: %d > %d\n",
++ arizona->pdata.num_micd_ranges, ARIZONA_MAX_MICD_BUTTONS);
++ return -EINVAL;
+ }
+
+ if (info->num_micd_ranges > 1) {
+ for (i = 1; i < info->num_micd_ranges; i++) {
+ if (info->micd_ranges[i - 1].max >
+ info->micd_ranges[i].max) {
+- dev_err(arizona->dev,
+- "MICD ranges must be sorted\n");
+- ret = -EINVAL;
+- goto err_gpio;
++ dev_err(arizona->dev, "MICD ranges must be sorted\n");
++ return -EINVAL;
+ }
+ }
+ }
+@@ -1598,16 +1454,18 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+ if (j == ARIZONA_NUM_MICD_BUTTON_LEVELS) {
+ dev_err(arizona->dev, "Unsupported MICD level %d\n",
+ info->micd_ranges[i].max);
+- ret = -EINVAL;
+- goto err_gpio;
++ return -EINVAL;
+ }
+
+ dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n",
+ arizona_micd_levels[j], i);
+
+ arizona_micd_set_level(arizona, i, j);
+- input_set_capability(info->input, EV_KEY,
+- info->micd_ranges[i].key);
++
++ /* SND_JACK_BTN_# masks start with the most significant bit */
++ info->micd_button_mask |= SND_JACK_BTN_0 >> i;
++ snd_jack_set_key(jack->jack, SND_JACK_BTN_0 >> i,
++ info->micd_ranges[i].key);
+
+ /* Enable reporting of that range */
+ regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_2,
+@@ -1655,9 +1513,9 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+
+ arizona_extcon_set_mode(info, 0);
+
+- pm_runtime_enable(&pdev->dev);
+- pm_runtime_idle(&pdev->dev);
+- pm_runtime_get_sync(&pdev->dev);
++ info->jack = jack;
++
++ pm_runtime_get_sync(arizona->dev);
+
+ if (info->micd_clamp) {
+ jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
+@@ -1670,43 +1528,40 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+ ret = arizona_request_irq(arizona, jack_irq_rise,
+ "JACKDET rise", arizona_jackdet, info);
+ if (ret != 0) {
+- dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
+- ret);
++ dev_err(arizona->dev, "Failed to get JACKDET rise IRQ: %d\n", ret);
+ goto err_pm;
+ }
+
+ ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1);
+ if (ret != 0) {
+- dev_err(&pdev->dev, "Failed to set JD rise IRQ wake: %d\n",
+- ret);
++ dev_err(arizona->dev, "Failed to set JD rise IRQ wake: %d\n", ret);
+ goto err_rise;
+ }
+
+ ret = arizona_request_irq(arizona, jack_irq_fall,
+ "JACKDET fall", arizona_jackdet, info);
+ if (ret != 0) {
+- dev_err(&pdev->dev, "Failed to get JD fall IRQ: %d\n", ret);
++ dev_err(arizona->dev, "Failed to get JD fall IRQ: %d\n", ret);
+ goto err_rise_wake;
+ }
+
+ ret = arizona_set_irq_wake(arizona, jack_irq_fall, 1);
+ if (ret != 0) {
+- dev_err(&pdev->dev, "Failed to set JD fall IRQ wake: %d\n",
+- ret);
++ dev_err(arizona->dev, "Failed to set JD fall IRQ wake: %d\n", ret);
+ goto err_fall;
+ }
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_MICDET,
+ "MICDET", arizona_micdet, info);
+ if (ret != 0) {
+- dev_err(&pdev->dev, "Failed to get MICDET IRQ: %d\n", ret);
++ dev_err(arizona->dev, "Failed to get MICDET IRQ: %d\n", ret);
+ goto err_fall_wake;
+ }
+
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_HPDET,
+ "HPDET", arizona_hpdet_irq, info);
+ if (ret != 0) {
+- dev_err(&pdev->dev, "Failed to get HPDET IRQ: %d\n", ret);
++ dev_err(arizona->dev, "Failed to get HPDET IRQ: %d\n", ret);
+ goto err_micdet;
+ }
+
+@@ -1718,21 +1573,12 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret != 0)
+- dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
+- ret);
+-
+- ret = input_register_device(info->input);
+- if (ret) {
+- dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
+- goto err_hpdet;
+- }
++ dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n", ret);
+
+- pm_runtime_put(&pdev->dev);
++ pm_runtime_put(arizona->dev);
+
+ return 0;
+
+-err_hpdet:
+- arizona_free_irq(arizona, ARIZONA_IRQ_HPDET, info);
+ err_micdet:
+ arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
+ err_fall_wake:
+@@ -1744,39 +1590,20 @@ static int arizona_extcon_probe(struct platform_device *pdev)
+ err_rise:
+ arizona_free_irq(arizona, jack_irq_rise, info);
+ err_pm:
+- pm_runtime_put(&pdev->dev);
+- pm_runtime_disable(&pdev->dev);
+-err_gpio:
+- gpiod_put(info->micd_pol_gpio);
++ pm_runtime_put(arizona->dev);
++ info->jack = NULL;
+ return ret;
+ }
+
+-static int arizona_extcon_remove(struct platform_device *pdev)
++static int arizona_jack_disable_jack_detect(struct arizona_priv *info)
+ {
+- struct arizona_extcon_info *info = platform_get_drvdata(pdev);
+ struct arizona *arizona = info->arizona;
+ int jack_irq_rise, jack_irq_fall;
+ bool change;
+ int ret;
+
+- ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
+- ARIZONA_MICD_ENA, 0,
+- &change);
+- if (ret < 0) {
+- dev_err(&pdev->dev, "Failed to disable micd on remove: %d\n",
+- ret);
+- } else if (change) {
+- regulator_disable(info->micvdd);
+- pm_runtime_put(info->dev);
+- }
+-
+- gpiod_put(info->micd_pol_gpio);
+-
+- pm_runtime_disable(&pdev->dev);
+-
+- regmap_update_bits(arizona->regmap,
+- ARIZONA_MICD_CLAMP_CONTROL,
+- ARIZONA_MICD_CLAMP_MODE_MASK, 0);
++ if (!info->jack)
++ return 0;
+
+ if (info->micd_clamp) {
+ jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE;
+@@ -1793,24 +1620,38 @@ static int arizona_extcon_remove(struct platform_device *pdev)
+ arizona_free_irq(arizona, jack_irq_rise, info);
+ arizona_free_irq(arizona, jack_irq_fall, info);
+ cancel_delayed_work_sync(&info->hpdet_work);
++ cancel_delayed_work_sync(&info->micd_detect_work);
++ cancel_delayed_work_sync(&info->micd_timeout_work);
++
++ ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1,
++ ARIZONA_MICD_ENA, 0,
++ &change);
++ if (ret < 0) {
++ dev_err(arizona->dev, "Failed to disable micd on remove: %d\n", ret);
++ } else if (change) {
++ regulator_disable(info->micvdd);
++ pm_runtime_put(arizona->dev);
++ }
++
++ regmap_update_bits(arizona->regmap,
++ ARIZONA_MICD_CLAMP_CONTROL,
++ ARIZONA_MICD_CLAMP_MODE_MASK, 0);
+ regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
+ ARIZONA_JD1_ENA, 0);
+ arizona_clk32k_disable(arizona);
++ info->jack = NULL;
+
+ return 0;
+ }
+
+-static struct platform_driver arizona_extcon_driver = {
+- .driver = {
+- .name = "arizona-extcon",
+- },
+- .probe = arizona_extcon_probe,
+- .remove = arizona_extcon_remove,
+-};
+-
+-module_platform_driver(arizona_extcon_driver);
++int arizona_jack_set_jack(struct snd_soc_component *component,
++ struct snd_soc_jack *jack, void *data)
++{
++ struct arizona_priv *info = snd_soc_component_get_drvdata(component);
+
+-MODULE_DESCRIPTION("Arizona Extcon driver");
+-MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+-MODULE_LICENSE("GPL");
+-MODULE_ALIAS("platform:extcon-arizona");
++ if (jack)
++ return arizona_jack_enable_jack_detect(info, jack);
++ else
++ return arizona_jack_disable_jack_detect(info);
++}
++EXPORT_SYMBOL_GPL(arizona_jack_set_jack);
+diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
+index b893d3e4c..173ebd0bf 100644
+--- a/sound/soc/codecs/arizona.h
++++ b/sound/soc/codecs/arizona.h
+@@ -91,6 +91,41 @@ struct arizona_priv {
+ unsigned int dvfs_reqs;
+ struct mutex dvfs_lock;
+ bool dvfs_cached;
++
++ /* Variables used by arizona-jack.c code */
++ struct mutex lock;
++ struct delayed_work hpdet_work;
++ struct delayed_work micd_detect_work;
++ struct delayed_work micd_timeout_work;
++ struct snd_soc_jack *jack;
++ struct regulator *micvdd;
++ struct gpio_desc *micd_pol_gpio;
++
++ u16 last_jackdet;
++
++ int micd_mode;
++ const struct arizona_micd_config *micd_modes;
++ int micd_num_modes;
++
++ int micd_button_mask;
++ const struct arizona_micd_range *micd_ranges;
++ int num_micd_ranges;
++
++ bool micd_reva;
++ bool micd_clamp;
++
++ bool hpdet_active;
++ bool hpdet_done;
++ bool hpdet_retried;
++
++ bool mic;
++ bool detecting;
++
++ int num_hpdet_res;
++ unsigned int hpdet_res[3];
++
++ int jack_flips;
++ int hpdet_ip_version;
+ };
+
+ struct arizona_voice_trigger_info {
+@@ -222,6 +257,9 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
+ #define ARIZONA_RATE_ENUM_SIZE 4
+ #define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
+
++/* SND_JACK_* mask for supported cable/switch types */
++#define ARIZONA_JACK_MASK (SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_MECHANICAL)
++
+ extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
+ extern const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+ extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
+@@ -351,4 +389,10 @@ static inline int arizona_unregister_notifier(struct snd_soc_component *componen
+
+ int arizona_of_get_audio_pdata(struct arizona *arizona);
+
++int arizona_jack_codec_dev_probe(struct arizona_priv *info, struct device *dev);
++int arizona_jack_codec_dev_remove(struct arizona_priv *info);
++
++int arizona_jack_set_jack(struct snd_soc_component *component,
++ struct snd_soc_jack *jack, void *data);
++
+ #endif
+diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
+index 70d353b63..b77595fb3 100644
+--- a/sound/soc/codecs/wm5102.c
++++ b/sound/soc/codecs/wm5102.c
+@@ -2004,6 +2004,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm5102 = {
+ .remove = wm5102_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm5102_set_fll,
++ .set_jack = arizona_jack_set_jack,
+ .name = DRV_NAME,
+ .compress_ops = &wm5102_compress_ops,
+ .controls = wm5102_snd_controls,
+@@ -2057,6 +2058,11 @@ static int wm5102_probe(struct platform_device *pdev)
+ if (ret != 0)
+ return ret;
+
++ /* This may return -EPROBE_DEFER, so do this early on */
++ ret = arizona_jack_codec_dev_probe(&wm5102->core, &pdev->dev);
++ if (ret)
++ return ret;
++
+ for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
+ wm5102->fll[i].vco_mult = 1;
+
+@@ -2089,7 +2095,7 @@ static int wm5102_probe(struct platform_device *pdev)
+ wm5102);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+- return ret;
++ goto err_jack_codec_dev;
+ }
+
+ ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1);
+@@ -2123,6 +2129,8 @@ static int wm5102_probe(struct platform_device *pdev)
+ err_dsp_irq:
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
++err_jack_codec_dev:
++ arizona_jack_codec_dev_remove(&wm5102->core);
+
+ return ret;
+ }
+@@ -2141,6 +2149,8 @@ static int wm5102_remove(struct platform_device *pdev)
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
+
++ arizona_jack_codec_dev_remove(&wm5102->core);
++
+ return 0;
+ }
+
+diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
+index 4238929b2..ef22051a3 100644
+--- a/sound/soc/codecs/wm5110.c
++++ b/sound/soc/codecs/wm5110.c
+@@ -2370,6 +2370,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm5110 = {
+ .remove = wm5110_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm5110_set_fll,
++ .set_jack = arizona_jack_set_jack,
+ .name = DRV_NAME,
+ .compress_ops = &wm5110_compress_ops,
+ .controls = wm5110_snd_controls,
+@@ -2424,6 +2425,11 @@ static int wm5110_probe(struct platform_device *pdev)
+ return ret;
+ }
+
++ /* This may return -EPROBE_DEFER, so do this early on */
++ ret = arizona_jack_codec_dev_probe(&wm5110->core, &pdev->dev);
++ if (ret)
++ return ret;
++
+ for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++)
+ wm5110->fll[i].vco_mult = 3;
+
+@@ -2456,7 +2462,7 @@ static int wm5110_probe(struct platform_device *pdev)
+ wm5110);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+- return ret;
++ goto err_jack_codec_dev;
+ }
+
+ ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 1);
+@@ -2490,6 +2496,8 @@ static int wm5110_probe(struct platform_device *pdev)
+ err_dsp_irq:
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
++err_jack_codec_dev:
++ arizona_jack_codec_dev_remove(&wm5110->core);
+
+ return ret;
+ }
+@@ -2510,6 +2518,8 @@ static int wm5110_remove(struct platform_device *pdev)
+ arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
+
++ arizona_jack_codec_dev_remove(&wm5110->core);
++
+ return 0;
+ }
+
+diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
+index 229f2986c..4f5a84896 100644
+--- a/sound/soc/codecs/wm8997.c
++++ b/sound/soc/codecs/wm8997.c
+@@ -1096,6 +1096,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8997 = {
+ .remove = wm8997_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8997_set_fll,
++ .set_jack = arizona_jack_set_jack,
+ .controls = wm8997_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8997_snd_controls),
+ .dapm_widgets = wm8997_dapm_widgets,
+@@ -1132,6 +1133,11 @@ static int wm8997_probe(struct platform_device *pdev)
+
+ arizona_init_dvfs(&wm8997->core);
+
++ /* This may return -EPROBE_DEFER, so do this early on */
++ ret = arizona_jack_codec_dev_probe(&wm8997->core, &pdev->dev);
++ if (ret)
++ return ret;
++
+ for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
+ wm8997->fll[i].vco_mult = 1;
+
+@@ -1163,10 +1169,10 @@ static int wm8997_probe(struct platform_device *pdev)
+
+ ret = arizona_init_vol_limit(arizona);
+ if (ret < 0)
+- return ret;
++ goto err_jack_codec_dev;
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+- return ret;
++ goto err_jack_codec_dev;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_wm8997,
+@@ -1181,6 +1187,8 @@ static int wm8997_probe(struct platform_device *pdev)
+
+ err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
++err_jack_codec_dev:
++ arizona_jack_codec_dev_remove(&wm8997->core);
+
+ return ret;
+ }
+@@ -1194,6 +1202,8 @@ static int wm8997_remove(struct platform_device *pdev)
+
+ arizona_free_spk_irqs(arizona);
+
++ arizona_jack_codec_dev_remove(&wm8997->core);
++
+ return 0;
+ }
+
+diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
+index 541325429..f74af1c46 100644
+--- a/sound/soc/codecs/wm8998.c
++++ b/sound/soc/codecs/wm8998.c
+@@ -1316,6 +1316,7 @@ static const struct snd_soc_component_driver soc_component_dev_wm8998 = {
+ .remove = wm8998_component_remove,
+ .set_sysclk = arizona_set_sysclk,
+ .set_pll = wm8998_set_fll,
++ .set_jack = arizona_jack_set_jack,
+ .controls = wm8998_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8998_snd_controls),
+ .dapm_widgets = wm8998_dapm_widgets,
+@@ -1350,6 +1351,11 @@ static int wm8998_probe(struct platform_device *pdev)
+ wm8998->core.arizona = arizona;
+ wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */
+
++ /* This may return -EPROBE_DEFER, so do this early on */
++ ret = arizona_jack_codec_dev_probe(&wm8998->core, &pdev->dev);
++ if (ret)
++ return ret;
++
+ for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++)
+ wm8998->fll[i].vco_mult = 1;
+
+@@ -1392,6 +1398,7 @@ static int wm8998_probe(struct platform_device *pdev)
+ arizona_free_spk_irqs(arizona);
+ err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
++ arizona_jack_codec_dev_remove(&wm8998->core);
+
+ return ret;
+ }
+@@ -1405,6 +1412,8 @@ static int wm8998_remove(struct platform_device *pdev)
+
+ arizona_free_spk_irqs(arizona);
+
++ arizona_jack_codec_dev_remove(&wm8998->core);
++
+ return 0;
+ }
+
+diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
+index b58b9b60d..d1d28129a 100644
+--- a/sound/soc/intel/boards/Kconfig
++++ b/sound/soc/intel/boards/Kconfig
+@@ -111,6 +111,18 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
++config SND_SOC_INTEL_BYTCR_WM5102_MACH
++ tristate "Baytrail and Baytrail-CR with WM5102 codec"
++ depends on MFD_ARIZONA && MFD_WM5102 && SPI_MASTER && ACPI
++ depends on X86_INTEL_LPSS || COMPILE_TEST
++ select SND_SOC_ACPI
++ select SND_SOC_WM5102
++ help
++ This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
++ platforms with WM5102 audio codec.
++ Say Y if you have such a device.
++ If unsure select "N".
++
+ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+ tristate "Cherrytrail & Braswell with RT5672 codec"
+ depends on I2C && ACPI
+diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
+index 5f03e484b..616c5fbab 100644
+--- a/sound/soc/intel/boards/Makefile
++++ b/sound/soc/intel/boards/Makefile
+@@ -10,6 +10,7 @@ snd-soc-sst-sof-wm8804-objs := sof_wm8804.o
+ snd-soc-sst-glk-rt5682_max98357a-objs := glk_rt5682_max98357a.o hda_dsp_common.o
+ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
+ snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
++snd-soc-sst-bytcr-wm5102-objs := bytcr_wm5102.o
+ snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
+ snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
+ snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+@@ -51,6 +52,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5650_MACH) += snd-soc-sst-bdw-rt5650-mach.o
+ obj-$(CONFIG_SND_SOC_INTEL_BDW_RT5677_MACH) += snd-soc-sst-bdw-rt5677-mach.o
+ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
+ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5651_MACH) += snd-soc-sst-bytcr-rt5651.o
++obj-$(CONFIG_SND_SOC_INTEL_BYTCR_WM5102_MACH) += snd-soc-sst-bytcr-wm5102.o
+ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
+ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+diff --git a/sound/soc/intel/boards/bytcr_wm5102.c b/sound/soc/intel/boards/bytcr_wm5102.c
+new file mode 100644
+index 000000000..cdfe203ed
+--- /dev/null
++++ b/sound/soc/intel/boards/bytcr_wm5102.c
+@@ -0,0 +1,491 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * bytcr_wm5102.c - ASoc Machine driver for Intel Baytrail platforms with a
++ * Wolfson Microelectronics WM5102 codec
++ *
++ * Copyright (C) 2020 Hans de Goede <hdegoede@redhat.com>
++ * Loosely based on bytcr_rt5640.c which is:
++ * Copyright (C) 2014-2020 Intel Corp
++ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
++ */
++
++#include <linux/acpi.h>
++#include <linux/clk.h>
++#include <linux/device.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/spi/spi.h>
++#include <sound/jack.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-acpi.h>
++#include "../../codecs/wm5102.h"
++#include "../atom/sst-atom-controls.h"
++
++#define MCLK_FREQ 25000000
++
++#define WM5102_MAX_SYSCLK_4K 49152000 /* max sysclk for 4K family */
++#define WM5102_MAX_SYSCLK_11025 45158400 /* max sysclk for 11.025K family */
++
++struct byt_wm5102_private {
++ struct snd_soc_jack jack;
++ struct clk *mclk;
++ struct gpio_desc *spkvdd_en_gpio;
++};
++
++static int byt_wm5102_spkvdd_power_event(struct snd_soc_dapm_widget *w,
++ struct snd_kcontrol *kcontrol, int event)
++{
++ struct snd_soc_card *card = w->dapm->card;
++ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
++
++ gpiod_set_value_cansleep(priv->spkvdd_en_gpio,
++ !!SND_SOC_DAPM_EVENT_ON(event));
++
++ return 0;
++}
++
++static int byt_wm5102_prepare_and_enable_pll1(struct snd_soc_dai *codec_dai, int rate)
++{
++ struct snd_soc_component *codec_component = codec_dai->component;
++ int sr_mult = ((rate % 4000) == 0) ?
++ (WM5102_MAX_SYSCLK_4K / rate) :
++ (WM5102_MAX_SYSCLK_11025 / rate);
++ int ret;
++
++ /* Reset FLL1 */
++ snd_soc_dai_set_pll(codec_dai, WM5102_FLL1_REFCLK, ARIZONA_FLL_SRC_NONE, 0, 0);
++ snd_soc_dai_set_pll(codec_dai, WM5102_FLL1, ARIZONA_FLL_SRC_NONE, 0, 0);
++
++ /* Configure the FLL1 PLL before selecting it */
++ ret = snd_soc_dai_set_pll(codec_dai, WM5102_FLL1, ARIZONA_CLK_SRC_MCLK1,
++ MCLK_FREQ, rate * sr_mult);
++ if (ret) {
++ dev_err(codec_component->dev, "Error setting PLL: %d\n", ret);
++ return ret;
++ }
++
++ ret = snd_soc_component_set_sysclk(codec_component, ARIZONA_CLK_SYSCLK,
++ ARIZONA_CLK_SRC_FLL1, rate * sr_mult,
++ SND_SOC_CLOCK_IN);
++ if (ret) {
++ dev_err(codec_component->dev, "Error setting SYSCLK: %d\n", ret);
++ return ret;
++ }
++
++ ret = snd_soc_dai_set_sysclk(codec_dai, ARIZONA_CLK_SYSCLK,
++ rate * 512, SND_SOC_CLOCK_IN);
++ if (ret) {
++ dev_err(codec_component->dev, "Error setting clock: %d\n", ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int platform_clock_control(struct snd_soc_dapm_widget *w,
++ struct snd_kcontrol *k, int event)
++{
++ struct snd_soc_dapm_context *dapm = w->dapm;
++ struct snd_soc_card *card = dapm->card;
++ struct snd_soc_dai *codec_dai;
++ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
++ int ret;
++
++ codec_dai = snd_soc_card_get_codec_dai(card, "wm5102-aif1");
++ if (!codec_dai) {
++ dev_err(card->dev, "Error codec DAI not found\n");
++ return -EIO;
++ }
++
++ if (SND_SOC_DAPM_EVENT_ON(event)) {
++ ret = clk_prepare_enable(priv->mclk);
++ if (ret) {
++ dev_err(card->dev, "Error enabling MCLK: %d\n", ret);
++ return ret;
++ }
++ ret = byt_wm5102_prepare_and_enable_pll1(codec_dai, 48000);
++ if (ret) {
++ dev_err(card->dev, "Error setting codec sysclk: %d\n", ret);
++ return ret;
++ }
++ } else {
++ /*
++ * The WM5102 has a separate 32KHz clock for jack-detect
++ * so we can disable the PLL, followed by disabling the
++ * platform clock which is the source-clock for the PLL.
++ */
++ snd_soc_dai_set_pll(codec_dai, WM5102_FLL1, ARIZONA_FLL_SRC_NONE, 0, 0);
++ clk_disable_unprepare(priv->mclk);
++ }
++
++ return 0;
++}
++
++static const struct snd_soc_dapm_widget byt_wm5102_widgets[] = {
++ SND_SOC_DAPM_HP("Headphone", NULL),
++ SND_SOC_DAPM_MIC("Headset Mic", NULL),
++ SND_SOC_DAPM_MIC("Internal Mic", NULL),
++ SND_SOC_DAPM_SPK("Speaker", NULL),
++ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
++ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
++ SND_SOC_DAPM_POST_PMD),
++ SND_SOC_DAPM_SUPPLY("Speaker VDD", SND_SOC_NOPM, 0, 0,
++ byt_wm5102_spkvdd_power_event,
++ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
++};
++
++static const struct snd_soc_dapm_route byt_wm5102_audio_map[] = {
++ {"Headphone", NULL, "Platform Clock"},
++ {"Headset Mic", NULL, "Platform Clock"},
++ {"Internal Mic", NULL, "Platform Clock"},
++ {"Speaker", NULL, "Platform Clock"},
++
++ {"Speaker", NULL, "SPKOUTLP"},
++ {"Speaker", NULL, "SPKOUTLN"},
++ {"Speaker", NULL, "SPKOUTRP"},
++ {"Speaker", NULL, "SPKOUTRN"},
++ {"Speaker", NULL, "Speaker VDD"},
++
++ {"Headphone", NULL, "HPOUT1L"},
++ {"Headphone", NULL, "HPOUT1R"},
++
++ {"Internal Mic", NULL, "MICBIAS3"},
++ {"IN3L", NULL, "Internal Mic"},
++
++ /*
++ * The Headset Mix uses MICBIAS1 or 2 depending on if a CTIA/OMTP Headset
++ * is connected, as the MICBIAS is applied after the CTIA/OMTP cross-switch.
++ */
++ {"Headset Mic", NULL, "MICBIAS1"},
++ {"Headset Mic", NULL, "MICBIAS2"},
++ {"IN1L", NULL, "Headset Mic"},
++
++ {"AIF1 Playback", NULL, "ssp0 Tx"},
++ {"ssp0 Tx", NULL, "modem_out"},
++
++ {"modem_in", NULL, "ssp0 Rx"},
++ {"ssp0 Rx", NULL, "AIF1 Capture"},
++};
++
++static const struct snd_kcontrol_new byt_wm5102_controls[] = {
++ SOC_DAPM_PIN_SWITCH("Headphone"),
++ SOC_DAPM_PIN_SWITCH("Headset Mic"),
++ SOC_DAPM_PIN_SWITCH("Internal Mic"),
++ SOC_DAPM_PIN_SWITCH("Speaker"),
++};
++
++static struct snd_soc_jack_pin byt_wm5102_pins[] = {
++ {
++ .pin = "Headphone",
++ .mask = SND_JACK_HEADPHONE,
++ },
++ {
++ .pin = "Headset Mic",
++ .mask = SND_JACK_MICROPHONE,
++ },
++};
++
++static int byt_wm5102_init(struct snd_soc_pcm_runtime *runtime)
++{
++ struct snd_soc_card *card = runtime->card;
++ struct byt_wm5102_private *priv = snd_soc_card_get_drvdata(card);
++ struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component;
++ int ret, jack_type;
++
++ card->dapm.idle_bias_off = true;
++
++ ret = snd_soc_add_card_controls(card, byt_wm5102_controls,
++ ARRAY_SIZE(byt_wm5102_controls));
++ if (ret) {
++ dev_err(card->dev, "Error adding card controls: %d\n", ret);
++ return ret;
++ }
++
++ /*
++ * The firmware might enable the clock at boot (this information
++ * may or may not be reflected in the enable clock register).
++ * To change the rate we must disable the clock first to cover these
++ * cases. Due to common clock framework restrictions that do not allow
++ * to disable a clock that has not been enabled, we need to enable
++ * the clock first.
++ */
++ ret = clk_prepare_enable(priv->mclk);
++ if (!ret)
++ clk_disable_unprepare(priv->mclk);
++
++ ret = clk_set_rate(priv->mclk, MCLK_FREQ);
++ if (ret) {
++ dev_err(card->dev, "Error setting MCLK rate: %d\n", ret);
++ return ret;
++ }
++
++ jack_type = ARIZONA_JACK_MASK | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
++ SND_JACK_BTN_2 | SND_JACK_BTN_3;
++ ret = snd_soc_card_jack_new(card, "Headset", jack_type,
++ &priv->jack, byt_wm5102_pins,
++ ARRAY_SIZE(byt_wm5102_pins));
++ if (ret) {
++ dev_err(card->dev, "Error creating jack: %d\n", ret);
++ return ret;
++ }
++
++ snd_soc_component_set_jack(component, &priv->jack, NULL);
++
++ return 0;
++}
++
++static const struct snd_soc_pcm_stream byt_wm5102_dai_params = {
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,</