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

📄 harmony.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Hewlett-Packard Harmony audio driver * *   This is a driver for the Harmony audio chipset found *   on the LASI ASIC of various early HP PA-RISC workstations. * *   Copyright (C) 2004, Kyle McMartin <kyle@{debian.org,parisc-linux.org}> * *     Based on the previous Harmony incarnations by, *       Copyright 2000 (c) Linuxcare Canada, Alex deVries *       Copyright 2000-2003 (c) Helge Deller *       Copyright 2001 (c) Matthieu Delahaye *       Copyright 2001 (c) Jean-Christophe Vaugeois *       Copyright 2003 (c) Laurent Canet *       Copyright 2004 (c) Stuart Brady * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License, version 2, as *   published by the Free Software Foundation. * *   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., 675 Mass Ave, Cambridge, MA 02139, USA. * * Notes: *   - graveyard and silence buffers last for lifetime of *     the driver. playback and capture buffers are allocated *     per _open()/_close(). *  * TODO: * */#include <linux/init.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/wait.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/dma-mapping.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/control.h>#include <sound/rawmidi.h>#include <sound/initval.h>#include <sound/info.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/parisc-device.h>#include "harmony.h"static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for Harmony driver.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for Harmony driver.");static struct parisc_device_id snd_harmony_devtable[] = {	/* bushmaster / flounder */	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, 	/* 712 / 715 */	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, 	/* pace */	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, 	/* outfield / coral II */	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F },	{ 0, }};MODULE_DEVICE_TABLE(parisc, snd_harmony_devtable);#define NAME "harmony"#define PFX  NAME ": "static unsigned int snd_harmony_rates[] = {	5512, 6615, 8000, 9600,	11025, 16000, 18900, 22050,	27428, 32000, 33075, 37800,	44100, 48000};static unsigned int rate_bits[14] = {	HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,	HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,	HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,	HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ,	HARMONY_SR_44KHZ, HARMONY_SR_48KHZ};static struct snd_pcm_hw_constraint_list hw_constraint_rates = {	.count = ARRAY_SIZE(snd_harmony_rates),	.list = snd_harmony_rates,	.mask = 0,};static inline unsigned longharmony_read(struct snd_harmony *h, unsigned r){	return __raw_readl(h->iobase + r);}static inline voidharmony_write(struct snd_harmony *h, unsigned r, unsigned long v){	__raw_writel(v, h->iobase + r);}static inline voidharmony_wait_for_control(struct snd_harmony *h){	while (harmony_read(h, HARMONY_CNTL) & HARMONY_CNTL_C) ;}static inline voidharmony_reset(struct snd_harmony *h){	harmony_write(h, HARMONY_RESET, 1);	mdelay(50);	harmony_write(h, HARMONY_RESET, 0);}static voidharmony_disable_interrupts(struct snd_harmony *h){	u32 dstatus;	harmony_wait_for_control(h);	dstatus = harmony_read(h, HARMONY_DSTATUS);	dstatus &= ~HARMONY_DSTATUS_IE;	harmony_write(h, HARMONY_DSTATUS, dstatus);}static voidharmony_enable_interrupts(struct snd_harmony *h){	u32 dstatus;	harmony_wait_for_control(h);	dstatus = harmony_read(h, HARMONY_DSTATUS);	dstatus |= HARMONY_DSTATUS_IE;	harmony_write(h, HARMONY_DSTATUS, dstatus);}static voidharmony_mute(struct snd_harmony *h){	unsigned long flags;	spin_lock_irqsave(&h->mixer_lock, flags);	harmony_wait_for_control(h);	harmony_write(h, HARMONY_GAINCTL, HARMONY_GAIN_SILENCE);	spin_unlock_irqrestore(&h->mixer_lock, flags);}static voidharmony_unmute(struct snd_harmony *h){	unsigned long flags;	spin_lock_irqsave(&h->mixer_lock, flags);	harmony_wait_for_control(h);	harmony_write(h, HARMONY_GAINCTL, h->st.gain);	spin_unlock_irqrestore(&h->mixer_lock, flags);}static voidharmony_set_control(struct snd_harmony *h){	u32 ctrl;	unsigned long flags;	spin_lock_irqsave(&h->lock, flags);	ctrl = (HARMONY_CNTL_C      |		(h->st.format << 6) |		(h->st.stereo << 5) |		(h->st.rate));	harmony_wait_for_control(h);	harmony_write(h, HARMONY_CNTL, ctrl);	spin_unlock_irqrestore(&h->lock, flags);}static irqreturn_tsnd_harmony_interrupt(int irq, void *dev){	u32 dstatus;	struct snd_harmony *h = dev;	spin_lock(&h->lock);	harmony_disable_interrupts(h);	harmony_wait_for_control(h);	dstatus = harmony_read(h, HARMONY_DSTATUS);	spin_unlock(&h->lock);	if (dstatus & HARMONY_DSTATUS_PN) {		if (h->psubs && h->st.playing) {			spin_lock(&h->lock);			h->pbuf.buf += h->pbuf.count; /* PAGE_SIZE */			h->pbuf.buf %= h->pbuf.size; /* MAX_BUFS*PAGE_SIZE */			harmony_write(h, HARMONY_PNXTADD, 				      h->pbuf.addr + h->pbuf.buf);			h->stats.play_intr++;			spin_unlock(&h->lock);                        snd_pcm_period_elapsed(h->psubs);		} else {			spin_lock(&h->lock);			harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);			h->stats.silence_intr++;			spin_unlock(&h->lock);		}	}	if (dstatus & HARMONY_DSTATUS_RN) {		if (h->csubs && h->st.capturing) {			spin_lock(&h->lock);			h->cbuf.buf += h->cbuf.count;			h->cbuf.buf %= h->cbuf.size;			harmony_write(h, HARMONY_RNXTADD,				      h->cbuf.addr + h->cbuf.buf);			h->stats.rec_intr++;			spin_unlock(&h->lock);                        snd_pcm_period_elapsed(h->csubs);		} else {			spin_lock(&h->lock);			harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);			h->stats.graveyard_intr++;			spin_unlock(&h->lock);		}	}	spin_lock(&h->lock);	harmony_enable_interrupts(h);	spin_unlock(&h->lock);	return IRQ_HANDLED;}static unsigned int snd_harmony_rate_bits(int rate){	unsigned int i;		for (i = 0; i < ARRAY_SIZE(snd_harmony_rates); i++)		if (snd_harmony_rates[i] == rate)			return rate_bits[i];	return HARMONY_SR_44KHZ;}static struct snd_pcm_hardware snd_harmony_playback ={	.info =	(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 		 SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |		 SNDRV_PCM_INFO_BLOCK_TRANSFER),	.formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |		    SNDRV_PCM_FMTBIT_A_LAW),	.rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |		  SNDRV_PCM_RATE_KNOT),	.rate_min = 5512,	.rate_max = 48000,	.channels_min =	1,	.channels_max =	2,	.buffer_bytes_max = MAX_BUF_SIZE,	.period_bytes_min = BUF_SIZE,	.period_bytes_max = BUF_SIZE,	.periods_min = 1,	.periods_max = MAX_BUFS,	.fifo_size = 0,};static struct snd_pcm_hardware snd_harmony_capture ={        .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |                 SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_MMAP_VALID |                 SNDRV_PCM_INFO_BLOCK_TRANSFER),        .formats = (SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_MU_LAW |                    SNDRV_PCM_FMTBIT_A_LAW),        .rates = (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 |		  SNDRV_PCM_RATE_KNOT),        .rate_min = 5512,        .rate_max = 48000,        .channels_min = 1,        .channels_max = 2,        .buffer_bytes_max = MAX_BUF_SIZE,        .period_bytes_min = BUF_SIZE,        .period_bytes_max = BUF_SIZE,        .periods_min = 1,        .periods_max = MAX_BUFS,        .fifo_size = 0,};static intsnd_harmony_playback_trigger(struct snd_pcm_substream *ss, int cmd){	struct snd_harmony *h = snd_pcm_substream_chip(ss);	if (h->st.capturing)		return -EBUSY;	spin_lock(&h->lock);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:		h->st.playing = 1;		harmony_write(h, HARMONY_PNXTADD, h->pbuf.addr);		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);		harmony_unmute(h);		harmony_enable_interrupts(h);		break;	case SNDRV_PCM_TRIGGER_STOP:		h->st.playing = 0;		harmony_mute(h);		harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);		harmony_disable_interrupts(h);		break;	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:	case SNDRV_PCM_TRIGGER_SUSPEND:	default:		spin_unlock(&h->lock);		snd_BUG();		return -EINVAL;	}	spin_unlock(&h->lock);		return 0;}static intsnd_harmony_capture_trigger(struct snd_pcm_substream *ss, int cmd){        struct snd_harmony *h = snd_pcm_substream_chip(ss);	if (h->st.playing)		return -EBUSY;	spin_lock(&h->lock);        switch (cmd) {        case SNDRV_PCM_TRIGGER_START:		h->st.capturing = 1;                harmony_write(h, HARMONY_PNXTADD, h->sdma.addr);                harmony_write(h, HARMONY_RNXTADD, h->cbuf.addr);		harmony_unmute(h);                harmony_enable_interrupts(h);		break;        case SNDRV_PCM_TRIGGER_STOP:		h->st.capturing = 0;		harmony_mute(h);		harmony_write(h, HARMONY_RNXTADD, h->gdma.addr);		harmony_disable_interrupts(h);		break;        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:        case SNDRV_PCM_TRIGGER_SUSPEND:	default:		spin_unlock(&h->lock);		snd_BUG();                return -EINVAL;        }	spin_unlock(&h->lock);		        return 0;}static intsnd_harmony_set_data_format(struct snd_harmony *h, int fmt, int force){	int o = h->st.format;	int n;	switch(fmt) {	case SNDRV_PCM_FORMAT_S16_BE:		n = HARMONY_DF_16BIT_LINEAR;		break;	case SNDRV_PCM_FORMAT_A_LAW:		n = HARMONY_DF_8BIT_ALAW;		break;	case SNDRV_PCM_FORMAT_MU_LAW:		n = HARMONY_DF_8BIT_ULAW;		break;	default:		n = HARMONY_DF_16BIT_LINEAR;		break;	}	if (force || o != n) {		snd_pcm_format_set_silence(fmt, h->sdma.area, SILENCE_BUFSZ / 					   (snd_pcm_format_physical_width(fmt)					    / 8));	}	return n;}static intsnd_harmony_playback_prepare(struct snd_pcm_substream *ss){	struct snd_harmony *h = snd_pcm_substream_chip(ss);	struct snd_pcm_runtime *rt = ss->runtime;		if (h->st.capturing)		return -EBUSY;		h->pbuf.size = snd_pcm_lib_buffer_bytes(ss);	h->pbuf.count = snd_pcm_lib_period_bytes(ss);	if (h->pbuf.buf >= h->pbuf.size)		h->pbuf.buf = 0;	h->st.playing = 0;	h->st.rate = snd_harmony_rate_bits(rt->rate);	h->st.format = snd_harmony_set_data_format(h, rt->format, 0);		if (rt->channels == 2)		h->st.stereo = HARMONY_SS_STEREO;	else		h->st.stereo = HARMONY_SS_MONO;	harmony_set_control(h);	h->pbuf.addr = rt->dma_addr;	return 0;}static intsnd_harmony_capture_prepare(struct snd_pcm_substream *ss){        struct snd_harmony *h = snd_pcm_substream_chip(ss);        struct snd_pcm_runtime *rt = ss->runtime;	if (h->st.playing)		return -EBUSY;        h->cbuf.size = snd_pcm_lib_buffer_bytes(ss);        h->cbuf.count = snd_pcm_lib_period_bytes(ss);	if (h->cbuf.buf >= h->cbuf.size)	        h->cbuf.buf = 0;	h->st.capturing = 0;        h->st.rate = snd_harmony_rate_bits(rt->rate);        h->st.format = snd_harmony_set_data_format(h, rt->format, 0);        if (rt->channels == 2)                h->st.stereo = HARMONY_SS_STEREO;        else                h->st.stereo = HARMONY_SS_MONO;        harmony_set_control(h);        h->cbuf.addr = rt->dma_addr;        return 0;}static snd_pcm_uframes_t snd_harmony_playback_pointer(struct snd_pcm_substream *ss){	struct snd_pcm_runtime *rt = ss->runtime;	struct snd_harmony *h = snd_pcm_substream_chip(ss);	unsigned long pcuradd;	unsigned long played;	if (!(h->st.playing) || (h->psubs == NULL)) 		return 0;	if ((h->pbuf.addr == 0) || (h->pbuf.size == 0))		return 0;		pcuradd = harmony_read(h, HARMONY_PCURADD);	played = pcuradd - h->pbuf.addr;#ifdef HARMONY_DEBUG	printk(KERN_DEBUG PFX "playback_pointer is 0x%lx-0x%lx = %d bytes\n", 	       pcuradd, h->pbuf.addr, played);	#endif	if (pcuradd > h->pbuf.addr + h->pbuf.size) {		return 0;	}	return bytes_to_frames(rt, played);}static snd_pcm_uframes_tsnd_harmony_capture_pointer(struct snd_pcm_substream *ss){        struct snd_pcm_runtime *rt = ss->runtime;        struct snd_harmony *h = snd_pcm_substream_chip(ss);        unsigned long rcuradd;        unsigned long caught;        if (!(h->st.capturing) || (h->csubs == NULL))                return 0;        if ((h->cbuf.addr == 0) || (h->cbuf.size == 0))                return 0;        rcuradd = harmony_read(h, HARMONY_RCURADD);        caught = rcuradd - h->cbuf.addr;#ifdef HARMONY_DEBUG        printk(KERN_DEBUG PFX "capture_pointer is 0x%lx-0x%lx = %d bytes\n",               rcuradd, h->cbuf.addr, caught);#endif        if (rcuradd > h->cbuf.addr + h->cbuf.size) {		return 0;	}

⌨️ 快捷键说明

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