From: Takashi Iwai <tiwai@suse.de>
Subject: Fix kABI breakage by HD-audio bus caps extensions
Patch-mainline: Never, SLE12-SP3 / openSUSE-42.3 only
References: bsc#1048356

The addition of new fields to struct hdac_bus breaks kABI.
Luckily we have the reserved pointer, so allocate a new reocrd and
point to it.

Also, the change of snd_hdac_bus_handle_stream_irq() return type
breaks kABI as well.  Re-define the new function and use it instead.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

---
 include/sound/hdaudio.h        |   23 ++++++++++++++++-------
 sound/hda/hdac_bus.c           |    6 ++++++
 sound/hda/hdac_controller.c    |   25 ++++++++++++++++++-------
 sound/pci/hda/hda_controller.c |    2 +-
 sound/pci/hda/hda_controller.h |    2 ++
 sound/pci/hda/hda_intel.c      |   16 ++++++++--------
 6 files changed, 51 insertions(+), 23 deletions(-)

--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -254,6 +254,14 @@ struct hdac_rb {
  * @gtscap: gts capabilities pointer
  * @drsmcap: dma resume capabilities pointer
  */
+struct hdac_bus_caps {
+	void __iomem *ppcap;
+	void __iomem *spbcap;
+	void __iomem *mlcap;
+	void __iomem *gtscap;
+	void __iomem *drsmcap;
+};
+
 struct hdac_bus {
 	struct device *dev;
 	const struct hdac_bus_ops *ops;
@@ -264,12 +272,6 @@ struct hdac_bus {
 	void __iomem *remap_addr;
 	int irq;
 
-	void __iomem *ppcap;
-	void __iomem *spbcap;
-	void __iomem *mlcap;
-	void __iomem *gtscap;
-	void __iomem *drsmcap;
-
 	/* codec linked list */
 	struct list_head codec_list;
 	unsigned int num_codecs;
@@ -321,7 +323,11 @@ struct hdac_bus {
 	struct i915_audio_component *audio_component;
 	int i915_power_refcount;
 
+#ifdef __GENKSYMS__
 	void *reserved;		/* kABI placeholder */
+#else
+	struct hdac_bus_caps *caps;
+#endif
 };
 
 int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
@@ -362,7 +368,10 @@ void snd_hdac_bus_enter_link_reset(struc
 void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus);
 
 void snd_hdac_bus_update_rirb(struct hdac_bus *bus);
-int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+				    void (*ack)(struct hdac_bus *,
+						struct hdac_stream *));
+int _snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
 				    void (*ack)(struct hdac_bus *,
 						struct hdac_stream *));
 
--- a/sound/hda/hdac_bus.c
+++ b/sound/hda/hdac_bus.c
@@ -6,6 +6,7 @@
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/export.h>
+#include <linux/slab.h>
 #include <sound/hdaudio.h>
 #include "trace.h"
 
@@ -41,6 +42,9 @@ int snd_hdac_bus_init(struct hdac_bus *b
 	spin_lock_init(&bus->reg_lock);
 	mutex_init(&bus->cmd_mutex);
 	bus->irq = -1;
+	bus->caps = kzalloc(sizeof(*bus->caps), GFP_KERNEL);
+	if (!bus->caps)
+		return -ENOMEM;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_bus_init);
@@ -54,6 +58,8 @@ void snd_hdac_bus_exit(struct hdac_bus *
 	WARN_ON(!list_empty(&bus->stream_list));
 	WARN_ON(!list_empty(&bus->codec_list));
 	cancel_work_sync(&bus->unsol_work);
+	kfree(bus->caps);
+	bus->caps = NULL;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_bus_exit);
 
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -272,6 +272,8 @@ int snd_hdac_bus_parse_capabilities(stru
 	unsigned int offset;
 	unsigned int counter = 0;
 
+	if (WARN_ON(!bus->caps))
+		return -EINVAL;
 	offset = snd_hdac_chip_readl(bus, LLCH);
 
 	/* Lets walk the linked capabilities list */
@@ -287,30 +289,30 @@ int snd_hdac_bus_parse_capabilities(stru
 		switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
 		case AZX_ML_CAP_ID:
 			dev_dbg(bus->dev, "Found ML capability\n");
-			bus->mlcap = bus->remap_addr + offset;
+			bus->caps->mlcap = bus->remap_addr + offset;
 			break;
 
 		case AZX_GTS_CAP_ID:
 			dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
-			bus->gtscap = bus->remap_addr + offset;
+			bus->caps->gtscap = bus->remap_addr + offset;
 			break;
 
 		case AZX_PP_CAP_ID:
 			/* PP capability found, the Audio DSP is present */
 			dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
-			bus->ppcap = bus->remap_addr + offset;
+			bus->caps->ppcap = bus->remap_addr + offset;
 			break;
 
 		case AZX_SPB_CAP_ID:
 			/* SPIB capability found, handler function */
 			dev_dbg(bus->dev, "Found SPB capability\n");
-			bus->spbcap = bus->remap_addr + offset;
+			bus->caps->spbcap = bus->remap_addr + offset;
 			break;
 
 		case AZX_DRSM_CAP_ID:
 			/* DMA resume  capability found, handler function */
 			dev_dbg(bus->dev, "Found DRSM capability\n");
-			bus->drsmcap = bus->remap_addr + offset;
+			bus->caps->drsmcap = bus->remap_addr + offset;
 			break;
 
 		default:
@@ -525,7 +527,16 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip
  *
  * Returns the bits of handled streams, or zero if no stream is handled.
  */
-int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
+				    void (*ack)(struct hdac_bus *,
+						struct hdac_stream *))
+{
+	_snd_hdac_bus_handle_stream_irq(bus, status, ack);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);
+
+/* XXX re-defined for kABI compatibility on SLE12-SP3 */
+int _snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
 				    void (*ack)(struct hdac_bus *,
 						struct hdac_stream *))
 {
@@ -547,7 +558,7 @@ int snd_hdac_bus_handle_stream_irq(struc
 	}
 	return handled;
 }
-EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);
+EXPORT_SYMBOL_GPL(_snd_hdac_bus_handle_stream_irq);
 
 /**
  * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -155,8 +155,10 @@ struct azx {
 	unsigned int region_requested:1;
 	unsigned int disabled:1; /* disabled by vga_switcheroo */
 
+#ifndef __GENKSYMS__
 	/* GTS present */
 	unsigned int gts_present:1;
+#endif
 
 #ifdef CONFIG_SND_HDA_DSP_LOADER
 	struct azx_dev saved_azx_dev;
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -549,7 +549,7 @@ static int intel_get_lctl_scf(struct azx
 	u32 val, t;
 	int i;
 
-	val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP);
+	val = readl(bus->caps->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP);
 
 	for (i = 0; i < ARRAY_SIZE(preferred_bits); i++) {
 		t = preferred_bits[i];
@@ -571,14 +571,14 @@ static int intel_ml_lctl_set_power(struc
 	 * the codecs are sharing the first link setting by default
 	 * If other links are enabled for stream, they need similar fix
 	 */
-	val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+	val = readl(bus->caps->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
 	val &= ~AZX_MLCTL_SPA;
 	val |= state << AZX_MLCTL_SPA_SHIFT;
-	writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+	writel(val, bus->caps->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
 	/* wait for CPA */
 	timeout = 50;
 	while (timeout) {
-		if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) &
+		if (((readl(bus->caps->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) &
 		    AZX_MLCTL_CPA) == (state << AZX_MLCTL_CPA_SHIFT))
 			return 0;
 		timeout--;
@@ -595,7 +595,7 @@ static void intel_init_lctl(struct azx *
 	int ret;
 
 	/* 0. check lctl register value is correct or not */
-	val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+	val = readl(bus->caps->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
 	/* if SCF is already set, let's use it */
 	if ((val & ML_LCTL_SCF_MASK) != 0)
 		return;
@@ -617,7 +617,7 @@ static void intel_init_lctl(struct azx *
 	/* 2. update SCF to select a properly audio clock*/
 	val &= ~ML_LCTL_SCF_MASK;
 	val |= intel_get_lctl_scf(chip);
-	writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+	writel(val, bus->caps->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
 
 set_spa:
 	/* 4. turn link up: set SPA to 1 and wait CPA to 1 */
@@ -651,7 +651,7 @@ static void hda_intel_init_chip(struct a
 	if (IS_BXT(pci))
 		bxt_reduce_dma_latency(chip);
 
-	if (bus->mlcap != NULL)
+	if (bus->caps && bus->caps->mlcap)
 		intel_init_lctl(chip);
 }
 
@@ -1752,7 +1752,7 @@ static int azx_first_init(struct azx *ch
 	chip->gts_present = false;
 
 #ifdef CONFIG_X86
-	if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART))
+	if (bus->caps && bus->caps->ppcap && boot_cpu_has(X86_FEATURE_ART))
 		chip->gts_present = true;
 #endif
 
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -956,7 +956,7 @@ irqreturn_t azx_interrupt(int irq, void
 
 		handled = true;
 		active = false;
-		if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
+		if (_snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
 			active = true;
 
 		/* clear rirb int */
