Revision d26d45253b78ec0a67003eea25bd08fceaa5b78c authored by Linus Torvalds on 22 January 2013, 18:30:49 UTC, committed by Linus Torvalds on 22 January 2013, 18:30:49 UTC
Pull ftrace fix from Steven Rostedt:
 "Kprobes now uses the function tracer if it can.  That is, if a probe
  is placed on a function mcount/nop location, and the arch supports it,
  instead of adding a breakpoint, kprobes will register a function
  callback as that is much more efficient.

  The function tracer requires to update modules before they run, and
  uses the module notifier to do so.  But if something else in the
  module notifiers registers a kprobe at one of these locations, before
  ftrace can get to it, then the system could fail.

  The function tracer must be initialized early, otherwise module
  notifiers that probe will only work by chance."

* tag 'trace-3.8-rc4-fix' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  ftrace: Be first to run code modification on modules
2 parent s 0944c0a + c1bf08a
Raw File
gus_dma.c
/*
 *  Routines for GF1 DMA control
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <asm/dma.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/gus.h>

static void snd_gf1_dma_ack(struct snd_gus_card * gus)
{
	unsigned long flags;

	spin_lock_irqsave(&gus->reg_lock, flags);
	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
	snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
	spin_unlock_irqrestore(&gus->reg_lock, flags);
}

static void snd_gf1_dma_program(struct snd_gus_card * gus,
				unsigned int addr,
				unsigned long buf_addr,
				unsigned int count,
				unsigned int cmd)
{
	unsigned long flags;
	unsigned int address;
	unsigned char dma_cmd;
	unsigned int address_high;

	snd_printdd("dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
		    addr, buf_addr, count);

	if (gus->gf1.dma1 > 3) {
		if (gus->gf1.enh_mode) {
			address = addr >> 1;
		} else {
			if (addr & 0x1f) {
				snd_printd("snd_gf1_dma_transfer: unaligned address (0x%x)?\n", addr);
				return;
			}
			address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
		}
	} else {
		address = addr;
	}

	dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;
#if 0
	dma_cmd |= 0x08;
#endif
	if (dma_cmd & SNDRV_GF1_DMA_16BIT) {
		count++;
		count &= ~1;	/* align */
	}
	if (gus->gf1.dma1 > 3) {
		dma_cmd |= SNDRV_GF1_DMA_WIDTH16;
		count++;
		count &= ~1;	/* align */
	}
	snd_gf1_dma_ack(gus);
	snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
#if 0
	snd_printk(KERN_DEBUG "address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
		   address << 1, count, dma_cmd);
#endif
	spin_lock_irqsave(&gus->reg_lock, flags);
	if (gus->gf1.enh_mode) {
		address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
		snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);
	} else
		snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
	snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
	spin_unlock_irqrestore(&gus->reg_lock, flags);
}

static struct snd_gf1_dma_block *snd_gf1_dma_next_block(struct snd_gus_card * gus)
{
	struct snd_gf1_dma_block *block;

	/* PCM block have bigger priority than synthesizer one */
	if (gus->gf1.dma_data_pcm) {
		block = gus->gf1.dma_data_pcm;
		if (gus->gf1.dma_data_pcm_last == block) {
			gus->gf1.dma_data_pcm =
			gus->gf1.dma_data_pcm_last = NULL;
		} else {
			gus->gf1.dma_data_pcm = block->next;
		}
	} else if (gus->gf1.dma_data_synth) {
		block = gus->gf1.dma_data_synth;
		if (gus->gf1.dma_data_synth_last == block) {
			gus->gf1.dma_data_synth =
			gus->gf1.dma_data_synth_last = NULL;
		} else {
			gus->gf1.dma_data_synth = block->next;
		}
	} else {
		block = NULL;
	}
	if (block) {
		gus->gf1.dma_ack = block->ack;
		gus->gf1.dma_private_data = block->private_data;
	}
	return block;
}


static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
{
	struct snd_gf1_dma_block *block;

	snd_gf1_dma_ack(gus);
	if (gus->gf1.dma_ack)
		gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
	spin_lock(&gus->dma_lock);
	if (gus->gf1.dma_data_pcm == NULL &&
	    gus->gf1.dma_data_synth == NULL) {
	    	gus->gf1.dma_ack = NULL;
		gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
		spin_unlock(&gus->dma_lock);
		return;
	}
	block = snd_gf1_dma_next_block(gus);
	spin_unlock(&gus->dma_lock);
	snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
	kfree(block);
#if 0
	snd_printd(KERN_DEBUG "program dma (IRQ) - "
		   "addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
		   block->addr, block->buf_addr, block->count, block->cmd);
#endif
}

int snd_gf1_dma_init(struct snd_gus_card * gus)
{
	mutex_lock(&gus->dma_mutex);
	gus->gf1.dma_shared++;
	if (gus->gf1.dma_shared > 1) {
		mutex_unlock(&gus->dma_mutex);
		return 0;
	}
	gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
	gus->gf1.dma_data_pcm = 
	gus->gf1.dma_data_pcm_last =
	gus->gf1.dma_data_synth = 
	gus->gf1.dma_data_synth_last = NULL;
	mutex_unlock(&gus->dma_mutex);
	return 0;
}

int snd_gf1_dma_done(struct snd_gus_card * gus)
{
	struct snd_gf1_dma_block *block;

	mutex_lock(&gus->dma_mutex);
	gus->gf1.dma_shared--;
	if (!gus->gf1.dma_shared) {
		snd_dma_disable(gus->gf1.dma1);
		snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);
		snd_gf1_dma_ack(gus);
		while ((block = gus->gf1.dma_data_pcm)) {
			gus->gf1.dma_data_pcm = block->next;
			kfree(block);
		}
		while ((block = gus->gf1.dma_data_synth)) {
			gus->gf1.dma_data_synth = block->next;
			kfree(block);
		}
		gus->gf1.dma_data_pcm_last =
		gus->gf1.dma_data_synth_last = NULL;
	}
	mutex_unlock(&gus->dma_mutex);
	return 0;
}

int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
			       struct snd_gf1_dma_block * __block,
			       int atomic,
			       int synth)
{
	unsigned long flags;
	struct snd_gf1_dma_block *block;

	block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
	if (block == NULL) {
		snd_printk(KERN_ERR "gf1: DMA transfer failure; not enough memory\n");
		return -ENOMEM;
	}
	*block = *__block;
	block->next = NULL;

	snd_printdd("addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
		    block->addr, (long) block->buffer, block->count,
		    block->cmd);

	snd_printdd("gus->gf1.dma_data_pcm_last = 0x%lx\n",
		    (long)gus->gf1.dma_data_pcm_last);
	snd_printdd("gus->gf1.dma_data_pcm = 0x%lx\n",
		    (long)gus->gf1.dma_data_pcm);

	spin_lock_irqsave(&gus->dma_lock, flags);
	if (synth) {
		if (gus->gf1.dma_data_synth_last) {
			gus->gf1.dma_data_synth_last->next = block;
			gus->gf1.dma_data_synth_last = block;
		} else {
			gus->gf1.dma_data_synth = 
			gus->gf1.dma_data_synth_last = block;
		}
	} else {
		if (gus->gf1.dma_data_pcm_last) {
			gus->gf1.dma_data_pcm_last->next = block;
			gus->gf1.dma_data_pcm_last = block;
		} else {
			gus->gf1.dma_data_pcm = 
			gus->gf1.dma_data_pcm_last = block;
		}
	}
	if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
		gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
		block = snd_gf1_dma_next_block(gus);
		spin_unlock_irqrestore(&gus->dma_lock, flags);
		if (block == NULL)
			return 0;
		snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
		kfree(block);
		return 0;
	}
	spin_unlock_irqrestore(&gus->dma_lock, flags);
	return 0;
}
back to top