From 0ab8d3145b982810bf5b1c1d2388f5c3820151a8 Mon Sep 17 00:00:00 2001
From: christophe montaud <christophe.montaud@st.com>
Date: Mon, 14 Jan 2019 17:18:56 +0100
Subject: [PATCH 53/55] ARM stm32mp1 r0 rc4 hotfix-w903.1 DRIVERS

---
 .../bindings/connector/usb-connector.txt           |   2 +
 .../devicetree/bindings/usb/st,typec-stusb.txt     |  32 ++
 drivers/bluetooth/hci_bcm.c                        |   3 +-
 drivers/gpu/drm/stm/dw_mipi_dsi-stm.c              |   9 +-
 drivers/iio/adc/stm32-dfsdm-adc.c                  |   5 +-
 drivers/media/platform/stm32/stm32-dcmi.c          |  17 +
 drivers/mfd/stm32-pwr.c                            |  14 -
 .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c |   5 +
 drivers/remoteproc/remoteproc_core.c               |  10 +
 drivers/remoteproc/remoteproc_virtio.c             |   2 +
 drivers/remoteproc/stm32_rproc.c                   |  63 ++-
 drivers/usb/typec/Kconfig                          |   9 +
 drivers/usb/typec/Makefile                         |   1 +
 drivers/usb/typec/class.c                          |  15 +
 drivers/usb/typec/typec_stusb.c                    | 589 +++++++++++++++++++++
 include/linux/usb/typec.h                          |   1 +
 sound/soc/stm/stm32_adfsdm.c                       |  21 +-
 17 files changed, 761 insertions(+), 37 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/usb/st,typec-stusb.txt
 create mode 100644 drivers/usb/typec/typec_stusb.c

diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt
index 8855bfc..bf43ee9 100644
--- a/Documentation/devicetree/bindings/connector/usb-connector.txt
+++ b/Documentation/devicetree/bindings/connector/usb-connector.txt
@@ -18,6 +18,8 @@ Optional properties:
 Optional properties for usb-c-connector:
 - power-role: should be one of "source", "sink" or "dual"(DRP) if typec
   connector has power support.
+- power-opmode: should be one of "default", "1.5A", "3.0A" or
+  "usb_power_delivery" if typec connector has power support.
 - try-power-role: preferred power role if "dual"(DRP) can support Try.SNK
   or Try.SRC, should be "sink" for Try.SNK or "source" for Try.SRC.
 - data-role: should be one of "host", "device", "dual"(DRD) if typec
diff --git a/Documentation/devicetree/bindings/usb/st,typec-stusb.txt b/Documentation/devicetree/bindings/usb/st,typec-stusb.txt
new file mode 100644
index 0000000..817360d
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/st,typec-stusb.txt
@@ -0,0 +1,32 @@
+STMicroelectronics STUSB Type-C Controller family
+
+Required properties:
+ - compatible: should be "st,stusb1600".
+ - reg: I2C slave address of the device.
+
+Optional properties:
+ - vdd-supply: main power supply [4.1V;22V].
+ - vsys-supply: low power supply [3.0V;5.5V].
+ - vconn-supply: power supply [2.7;5.5V] used to supply VConn on CC pin in
+   source or dual power role.
+
+USB-C connector attached to STUSB Type-C port controller can be described in
+an optional connector sub-node. Refer to ../connector/usb-connector.txt.
+
+Example :
+
+	typec: stusb1600@28 {
+		compatible = "st,stusb1600";
+		reg = <0x28>;
+		vdd-supply = <&vbus_drd>;
+		vsys-supply = <&vdd_usb>;
+
+		usb_con: connector {
+			compatible = "usb-c-connector";
+			label = "USB-C";
+			power-role = "dual";
+			power-opmode = "1.5A";
+			data-role = "dual";
+		};
+	};
+
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index ddbd8c6..7a23fee 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -1295,7 +1295,8 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
 	if (!bcmdev->shutdown) {
 		dev_warn(&serdev->dev,
 			 "No reset resource, using default baud rate\n");
-		bcmdev->oper_speed = bcmdev->init_speed;
+		if (!bcmdev->oper_speed)
+			bcmdev->oper_speed = bcmdev->init_speed;
 	}
 
 	err = bcm_gpio_set_power(bcmdev, false);
diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
index a6edd86..a373651 100644
--- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
+++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
@@ -227,7 +227,6 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
 	u32 val;
 
 	/* Update lane capabilities according to hw version */
-	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
 	dsi->lane_min_kbps = LANE_MIN_KBPS;
 	dsi->lane_max_kbps = LANE_MAX_KBPS;
 	if (dsi->hw_version == HWVER_131) {
@@ -347,6 +346,14 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
+	if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) {
+		dev_err(dev, "bad dsi hardware version\n");
+		clk_disable_unprepare(dsi->pllref_clk);
+		regulator_disable(dsi->vdd_supply);
+		return -ENODEV;
+	}
+
 	dw_mipi_dsi_stm_plat_data.base = dsi->base;
 	dw_mipi_dsi_stm_plat_data.priv_data = dsi;
 
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index c97d9ee..13854b7 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -569,8 +569,9 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
 
 	oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
 	if (spi_freq % sample_freq)
-		dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
-			 spi_freq / oversamp);
+		dev_dbg(&indio_dev->dev,
+			"Rate not accurate. requested (%u), actual (%u)\n",
+			sample_freq, spi_freq / oversamp);
 
 	ret = stm32_dfsdm_set_osrs(fl, 0, oversamp);
 	if (ret < 0) {
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c
index 49849e6..6f6dc66 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/stm32/stm32-dcmi.c
@@ -167,6 +167,9 @@ struct stm32_dcmi {
 	int				errors_count;
 	int				overrun_count;
 	int				buffers_count;
+
+	/* Ensure DMA operations atomicity */
+	struct mutex			dma_lock;
 };
 
 static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n)
@@ -317,6 +320,13 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
 		return ret;
 	}
 
+	/*
+	 * Avoid call of dmaengine_terminate_all() between
+	 * dmaengine_prep_slave_single() and dmaengine_submit()
+	 * by locking the whole DMA submission sequence
+	 */
+	mutex_lock(&dcmi->dma_lock);
+
 	/* Prepare a DMA transaction */
 	desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
 					   buf->size,
@@ -325,6 +335,7 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
 	if (!desc) {
 		dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n",
 			__func__, &buf->paddr, buf->size);
+		mutex_unlock(&dcmi->dma_lock);
 		return -EINVAL;
 	}
 
@@ -336,9 +347,12 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
 	dcmi->dma_cookie = dmaengine_submit(desc);
 	if (dma_submit_error(dcmi->dma_cookie)) {
 		dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__);
+		mutex_unlock(&dcmi->dma_lock);
 		return -ENXIO;
 	}
 
+	mutex_unlock(&dcmi->dma_lock);
+
 	dma_async_issue_pending(dcmi->dma_chan);
 
 	return 0;
@@ -746,7 +760,9 @@ static void dcmi_stop_streaming(struct vb2_queue *vq)
 	spin_unlock_irq(&dcmi->irqlock);
 
 	/* Stop all pending DMA operations */
+	mutex_lock(&dcmi->dma_lock);
 	dmaengine_terminate_all(dcmi->dma_chan);
+	mutex_unlock(&dcmi->dma_lock);
 
 	pm_runtime_put(dcmi->dev);
 
@@ -1740,6 +1756,7 @@ static int dcmi_probe(struct platform_device *pdev)
 
 	spin_lock_init(&dcmi->irqlock);
 	mutex_init(&dcmi->lock);
+	mutex_init(&dcmi->dma_lock);
 	init_completion(&dcmi->complete);
 	INIT_LIST_HEAD(&dcmi->buffers);
 
diff --git a/drivers/mfd/stm32-pwr.c b/drivers/mfd/stm32-pwr.c
index 206a933..088b3d6 100644
--- a/drivers/mfd/stm32-pwr.c
+++ b/drivers/mfd/stm32-pwr.c
@@ -70,19 +70,6 @@ static void stm32_pwr_irq_unmask(struct irq_data *d)
 	SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq));
 }
 
-static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on)
-{
-	struct stm32_pwr_data *priv = d->domain->host_data;
-
-	pr_debug("irq:%lu on:%d\n", d->hwirq, on);
-	if (on)
-		enable_irq_wake(priv->irq);
-	else
-		disable_irq_wake(priv->irq);
-
-	return 0;
-}
-
 static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type)
 {
 	struct stm32_pwr_data *priv = d->domain->host_data;
@@ -125,7 +112,6 @@ static struct irq_chip stm32_pwr_irq_chip = {
 	.irq_mask = stm32_pwr_irq_mask,
 	.irq_unmask = stm32_pwr_irq_unmask,
 	.irq_set_type = stm32_pwr_irq_set_type,
-	.irq_set_wake = stm32_pwr_irq_set_wake,
 };
 
 static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id,
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index ffa243e..55974a4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -496,6 +496,11 @@ int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid)
 	brcmf_dbg(TRACE, "reqid=%llu\n", reqid);
 
 	pi = ifp_to_pno(ifp);
+
+	/* No PNO reqeuset */
+	if (!pi->n_reqs)
+		return 0;
+
 	err = brcmf_pno_remove_request(pi, reqid);
 	if (err)
 		return err;
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index dd7e348..6430747 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1456,6 +1456,9 @@ static int rproc_stop(struct rproc *rproc, bool crashed)
 	struct device *dev = &rproc->dev;
 	int ret;
 
+	if (rproc->state == RPROC_OFFLINE)
+		return 0;
+
 	/* Stop any subdevices for the remote processor */
 	rproc_stop_subdevices(rproc, crashed);
 
@@ -1614,6 +1617,13 @@ int rproc_trigger_recovery(struct rproc *rproc)
 	/* generate coredump */
 	rproc_coredump(rproc);
 
+	if (!rproc->firmware) {
+		/* we don't know how to recover it, so try to shutdown it*/
+		mutex_unlock(&rproc->lock);
+		rproc_shutdown(rproc);
+		return 0;
+	}
+
 	/* load firmware */
 	ret = request_firmware(&firmware_p, rproc->firmware, dev);
 	if (ret < 0) {
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
index 9ee63c0..78462f5 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -359,6 +359,8 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
 		}
 	}
 
+	/* Reset vdev struct as you don't know how it has been previously allocated */
+	memset(vdev, 0, sizeof(struct virtio_device));
 	vdev->id.device	= id,
 	vdev->config = &rproc_virtio_config_ops,
 	vdev->dev.parent = dev;
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index 70b7e55c..1d2be11 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -67,7 +67,7 @@ struct stm32_rproc {
 	struct stm32_rproc_mem *rmems;
 	struct stm32_mbox mb[MBOX_NB_MBX];
 	bool secured_soc;
-	u32 rsc_addr;
+	void __iomem *rsc_va;
 	u32 rsc_len;
 };
 
@@ -84,7 +84,27 @@ static int stm32_rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da)
 		    pa >= p_mem->bus_addr + p_mem->size)
 			continue;
 		*da = pa - p_mem->bus_addr + p_mem->dev_addr;
-		dev_dbg(rproc->dev.parent, "da %llx to pa %#x\n", *da, pa);
+		dev_dbg(rproc->dev.parent, "pa %#x to da %llx\n", pa, *da);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int stm32_rproc_da_to_pa(struct rproc *rproc, u64 da, phys_addr_t *pa)
+{
+	unsigned int i;
+	struct stm32_rproc *ddata = rproc->priv;
+	struct stm32_rproc_mem *p_mem;
+
+	for (i = 0; i < ddata->nb_rmems; i++) {
+		p_mem = &ddata->rmems[i];
+
+		if (da < p_mem->dev_addr ||
+		    da >= p_mem->dev_addr + p_mem->size)
+			continue;
+		*pa = da - p_mem->dev_addr + p_mem->bus_addr;
+		dev_err(rproc->dev.parent, "da %llx to pa %#x\n", da, *pa);
 		return 0;
 	}
 
@@ -209,11 +229,9 @@ static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc,
 		return 0;
 	}
 
-	if (ddata->rsc_addr) {
+	if (ddata->rsc_va) {
 		tablesz = ddata->rsc_len;
-		table = (struct resource_table *)
-				rproc_da_to_va(rproc, (u64)ddata->rsc_addr,
-					       ddata->rsc_len);
+		table = (struct resource_table *)ddata->rsc_va;
 		rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
 		if (!rproc->cached_table)
 			return -ENOMEM;
@@ -297,12 +315,7 @@ stm32_rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
 	if (!rproc->early_boot)
 		return rproc_elf_find_loaded_rsc_table(rproc, fw);
 
-	if (ddata->rsc_addr)
-		return (struct resource_table *)
-				rproc_da_to_va(rproc, (u64)ddata->rsc_addr,
-					       ddata->rsc_len);
-
-	return NULL;
+	return (struct resource_table *)ddata->rsc_va;
 }
 
 static int stm32_rproc_elf_sanity_check(struct rproc *rproc,
@@ -582,6 +595,8 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
 	struct rproc *rproc = platform_get_drvdata(pdev);
 	struct stm32_rproc *ddata = rproc->priv;
 	struct stm32_syscon tz;
+	phys_addr_t rsc_pa;
+	u32 rsc_da;
 	unsigned int tzen;
 	int err, irq;
 
@@ -636,10 +651,14 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
 	rproc->auto_boot = of_property_read_bool(np, "auto_boot");
 	rproc->recovery_disabled = !of_property_read_bool(np, "recovery");
 
+	err = stm32_rproc_of_memory_translations(rproc);
+	if (err)
+		return err;
+
 	if (of_property_read_bool(np, "early-booted")) {
 		rproc->early_boot = true;
 
-		err = of_property_read_u32(np, "rsc-address", &ddata->rsc_addr);
+		err = of_property_read_u32(np, "rsc-address", &rsc_da);
 		if (!err) {
 			err = of_property_read_u32(np, "rsc-size",
 						   &ddata->rsc_len);
@@ -649,9 +668,19 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev)
 				return err;
 			}
 		}
+		err = stm32_rproc_da_to_pa(rproc, rsc_da, &rsc_pa);
+		if (err)
+			return err;
+		ddata->rsc_va = ioremap_wc(rsc_pa, ddata->rsc_len);
+		if (IS_ERR_OR_NULL(ddata->rsc_va)) {
+			dev_err(dev, "Unable to map memory region: %pa+%zx\n",
+				&rsc_pa,  ddata->rsc_len);
+			ddata->rsc_va = NULL;
+			return -ENOMEM;
+		}
 	}
 
-	return stm32_rproc_of_memory_translations(rproc);
+	return 0;
 }
 
 static int stm32_rproc_probe(struct platform_device *pdev)
@@ -699,13 +728,15 @@ static int stm32_rproc_probe(struct platform_device *pdev)
 static int stm32_rproc_remove(struct platform_device *pdev)
 {
 	struct rproc *rproc = platform_get_drvdata(pdev);
-	struct device *dev = &pdev->dev;
+	struct stm32_rproc *ddata = rproc->priv;
 
 	if (atomic_read(&rproc->power) > 0)
-		dev_warn(dev, "Releasing rproc while firmware running!\n");
+		rproc_shutdown(rproc);
 
 	rproc_del(rproc);
 	stm32_rproc_free_mbox(rproc);
+	if (ddata->rsc_va)
+		iounmap(ddata->rsc_va);
 	rproc_free(rproc);
 
 	return 0;
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
index 00878c3..1dbbf16 100644
--- a/drivers/usb/typec/Kconfig
+++ b/drivers/usb/typec/Kconfig
@@ -102,6 +102,15 @@ config TYPEC_TPS6598X
 	  If you choose to build this driver as a dynamically linked module, the
 	  module will be called tps6598x.ko.
 
+config TYPEC_STUSB
+	tristate "STMicroelectronics STUSB Type-C controller driver"
+	depends on I2C
+	select EXTCON
+	help
+	  The STMicroelectronics STUSB Type-C controller driver that works
+	  with Type-C Port Controller Manager to provide USB Type-C
+	  functionalities.
+
 source "drivers/usb/typec/mux/Kconfig"
 
 source "drivers/usb/typec/altmodes/Kconfig"
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 45b0aef..aedb153 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -7,6 +7,7 @@ obj-y				+= fusb302/
 obj-$(CONFIG_TYPEC_WCOVE)	+= typec_wcove.o
 obj-$(CONFIG_TYPEC_UCSI)	+= ucsi/
 obj-$(CONFIG_TYPEC_TPS6598X)	+= tps6598x.o
+obj-$(CONFIG_TYPEC_STUSB) += typec_stusb.o
 obj-$(CONFIG_TYPEC)		+= mux/
 obj-$(CONFIG_TYPEC_TCPCI)	+= tcpci.o
 obj-$(CONFIG_TYPEC_RT1711H)	+= tcpci_rt1711h.o
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index e61dffb..b518360 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -1382,6 +1382,21 @@ void typec_set_pwr_opmode(struct typec_port *port,
 EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
 
 /**
+ * typec_find_power_opmode - Get the typec port power operation mode
+ * @name: port power operation mode string
+ *
+ * This routine is used to find the typec_pwr_opmodes by its string name.
+ *
+ * Returns typec_pwr_opmodes if success, otherwise negative error code.
+ */
+int typec_find_port_power_opmode(const char *name)
+{
+	return match_string(typec_pwr_opmodes,
+			    ARRAY_SIZE(typec_pwr_opmodes), name);
+}
+EXPORT_SYMBOL_GPL(typec_find_port_power_opmode);
+
+/**
  * typec_find_port_power_role - Get the typec port power capability
  * @name: port power capability string
  *
diff --git a/drivers/usb/typec/typec_stusb.c b/drivers/usb/typec/typec_stusb.c
new file mode 100644
index 0000000..a2884fd
--- /dev/null
+++ b/drivers/usb/typec/typec_stusb.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STUSB Type-C controller family driver
+ *
+ * Copyright (C) 2019, STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/typec.h>
+
+#define STUSB_ALERT_STATUS			0x0B /* RC */
+#define STUSB_ALERT_STATUS_MASK_CTRL		0x0C /* RW */
+#define STUSB_CC_CONNECTION_STATUS_TRANS	0x0D /* RC */
+#define STUSB_CC_CONNECTION_STATUS		0x0E /* RO */
+#define STUSB_MONITORING_STATUS_TRANS		0x0F /* RC */
+#define STUSB_MONITORING_STATUS			0x10 /* RO */
+#define STUSB_CC_OPERATION_STATUS		0x11 /* RO */
+#define STUSB_HW_FAULT_STATUS_TRANS		0x12 /* RC */
+#define STUSB_HW_FAULT_STATUS			0x13 /* RO */
+#define STUSB_CC_CAPABILITY_CTRL		0x18 /* RW */
+#define STUSB_CC_VCONN_SWITCH_CTRL		0x1E /* RW */
+#define STUSB_VCONN_MONITORING_CTRL		0x20 /* RW */
+#define STUSB_VBUS_MONITORING_RANGE_CTRL	0x22 /* RW */
+#define STUSB_RESET_CTRL			0x23 /* RW */
+#define STUSB_VBUS_DISCHARGE_TIME_CTRL		0x25 /* RW */
+#define STUSB_VBUS_DISCHARGE_STATUS		0x26 /* RO */
+#define STUSB_VBUS_ENABLE_STATUS		0x27 /* RO */
+#define STUSB_CC_POWER_MODE_CTRL		0x28 /* RW */
+#define STUSB_VBUS_MONITORING_CTRL		0x2E /* RW */
+#define STUSB1600_REG_MAX			0x2F /* RO - Reserved */
+
+/* STUSB_ALERT_STATUS/STUSB_ALERT_STATUS_MASK_CTRL bitfields */
+#define STUSB_HW_FAULT				BIT(4)
+#define STUSB_MONITORING			BIT(5)
+#define STUSB_CC_CONNECTION			BIT(6)
+#define STUSB_ALL_ALERTS			GENMASK(6, 4)
+
+/* STUSB_CC_CONNECTION_STATUS_TRANS bitfields */
+#define STUSB_CC_ATTACH_TRANS			BIT(0)
+
+/* STUSB_CC_CONNECTION_STATUS bitfields */
+#define STUSB_CC_ATTACH				BIT(0)
+#define STUSB_CC_VCONN_SUPPLY			BIT(1)
+#define STUSB_CC_DATA_ROLE(s)			(!!((s) & BIT(2)))
+#define STUSB_CC_POWER_ROLE(s)			(!!((s) & BIT(3)))
+#define STUSB_CC_ATTACHED_MODE			GENMASK(7, 5)
+
+/* STUSB_MONITORING_STATUS_TRANS bitfields */
+#define STUSB_VCONN_PRESENCE_TRANS		BIT(0)
+#define STUSB_VBUS_PRESENCE_TRANS		BIT(1)
+#define STUSB_VBUS_VSAFE0V_TRANS		BIT(2)
+#define STUSB_VBUS_VALID_TRANS			BIT(3)
+
+/* STUSB_MONITORING_STATUS bitfields */
+#define STUSB_VCONN_PRESENCE			BIT(0)
+#define STUSB_VBUS_PRESENCE			BIT(1)
+#define STUSB_VBUS_VSAFE0V			BIT(2)
+#define STUSB_VBUS_VALID			BIT(3)
+
+/* STUSB_CC_OPERATION_STATUS bitfields */
+#define STUSB_TYPEC_FSM_STATE			GENMASK(4, 0)
+#define STUSB_SINK_POWER_STATE			GENMASK(6, 5)
+#define STUSB_CC_ATTACHED			BIT(7)
+
+/* STUSB_HW_FAULT_STATUS_TRANS bitfields */
+#define STUSB_VCONN_SW_OVP_FAULT_TRANS		BIT(0)
+#define STUSB_VCONN_SW_OCP_FAULT_TRANS		BIT(1)
+#define STUSB_VCONN_SW_RVP_FAULT_TRANS		BIT(2)
+#define STUSB_VPU_VALID_TRANS			BIT(4)
+#define STUSB_VPU_OVP_FAULT_TRANS		BIT(5)
+#define STUSB_THERMAL_FAULT			BIT(7)
+
+/* STUSB_HW_FAULT_STATUS bitfields */
+#define STUSB_VCONN_SW_OVP_FAULT_CC2		BIT(0)
+#define STUSB_VCONN_SW_OVP_FAULT_CC1		BIT(1)
+#define STUSB_VCONN_SW_OCP_FAULT_CC2		BIT(2)
+#define STUSB_VCONN_SW_OCP_FAULT_CC1		BIT(3)
+#define STUSB_VCONN_SW_RVP_FAULT_CC2		BIT(4)
+#define STUSB_VCONN_SW_RVP_FAULT_CC1		BIT(5)
+#define STUSB_VPU_VALID				BIT(6)
+#define STUSB_VPU_OVP_FAULT			BIT(7)
+
+/* STUSB_CC_CAPABILITY_CTRL bitfields */
+#define STUSB_CC_VCONN_SUPPLY_EN		BIT(0)
+#define STUSB_CC_VCONN_DISCHARGE_EN		BIT(4)
+#define STUSB_CC_CURRENT_ADVERTISED		GENMASK(7, 6)
+
+/* STUSB_VCONN_SWITCH_CTRL bitfields */
+#define STUSB_CC_VCONN_SWITCH_ILIM		GENMASK(3, 0)
+
+/* STUSB_VCONN_MONITORING_CTRL bitfields */
+#define STUSB_VCONN_UVLO_THRESHOLD		BIT(6)
+#define STUSB_VCONN_MONITORING_EN		BIT(7)
+
+/* STUSB_VBUS_MONITORING_RANGE_CTRL bitfields */
+#define STUSB_SHIFT_LOW_VBUS_LIMIT		GENMASK(3, 0)
+#define STUSB_SHIFT_HIGH_VBUS_LIMIT		GENMASK(7, 4)
+
+/* STUSB_RESET_CTRL bitfields */
+#define STUSB_SW_RESET_EN			BIT(0)
+
+/* STUSB_VBUS_DISCHARGE_TIME_CTRL bitfields */
+#define STUSBXX02_VBUS_DISCHARGE_TIME_TO_PDO	GENMASK(3, 0)
+#define STUSB_VBUS_DISCHARGE_TIME_TO_0V		GENMASK(7, 4)
+
+/* STUSB_VBUS_DISCHARGE_STATUS bitfields */
+#define STUSB_VBUS_DISCHARGE_EN			BIT(7)
+
+/* STUSB_VBUS_ENABLE_STATUS bitfields */
+#define STUSB_VBUS_SOURCE_EN			BIT(0)
+#define STUSB_VBUS_SINK_EN			BIT(1)
+
+/* STUSB_CC_POWER_MODE_CTRL bitfields */
+#define STUSB_CC_POWER_MODE			GENMASK(2, 0)
+
+/* STUSB_VBUS_MONITORING_CTRL bitfields */
+#define STUSB_VDD_UVLO_DISABLE			BIT(0)
+#define STUSB_VBUS_VSAFE0V_THRESHOLD		GENMASK(2, 1)
+#define STUSB_VBUS_RANGE_DISABLE		BIT(4)
+#define STUSB_VDD_OVLO_DISABLE			BIT(6)
+
+enum stusb_pwr_mode {
+	SOURCE_WITH_ACCESSORY,
+	SINK_WITH_ACCESSORY,
+	SINK_WITHOUT_ACCESSORY,
+	DUAL_WITH_ACCESSORY,
+	DUAL_WITH_ACCESSORY_AND_TRY_SRC,
+	DUAL_WITH_ACCESSORY_AND_TRY_SNK,
+};
+
+struct stusb {
+	struct device		*dev;
+	struct regmap		*regmap;
+	struct regulator	*vdd_supply;
+	struct regulator	*vsys_supply;
+	struct regulator	*vconn_supply;
+
+	struct typec_port	*port;
+	struct typec_capability capability;
+
+	enum typec_port_type	port_type;
+	enum typec_pwr_opmode	pwr_opmode;
+};
+
+static bool stusb_reg_writeable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STUSB_ALERT_STATUS_MASK_CTRL:
+	case STUSB_CC_CAPABILITY_CTRL:
+	case STUSB_CC_VCONN_SWITCH_CTRL:
+	case STUSB_VCONN_MONITORING_CTRL:
+	case STUSB_VBUS_MONITORING_RANGE_CTRL:
+	case STUSB_RESET_CTRL:
+	case STUSB_VBUS_DISCHARGE_TIME_CTRL:
+	case STUSB_CC_POWER_MODE_CTRL:
+	case STUSB_VBUS_MONITORING_CTRL:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stusb_reg_readable(struct device *dev, unsigned int reg)
+{
+	if ((reg >= 0x00 && reg <= 0x0A) ||
+	    (reg >= 0x14 && reg <= 0x17) ||
+	    (reg >= 0x19 && reg <= 0x1D) ||
+	    (reg >= 0x29 && reg <= 0x2D) ||
+	    (reg == 0x1F || reg == 0x21 || reg == 0x24 || reg == 0x2F))
+		return false;
+	else
+		return true;
+}
+
+static bool stusb_reg_volatile(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STUSB_ALERT_STATUS:
+	case STUSB_CC_CONNECTION_STATUS_TRANS:
+	case STUSB_CC_CONNECTION_STATUS:
+	case STUSB_MONITORING_STATUS_TRANS:
+	case STUSB_MONITORING_STATUS:
+	case STUSB_CC_OPERATION_STATUS:
+	case STUSB_HW_FAULT_STATUS_TRANS:
+	case STUSB_HW_FAULT_STATUS:
+	case STUSB_VBUS_DISCHARGE_STATUS:
+	case STUSB_VBUS_ENABLE_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool stusb_reg_precious(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case STUSB_ALERT_STATUS:
+	case STUSB_CC_CONNECTION_STATUS_TRANS:
+	case STUSB_MONITORING_STATUS_TRANS:
+	case STUSB_HW_FAULT_STATUS_TRANS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config stusb1600_regmap_config = {
+	.reg_bits	= 8,
+	.reg_stride	= 1,
+	.val_bits	= 8,
+	.max_register	= STUSB1600_REG_MAX,
+	.writeable_reg	= stusb_reg_writeable,
+	.readable_reg	= stusb_reg_readable,
+	.volatile_reg	= stusb_reg_volatile,
+	.precious_reg	= stusb_reg_precious,
+	.cache_type	= REGCACHE_RBTREE,
+};
+
+static bool stusb_get_vconn(struct stusb *chip)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val);
+	if (ret) {
+		dev_err(chip->dev, "Unable to get Vconn status: %d\n", ret);
+		return false;
+	}
+
+	return !!FIELD_GET(STUSB_CC_VCONN_SUPPLY_EN, val);;
+}
+
+static int stusb_set_vconn(struct stusb *chip, bool on)
+{
+	int ret;
+
+	/* Manage VCONN input supply */
+	if (chip->vconn_supply) {
+		if (on) {
+			ret = regulator_enable(chip->vconn_supply);
+			if (ret) {
+				dev_err(chip->dev,
+					"failed to enable vconn supply: %d\n",
+					ret);
+				return ret;
+			}
+		} else {
+			regulator_disable(chip->vconn_supply);
+		}
+	}
+
+	/* Manage VCONN monitoring and power path */
+	ret = regmap_update_bits(chip->regmap, STUSB_VCONN_MONITORING_CTRL,
+				 STUSB_VCONN_MONITORING_EN,
+				 on ? STUSB_VCONN_MONITORING_EN : 0);
+	if (ret)
+		goto vconn_reg_disable;
+
+	return 0;
+
+vconn_reg_disable:
+	if (chip->vconn_supply && on)
+		regulator_disable(chip->vconn_supply);
+
+	return ret;
+}
+
+static enum typec_pwr_opmode stusb_get_pwr_opmode(struct stusb *chip)
+{
+	u32 val;
+	int ret;
+
+	ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val);
+	if (ret) {
+		dev_err(chip->dev, "Unable to get pwr opmode: %d\n", ret);
+		return TYPEC_PWR_MODE_USB;
+	}
+
+	return FIELD_GET(STUSB_CC_CURRENT_ADVERTISED, val);
+}
+
+static int stusb_init(struct stusb *chip)
+{
+	int ret;
+
+	/* Change the default Type-C power mode */
+	if (chip->port_type == TYPEC_PORT_SRC)
+		ret = regmap_update_bits(chip->regmap,
+					 STUSB_CC_POWER_MODE_CTRL,
+					 STUSB_CC_POWER_MODE,
+					 SOURCE_WITH_ACCESSORY);
+	else if (chip->port_type == TYPEC_PORT_SNK)
+		ret = regmap_update_bits(chip->regmap,
+					 STUSB_CC_POWER_MODE_CTRL,
+					 STUSB_CC_POWER_MODE,
+					 SINK_WITH_ACCESSORY);
+	else /* (capability->type == TYPEC_PORT_DRP) */
+		ret = regmap_update_bits(chip->regmap,
+					 STUSB_CC_POWER_MODE_CTRL,
+					 STUSB_CC_POWER_MODE,
+					 DUAL_WITH_ACCESSORY);
+	if (ret)
+		return ret;
+
+	if (chip->port_type == TYPEC_PORT_SNK)
+		return 0;
+
+	/* Change the default Type-C Source power operation mode capability */
+	ret = regmap_update_bits(chip->regmap, STUSB_CC_CAPABILITY_CTRL,
+				 STUSB_CC_CURRENT_ADVERTISED,
+				 FIELD_PREP(STUSB_CC_CURRENT_ADVERTISED,
+					    chip->pwr_opmode));
+	if (ret)
+		return ret;
+
+	/* Manage Type-C Source Vconn supply */
+	if (stusb_get_vconn(chip)) {
+		ret = stusb_set_vconn(chip, true);
+		if (ret)
+			return ret;
+	}
+
+	/* Mask all events interrupts - to be unmasked with interrupt support */
+	ret = regmap_update_bits(chip->regmap, STUSB_ALERT_STATUS_MASK_CTRL,
+				 STUSB_ALL_ALERTS, STUSB_ALL_ALERTS);
+
+	return ret;
+}
+
+static int stusb_fw_get_caps(struct stusb *chip)
+{
+	struct fwnode_handle *fwnode = device_get_named_child_node(chip->dev,
+								   "connector");
+	const char *cap_str;
+	int ret;
+
+	if (!fwnode)
+		return -EINVAL;
+
+	chip->capability.fwnode = fwnode;
+
+	ret = fwnode_property_read_string(fwnode, "power-role", &cap_str);
+	if (!ret) {
+		chip->port_type = typec_find_port_power_role(cap_str);
+		if (chip->port_type < 0)
+			return -EINVAL;
+
+		chip->capability.type = chip->port_type;
+	}
+
+	if (chip->port_type == TYPEC_PORT_SNK)
+		goto sink;
+
+	if (chip->port_type == TYPEC_PORT_DRP)
+		chip->capability.prefer_role = TYPEC_SINK;
+
+	ret = fwnode_property_read_string(fwnode, "power-opmode", &cap_str);
+	if (!ret) {
+		chip->pwr_opmode = typec_find_port_power_opmode(cap_str);
+
+		/* Power delivery not yet supported */
+		if (chip->pwr_opmode < 0 ||
+		    chip->pwr_opmode == TYPEC_PWR_MODE_PD) {
+			dev_err(chip->dev, "bad power operation mode: %d\n",
+				chip->pwr_opmode);
+			return -EINVAL;
+		}
+
+	} else {
+		chip->pwr_opmode = stusb_get_pwr_opmode(chip);
+	}
+
+sink:
+	return 0;
+}
+
+static int stusb_get_caps(struct stusb *chip, bool *try)
+{
+	enum typec_port_type *type = &chip->capability.type;
+	enum typec_port_data *data = &chip->capability.data;
+	enum typec_accessory *accessory = chip->capability.accessory;
+	u32 val;
+	int ret;
+
+	chip->capability.revision = USB_TYPEC_REV_1_2;
+
+	ret = regmap_read(chip->regmap, STUSB_CC_POWER_MODE_CTRL, &val);
+	if (ret)
+		return ret;
+
+	*try = false;
+
+	switch (FIELD_GET(STUSB_CC_POWER_MODE, val)) {
+	case SOURCE_WITH_ACCESSORY:
+		*type = TYPEC_PORT_SRC;
+		*data = TYPEC_PORT_DFP;
+		*accessory++ = TYPEC_ACCESSORY_AUDIO;
+		*accessory++ = TYPEC_ACCESSORY_DEBUG;
+		break;
+	case SINK_WITH_ACCESSORY:
+		*type = TYPEC_PORT_SNK;
+		*data = TYPEC_PORT_UFP;
+		*accessory++ = TYPEC_ACCESSORY_AUDIO;
+		*accessory++ = TYPEC_ACCESSORY_DEBUG;
+		break;
+	case SINK_WITHOUT_ACCESSORY:
+		*type = TYPEC_PORT_SNK;
+		*data = TYPEC_PORT_UFP;
+		break;
+	case DUAL_WITH_ACCESSORY:
+		*type = TYPEC_PORT_DRP;
+		*data = TYPEC_PORT_DRD;
+		*accessory++ = TYPEC_ACCESSORY_AUDIO;
+		*accessory++ = TYPEC_ACCESSORY_DEBUG;
+		break;
+	case DUAL_WITH_ACCESSORY_AND_TRY_SRC:
+	case DUAL_WITH_ACCESSORY_AND_TRY_SNK:
+		*type = TYPEC_PORT_DRP;
+		*data = TYPEC_PORT_DRD;
+		*accessory++ = TYPEC_ACCESSORY_AUDIO;
+		*accessory++ = TYPEC_ACCESSORY_DEBUG;
+		*try = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	chip->port_type = *type;
+
+	return stusb_fw_get_caps(chip);
+}
+
+static const struct of_device_id stusb_of_match[] = {
+	{ .compatible = "st,stusb1600", .data = &stusb1600_regmap_config},
+	{},
+};
+
+static int stusb_probe(struct i2c_client *client,
+		       const struct i2c_device_id *id)
+{
+	struct stusb *chip;
+	const struct of_device_id *match;
+	struct regmap_config *regmap_config;
+	bool try_role;
+	int ret;
+
+	chip = devm_kzalloc(&client->dev, sizeof(struct stusb), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, chip);
+
+	match = i2c_of_match_device(stusb_of_match, client);
+	regmap_config = (struct regmap_config *)match->data;
+	chip->regmap = devm_regmap_init_i2c(client, regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		ret = PTR_ERR(chip->regmap);
+		dev_err(&client->dev,
+			"Failed to allocate register map:%d\n", ret);
+		return ret;
+	}
+
+	chip->dev = &client->dev;
+
+	chip->vdd_supply = devm_regulator_get_optional(chip->dev, "vdd");
+	if (IS_ERR(chip->vdd_supply)) {
+		ret = PTR_ERR(chip->vdd_supply);
+		if (ret != -ENODEV)
+			return ret;
+		chip->vdd_supply = NULL;
+	} else {
+		ret = regulator_enable(chip->vdd_supply);
+		if (ret) {
+			dev_err(chip->dev,
+				"Failed to enable vdd supply: %d\n", ret);
+			return ret;
+		}
+	}
+
+	chip->vsys_supply = devm_regulator_get_optional(chip->dev, "vsys");
+	if (IS_ERR(chip->vsys_supply)) {
+		ret = PTR_ERR(chip->vsys_supply);
+		if (ret != -ENODEV)
+			goto vdd_reg_disable;
+		chip->vsys_supply = NULL;
+	} else {
+		ret = regulator_enable(chip->vsys_supply);
+		if (ret) {
+			dev_err(chip->dev,
+				"Failed to enable vsys supply: %d\n", ret);
+			goto vdd_reg_disable;
+		}
+	}
+
+	chip->vconn_supply = devm_regulator_get_optional(chip->dev, "vconn");
+	if (IS_ERR(chip->vconn_supply)) {
+		ret = PTR_ERR(chip->vconn_supply);
+		if (ret != -ENODEV)
+			goto vsys_reg_disable;
+		chip->vconn_supply = NULL;
+	}
+
+	ret = stusb_get_caps(chip, &try_role);
+	if (ret) {
+		dev_err(chip->dev, "failed to get port caps: %d\n", ret);
+		goto vsys_reg_disable;
+	}
+
+	ret = stusb_init(chip);
+	if (ret) {
+		dev_err(chip->dev, "failed to init port: %d\n", ret);
+		goto vsys_reg_disable;
+	}
+
+	chip->port = typec_register_port(chip->dev, &chip->capability);
+	if (!chip->port) {
+		ret = -ENODEV;
+		goto all_reg_disable;
+	}
+
+	/* To be moved in attach/detach procedure with interrupt support */
+	typec_set_pwr_opmode(chip->port, chip->pwr_opmode);
+
+	dev_info(chip->dev, "STUSB driver registered\n");
+
+	return 0;
+
+all_reg_disable:
+	if (stusb_get_vconn(chip))
+		stusb_set_vconn(chip, false);
+vsys_reg_disable:
+	if (chip->vsys_supply)
+		regulator_disable(chip->vsys_supply);
+vdd_reg_disable:
+	if (chip->vdd_supply)
+		regulator_disable(chip->vdd_supply);
+
+	return ret;
+}
+
+static int stusb_remove(struct i2c_client *client)
+{
+	struct stusb *chip = i2c_get_clientdata(client);
+
+	typec_unregister_port(chip->port);
+
+  	if (stusb_get_vconn(chip))
+		stusb_set_vconn(chip, false);
+
+	if (chip->vdd_supply)
+		regulator_disable(chip->vdd_supply);
+
+	if (chip->vsys_supply)
+		regulator_disable(chip->vsys_supply);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stusb_resume(struct device *dev)
+{
+	struct stusb *chip = dev_get_drvdata(dev);
+
+	return stusb_init(chip);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stusb_pm_ops, NULL, stusb_resume);
+
+static struct i2c_driver stusb_driver = {
+	.driver = {
+		.name = "typec_stusb",
+		.pm = &stusb_pm_ops,
+		.of_match_table = stusb_of_match,
+	},
+	.probe = stusb_probe,
+	.remove = stusb_remove,
+};
+module_i2c_driver(stusb_driver);
+
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STUSB Type-C controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 7df4eca..2671776 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -241,6 +241,7 @@ int typec_set_orientation(struct typec_port *port,
 enum typec_orientation typec_get_orientation(struct typec_port *port);
 int typec_set_mode(struct typec_port *port, int mode);
 
+int typec_find_port_power_opmode(const char *name);
 int typec_find_port_power_role(const char *name);
 int typec_find_power_role(const char *name);
 int typec_find_port_data_role(const char *name);
diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c
index 71d341b..24948b9 100644
--- a/sound/soc/stm/stm32_adfsdm.c
+++ b/sound/soc/stm/stm32_adfsdm.c
@@ -304,6 +304,7 @@ MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match);
 static int stm32_adfsdm_probe(struct platform_device *pdev)
 {
 	struct stm32_adfsdm_priv *priv;
+	struct snd_soc_component *component;
 	int ret;
 
 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@@ -331,9 +332,15 @@ static int stm32_adfsdm_probe(struct platform_device *pdev)
 	if (IS_ERR(priv->iio_cb))
 		return PTR_ERR(priv->iio_cb);
 
-	ret = devm_snd_soc_register_component(&pdev->dev,
-					      &stm32_adfsdm_soc_platform,
-					      NULL, 0);
+	component = devm_kzalloc(&pdev->dev, sizeof(*component), GFP_KERNEL);
+	if (!component)
+		return -ENOMEM;
+#ifdef CONFIG_DEBUG_FS
+	component->debugfs_prefix = "pcm";
+#endif
+
+	ret = snd_soc_add_component(&pdev->dev, component,
+				    &stm32_adfsdm_soc_platform, NULL, 0);
 	if (ret < 0)
 		dev_err(&pdev->dev, "%s: Failed to register PCM platform\n",
 			__func__);
@@ -341,12 +348,20 @@ static int stm32_adfsdm_probe(struct platform_device *pdev)
 	return ret;
 }
 
+static int stm32_adfsdm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+
+	return 0;
+}
+
 static struct platform_driver stm32_adfsdm_driver = {
 	.driver = {
 		   .name = STM32_ADFSDM_DRV_NAME,
 		   .of_match_table = stm32_adfsdm_of_match,
 		   },
 	.probe = stm32_adfsdm_probe,
+	.remove = stm32_adfsdm_remove,
 };
 
 module_platform_driver(stm32_adfsdm_driver);
-- 
2.7.4

