ALSA: hda - Fix non-snoop page handling
For non-snoop mode, we fiddle with the page attributes of CORB/RIRB and the position buffer, but also the ring buffers. The problem is that the current code blindly assumes that the buffer is contiguous. However, the ring buffers may be SG-buffers, thus a wrong vmapped address is passed there, leading to Oops. This patch fixes the handling for SG-buffers. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=800701 Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Родитель
f748abcc5b
Коммит
9ddf1aeb21
|
@ -656,29 +656,43 @@ static char *driver_short_names[] = {
|
|||
#define get_azx_dev(substream) (substream->runtime->private_data)
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
static void __mark_pages_wc(struct azx *chip, void *addr, size_t size, bool on)
|
||||
static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on)
|
||||
{
|
||||
int pages;
|
||||
|
||||
if (azx_snoop(chip))
|
||||
return;
|
||||
if (addr && size) {
|
||||
int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
if (!dmab || !dmab->area || !dmab->bytes)
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) {
|
||||
struct snd_sg_buf *sgbuf = dmab->private_data;
|
||||
if (on)
|
||||
set_memory_wc((unsigned long)addr, pages);
|
||||
set_pages_array_wc(sgbuf->page_table, sgbuf->pages);
|
||||
else
|
||||
set_memory_wb((unsigned long)addr, pages);
|
||||
set_pages_array_wb(sgbuf->page_table, sgbuf->pages);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
if (on)
|
||||
set_memory_wc((unsigned long)dmab->area, pages);
|
||||
else
|
||||
set_memory_wb((unsigned long)dmab->area, pages);
|
||||
}
|
||||
|
||||
static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
|
||||
bool on)
|
||||
{
|
||||
__mark_pages_wc(chip, buf->area, buf->bytes, on);
|
||||
__mark_pages_wc(chip, buf, on);
|
||||
}
|
||||
static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
|
||||
struct snd_pcm_runtime *runtime, bool on)
|
||||
struct snd_pcm_substream *substream, bool on)
|
||||
{
|
||||
if (azx_dev->wc_marked != on) {
|
||||
__mark_pages_wc(chip, runtime->dma_area, runtime->dma_bytes, on);
|
||||
__mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on);
|
||||
azx_dev->wc_marked = on;
|
||||
}
|
||||
}
|
||||
|
@ -689,7 +703,7 @@ static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf,
|
|||
{
|
||||
}
|
||||
static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
|
||||
struct snd_pcm_runtime *runtime, bool on)
|
||||
struct snd_pcm_substream *substream, bool on)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
@ -1968,11 +1982,10 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
|
||||
struct azx *chip = apcm->chip;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct azx_dev *azx_dev = get_azx_dev(substream);
|
||||
int ret;
|
||||
|
||||
mark_runtime_wc(chip, azx_dev, runtime, false);
|
||||
mark_runtime_wc(chip, azx_dev, substream, false);
|
||||
azx_dev->bufsize = 0;
|
||||
azx_dev->period_bytes = 0;
|
||||
azx_dev->format_val = 0;
|
||||
|
@ -1980,7 +1993,7 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
params_buffer_bytes(hw_params));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mark_runtime_wc(chip, azx_dev, runtime, true);
|
||||
mark_runtime_wc(chip, azx_dev, substream, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1989,7 +2002,6 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
|
|||
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
|
||||
struct azx_dev *azx_dev = get_azx_dev(substream);
|
||||
struct azx *chip = apcm->chip;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
|
||||
|
||||
/* reset BDL address */
|
||||
|
@ -2002,7 +2014,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
|
|||
|
||||
snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
|
||||
|
||||
mark_runtime_wc(chip, azx_dev, runtime, false);
|
||||
mark_runtime_wc(chip, azx_dev, substream, false);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче