From b3667bd7579e6d4dfe709315f13cff9bc9ee9053 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Sun, 10 Feb 2013 11:58:40 +0100
Subject: [PATCH] ALSA: hda - Fix memory leak and error handling in CA0132 DSP loader
Git-commit: b3667bd7579e6d4dfe709315f13cff9bc9ee9053
Patch-mainline: 3.9-rc1
References: bnc#814440,FATE#314291

This patch fixes a few obvious bugs in DSP loader stuff:
- Fix possible memory leaks in the error path
- Avoid double-free calls in dma_reset()
- Properly set/unset WC bits for DMA buffers
- Add missing error status checks

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

---
 sound/pci/hda/hda_intel.c    |   11 ++++++++++-
 sound/pci/hda/patch_ca0132.c |   10 +++++++---
 2 files changed, 17 insertions(+), 4 deletions(-)

--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -2451,8 +2451,9 @@ static int azx_load_dsp_prepare(struct h
 				  snd_dma_pci_data(chip->pci),
 				  byte_size, bufp);
 	if (err < 0)
-		goto error;
+		goto unlock;
 
+	mark_pages_wc(chip, bufp, true);
 	azx_dev = azx_get_dsp_loader_dev(chip);
 	azx_dev->bufsize = byte_size;
 	azx_dev->period_bytes = byte_size;
@@ -2474,6 +2475,9 @@ static int azx_load_dsp_prepare(struct h
 	return azx_dev->stream_tag;
 
  error:
+	mark_pages_wc(chip, bufp, false);
+	snd_dma_free_pages(bufp);
+unlock:
 	snd_hda_unlock_devices(bus);
 	return err;
 }
@@ -2496,6 +2500,9 @@ static void azx_load_dsp_cleanup(struct
 	struct azx *chip = bus->private_data;
 	struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
 
+	if (!dmab->area)
+		return;
+
 	/* reset BDL address */
 	azx_sd_writel(azx_dev, SD_BDLPL, 0);
 	azx_sd_writel(azx_dev, SD_BDLPU, 0);
@@ -2504,7 +2511,9 @@ static void azx_load_dsp_cleanup(struct
 	azx_dev->period_bytes = 0;
 	azx_dev->format_val = 0;
 
+	mark_pages_wc(chip, dmab, false);
 	snd_dma_free_pages(dmab);
+	dmab->area = NULL;
 
 	snd_hda_unlock_devices(bus);
 }
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -2065,7 +2065,7 @@ static int dma_reset(struct dma_engine *
 	struct ca0132_spec *spec = codec->spec;
 	int status;
 
-	if (dma->dmab)
+	if (dma->dmab->area)
 		snd_hda_codec_load_dsp_cleanup(codec, dma->dmab);
 
 	status = snd_hda_codec_load_dsp_prepare(codec,
@@ -2357,10 +2357,14 @@ static int dspxfr_one_seg(struct hda_cod
 						chip_addx_remainder,
 						data_remainder,
 						remainder_words);
+			if (status < 0)
+				return status;
 			remainder_words = 0;
 		}
 		if (hci_write) {
 			status = dspxfr_hci_write(codec, hci_write);
+			if (status < 0)
+				return status;
 			hci_write = NULL;
 		}
 
@@ -2376,7 +2380,7 @@ static int dspxfr_one_seg(struct hda_cod
 
 		snd_printdd(KERN_INFO "+++++ DMA complete");
 		dma_set_state(dma_engine, DMA_STATE_STOP);
-		dma_reset(dma_engine);
+		status = dma_reset(dma_engine);
 
 		if (status < 0)
 			return status;
@@ -2517,7 +2521,7 @@ exit:
 	if (ovly && (dma_chan != INVALID_DMA_CHANNEL))
 		dspio_free_dma_chan(codec, dma_chan);
 
-	if (dma_engine->dmab)
+	if (dma_engine->dmab->area)
 		snd_hda_codec_load_dsp_cleanup(codec, dma_engine->dmab);
 	kfree(dma_engine->dmab);
 	kfree(dma_engine);
