⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pmac.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * PMac DBDMA lowlevel functions * * Copyright (c) by Takashi Iwai <tiwai@suse.de> * code based on dmasound.c. * *   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 <sound/driver.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/dma-mapping.h>#include <sound/core.h>#include "pmac.h"#include <sound/pcm_params.h>#include <asm/pmac_feature.h>#include <asm/pci-bridge.h>/* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */static int awacs_freqs[8] = {	44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350};/* fixed frequency table for tumbler */static int tumbler_freqs[1] = {	44100};/* * allocate DBDMA command arrays */static int snd_pmac_dbdma_alloc(struct snd_pmac *chip, struct pmac_dbdma *rec, int size){	unsigned int rsize = sizeof(struct dbdma_cmd) * (size + 1);	rec->space = dma_alloc_coherent(&chip->pdev->dev, rsize,					&rec->dma_base, GFP_KERNEL);	if (rec->space == NULL)		return -ENOMEM;	rec->size = size;	memset(rec->space, 0, rsize);	rec->cmds = (void __iomem *)DBDMA_ALIGN(rec->space);	rec->addr = rec->dma_base + (unsigned long)((char *)rec->cmds - (char *)rec->space);	return 0;}static void snd_pmac_dbdma_free(struct snd_pmac *chip, struct pmac_dbdma *rec){	if (rec->space) {		unsigned int rsize = sizeof(struct dbdma_cmd) * (rec->size + 1);		dma_free_coherent(&chip->pdev->dev, rsize, rec->space, rec->dma_base);	}}/* * pcm stuff *//* * look up frequency table */unsigned int snd_pmac_rate_index(struct snd_pmac *chip, struct pmac_stream *rec, unsigned int rate){	int i, ok, found;	ok = rec->cur_freqs;	if (rate > chip->freq_table[0])		return 0;	found = 0;	for (i = 0; i < chip->num_freqs; i++, ok >>= 1) {		if (! (ok & 1)) continue;		found = i;		if (rate >= chip->freq_table[i])			break;	}	return found;}/* * check whether another stream is active */static inline int another_stream(int stream){	return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?		SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;}/* * allocate buffers */static int snd_pmac_pcm_hw_params(struct snd_pcm_substream *subs,				  struct snd_pcm_hw_params *hw_params){	return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params));}/* * release buffers */static int snd_pmac_pcm_hw_free(struct snd_pcm_substream *subs){	snd_pcm_lib_free_pages(subs);	return 0;}/* * get a stream of the opposite direction */static struct pmac_stream *snd_pmac_get_stream(struct snd_pmac *chip, int stream){	switch (stream) {	case SNDRV_PCM_STREAM_PLAYBACK:		return &chip->playback;	case SNDRV_PCM_STREAM_CAPTURE:		return &chip->capture;	default:		snd_BUG();		return NULL;	}}/* * wait while run status is on */static inline voidsnd_pmac_wait_ack(struct pmac_stream *rec){	int timeout = 50000;	while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0)		udelay(1);}/* * set the format and rate to the chip. * call the lowlevel function if defined (e.g. for AWACS). */static void snd_pmac_pcm_set_format(struct snd_pmac *chip){	/* set up frequency and format */	out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8));	out_le32(&chip->awacs->byteswap, chip->format == SNDRV_PCM_FORMAT_S16_LE ? 1 : 0);	if (chip->set_format)		chip->set_format(chip);}/* * stop the DMA transfer */static inline void snd_pmac_dma_stop(struct pmac_stream *rec){	out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);	snd_pmac_wait_ack(rec);}/* * set the command pointer address */static inline void snd_pmac_dma_set_command(struct pmac_stream *rec, struct pmac_dbdma *cmd){	out_le32(&rec->dma->cmdptr, cmd->addr);}/* * start the DMA */static inline void snd_pmac_dma_run(struct pmac_stream *rec, int status){	out_le32(&rec->dma->control, status | (status << 16));}/* * prepare playback/capture stream */static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs){	int i;	volatile struct dbdma_cmd __iomem *cp;	struct snd_pcm_runtime *runtime = subs->runtime;	int rate_index;	long offset;	struct pmac_stream *astr;		rec->dma_size = snd_pcm_lib_buffer_bytes(subs);	rec->period_size = snd_pcm_lib_period_bytes(subs);	rec->nperiods = rec->dma_size / rec->period_size;	rec->cur_period = 0;	rate_index = snd_pmac_rate_index(chip, rec, runtime->rate);	/* set up constraints */	astr = snd_pmac_get_stream(chip, another_stream(rec->stream));	if (! astr)		return -EINVAL;	astr->cur_freqs = 1 << rate_index;	astr->cur_formats = 1 << runtime->format;	chip->rate_index = rate_index;	chip->format = runtime->format;	/* We really want to execute a DMA stop command, after the AWACS	 * is initialized.	 * For reasons I don't understand, it stops the hissing noise	 * common to many PowerBook G3 systems and random noise otherwise	 * captured on iBook2's about every third time. -ReneR	 */	spin_lock_irq(&chip->reg_lock);	snd_pmac_dma_stop(rec);	st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP);	snd_pmac_dma_set_command(rec, &chip->extra_dma);	snd_pmac_dma_run(rec, RUN);	spin_unlock_irq(&chip->reg_lock);	mdelay(5);	spin_lock_irq(&chip->reg_lock);	/* continuous DMA memory type doesn't provide the physical address,	 * so we need to resolve the address here...	 */	offset = runtime->dma_addr;	for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) {		st_le32(&cp->phy_addr, offset);		st_le16(&cp->req_count, rec->period_size);		/*st_le16(&cp->res_count, 0);*/		st_le16(&cp->xfer_status, 0);		offset += rec->period_size;	}	/* make loop */	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);	st_le32(&cp->cmd_dep, rec->cmd.addr);	snd_pmac_dma_stop(rec);	snd_pmac_dma_set_command(rec, &rec->cmd);	spin_unlock_irq(&chip->reg_lock);	return 0;}/* * PCM trigger/stop */static int snd_pmac_pcm_trigger(struct snd_pmac *chip, struct pmac_stream *rec,				struct snd_pcm_substream *subs, int cmd){	volatile struct dbdma_cmd __iomem *cp;	int i, command;	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_RESUME:		if (rec->running)			return -EBUSY;		command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ?			   OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS;		spin_lock(&chip->reg_lock);		snd_pmac_beep_stop(chip);		snd_pmac_pcm_set_format(chip);		for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)			out_le16(&cp->command, command);		snd_pmac_dma_set_command(rec, &rec->cmd);		(void)in_le32(&rec->dma->status);		snd_pmac_dma_run(rec, RUN|WAKE);		rec->running = 1;		spin_unlock(&chip->reg_lock);		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_SUSPEND:		spin_lock(&chip->reg_lock);		rec->running = 0;		/*printk("stopped!!\n");*/		snd_pmac_dma_stop(rec);		for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++)			out_le16(&cp->command, DBDMA_STOP);		spin_unlock(&chip->reg_lock);		break;	default:		return -EINVAL;	}	return 0;}/* * return the current pointer */inlinestatic snd_pcm_uframes_t snd_pmac_pcm_pointer(struct snd_pmac *chip,					      struct pmac_stream *rec,					      struct snd_pcm_substream *subs){	int count = 0;#if 1 /* hmm.. how can we get the current dma pointer?? */	int stat;	volatile struct dbdma_cmd __iomem *cp = &rec->cmd.cmds[rec->cur_period];	stat = ld_le16(&cp->xfer_status);	if (stat & (ACTIVE|DEAD)) {		count = in_le16(&cp->res_count);		if (count)			count = rec->period_size - count;	}#endif	count += rec->cur_period * rec->period_size;	/*printk("pointer=%d\n", count);*/	return bytes_to_frames(subs->runtime, count);}/* * playback */static int snd_pmac_playback_prepare(struct snd_pcm_substream *subs){	struct snd_pmac *chip = snd_pcm_substream_chip(subs);	return snd_pmac_pcm_prepare(chip, &chip->playback, subs);}static int snd_pmac_playback_trigger(struct snd_pcm_substream *subs,				     int cmd){	struct snd_pmac *chip = snd_pcm_substream_chip(subs);	return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd);}static snd_pcm_uframes_t snd_pmac_playback_pointer(struct snd_pcm_substream *subs){	struct snd_pmac *chip = snd_pcm_substream_chip(subs);	return snd_pmac_pcm_pointer(chip, &chip->playback, subs);}/* * capture */static int snd_pmac_capture_prepare(struct snd_pcm_substream *subs){	struct snd_pmac *chip = snd_pcm_substream_chip(subs);	return snd_pmac_pcm_prepare(chip, &chip->capture, subs);}static int snd_pmac_capture_trigger(struct snd_pcm_substream *subs,				    int cmd){	struct snd_pmac *chip = snd_pcm_substream_chip(subs);	return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd);}static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs){	struct snd_pmac *chip = snd_pcm_substream_chip(subs);	return snd_pmac_pcm_pointer(chip, &chip->capture, subs);}/* * update playback/capture pointer from interrupts */static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec){	volatile struct dbdma_cmd __iomem *cp;	int c;	int stat;	spin_lock(&chip->reg_lock);	if (rec->running) {		cp = &rec->cmd.cmds[rec->cur_period];		for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */			stat = ld_le16(&cp->xfer_status);			if (! (stat & ACTIVE))				break;			/*printk("update frag %d\n", rec->cur_period);*/			st_le16(&cp->xfer_status, 0);			st_le16(&cp->req_count, rec->period_size);			/*st_le16(&cp->res_count, 0);*/			rec->cur_period++;			if (rec->cur_period >= rec->nperiods) {				rec->cur_period = 0;				cp = rec->cmd.cmds;			} else				cp++;			spin_unlock(&chip->reg_lock);			snd_pcm_period_elapsed(rec->substream);			spin_lock(&chip->reg_lock);		}	}	spin_unlock(&chip->reg_lock);}/* * hw info */static struct snd_pcm_hardware snd_pmac_playback ={	.info =			(SNDRV_PCM_INFO_INTERLEAVED |				 SNDRV_PCM_INFO_MMAP |				 SNDRV_PCM_INFO_MMAP_VALID |				 SNDRV_PCM_INFO_RESUME),	.formats =		SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE,	.rates =		SNDRV_PCM_RATE_8000_44100,	.rate_min =		7350,	.rate_max =		44100,	.channels_min =		2,	.channels_max =		2,	.buffer_bytes_max =	131072,	.period_bytes_min =	256,	.period_bytes_max =	16384,	.periods_min =		3,	.periods_max =		PMAC_MAX_FRAGS,};static struct snd_pcm_hardware snd_pmac_capture ={	.info =			(SNDRV_PCM_INFO_INTERLEAVED |				 SNDRV_PCM_INFO_MMAP |

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -