[kernel] r11816 - in dists/trunk/linux-2.6/debian: . config/mips patches/features/mips patches/series

Martin Michlmayr tbm at alioth.debian.org
Wed Jul 9 11:07:45 UTC 2008


Author: tbm
Date: Wed Jul  9 11:07:40 2008
New Revision: 11816

Log:
add SGI O2 sound driver


Added:
   dists/trunk/linux-2.6/debian/patches/features/mips/ip32_sound.patch
Modified:
   dists/trunk/linux-2.6/debian/changelog
   dists/trunk/linux-2.6/debian/config/mips/config.r5k-ip32
   dists/trunk/linux-2.6/debian/patches/series/1~experimental.1

Modified: dists/trunk/linux-2.6/debian/changelog
==============================================================================
--- dists/trunk/linux-2.6/debian/changelog	(original)
+++ dists/trunk/linux-2.6/debian/changelog	Wed Jul  9 11:07:40 2008
@@ -88,9 +88,10 @@
     on the request of Heinz Janssen.
   * [arm/orion5x] Add support for QNAP TS-409 and HP mv2120; thanks
     Sylver Bruneau.
-  * [mips] Add fixes from Thomas Bogendoerfer:
+  * [mips] Add patches from Thomas Bogendoerfer:
     - gbefb: fix cmap FIFO timeout (closes: #487257)
     - IP32: Enable FAST-20 for onboard scsi
+    - IP32: SGI O2 sound driver
   * [arm/ixp4xx] Add support for Freecom FSG-3 (Rod Whitby).
   * [arm/ixp4xx] Enable CONFIG_MACH_DSMG600.
   * [arm/iop32x] Unset NET_DMA since it actually leads to worse network

Modified: dists/trunk/linux-2.6/debian/config/mips/config.r5k-ip32
==============================================================================
--- dists/trunk/linux-2.6/debian/config/mips/config.r5k-ip32	(original)
+++ dists/trunk/linux-2.6/debian/config/mips/config.r5k-ip32	Wed Jul  9 11:07:40 2008
@@ -286,7 +286,8 @@
 ##
 ## file: drivers/input/misc/Kconfig
 ##
-# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_SGIO2_BTNS=m
 
 ##
 ## file: drivers/input/mouse/Kconfig
@@ -772,3 +773,5 @@
 # CONFIG_SND_VX222 is not set
 # CONFIG_SND_YMFPCI is not set
 
+CONFIG_SND_SGI_O2=m
+

Added: dists/trunk/linux-2.6/debian/patches/features/mips/ip32_sound.patch
==============================================================================
--- (empty file)
+++ dists/trunk/linux-2.6/debian/patches/features/mips/ip32_sound.patch	Wed Jul  9 11:07:40 2008
@@ -0,0 +1,1914 @@
+On Thu, Jul 03, 2008 at 01:21:18AM +0200, Thomas Bogendoerfer wrote:
+> [...]
+
+changes in v6:
+
+- fixed mmap setup
+- capture is working now
+- reworked all mixer controls
+- added dma error interrupt
+- fixed button detection in input driver
+
+This is the final complete patch. I'm now going to split the patch
+into pieces for submission to the different maintainers.
+
+Thomas.
+
+
+ arch/mips/sgi-ip32/ip32-platform.c |   36 ++
+ drivers/input/misc/Kconfig         |   10 +
+ drivers/input/misc/Makefile        |    1 +
+ drivers/input/misc/sgio2_btns.c    |  154 ++++++
+ include/sound/ad1843.h             |   46 ++
+ sound/mips/Kconfig                 |    6 +
+ sound/mips/Makefile                |    2 +
+ sound/mips/ad1843.c                |  539 +++++++++++++++++++
+ sound/mips/sgio2audio.c            | 1013 ++++++++++++++++++++++++++++++++++++
+ 9 files changed, 1807 insertions(+), 0 deletions(-)
+
+diff --git a/arch/mips/sgi-ip32/ip32-platform.c b/arch/mips/sgi-ip32/ip32-platform.c
+index 89a71f4..2ee401b 100644
+--- a/arch/mips/sgi-ip32/ip32-platform.c
++++ b/arch/mips/sgi-ip32/ip32-platform.c
+@@ -65,6 +65,42 @@ static __init int meth_devinit(void)
+ 
+ device_initcall(meth_devinit);
+ 
++static __init int sgio2audio_devinit(void)
++{
++	struct platform_device *pd;
++	int ret;
++
++	pd = platform_device_alloc("sgio2audio", -1);
++	if (!pd)
++		return -ENOMEM;
++
++	ret = platform_device_add(pd);
++	if (ret)
++		platform_device_put(pd);
++
++	return ret;
++}
++
++device_initcall(sgio2audio_devinit);
++
++static __init int sgio2btns_devinit(void)
++{
++	struct platform_device *pd;
++	int ret;
++
++	pd = platform_device_alloc("sgio2btns", -1);
++	if (!pd)
++		return -ENOMEM;
++
++	ret = platform_device_add(pd);
++	if (ret)
++		platform_device_put(pd);
++
++	return ret;
++}
++
++device_initcall(sgio2btns_devinit);
++
+ MODULE_AUTHOR("Ralf Baechle <ralf at linux-mips.org>");
+ MODULE_LICENSE("GPL");
+ MODULE_DESCRIPTION("8250 UART probe driver for SGI IP32 aka O2");
+diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
+index 432699d..e33d7cb 100644
+--- a/drivers/input/misc/Kconfig
++++ b/drivers/input/misc/Kconfig
+@@ -189,6 +189,16 @@ config INPUT_UINPUT
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called uinput.
+ 
++config INPUT_SGIO2_BTNS
++	tristate "SGI O2 volume button interface"
++	depends on SGI_IP32
++	select INPUT_POLLDEV
++	help
++	  Say Y here if you want to support SGI O2 volume button interface.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called sgio_btns.
++
+ config HP_SDC_RTC
+ 	tristate "HP SDC Real Time Clock"
+ 	depends on GSC || HP300
+diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
+index ebd39f2..0e274f1 100644
+--- a/drivers/input/misc/Makefile
++++ b/drivers/input/misc/Makefile
+@@ -19,3 +19,4 @@ obj-$(CONFIG_INPUT_YEALINK)		+= yealink.o
+ obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
+ obj-$(CONFIG_INPUT_UINPUT)		+= uinput.o
+ obj-$(CONFIG_INPUT_APANEL)		+= apanel.o
++obj-$(CONFIG_INPUT_SGIO2_BTNS)		+= sgio2_btns.o
+diff --git a/drivers/input/misc/sgio2_btns.c b/drivers/input/misc/sgio2_btns.c
+new file mode 100644
+index 0000000..5029873
+--- /dev/null
++++ b/drivers/input/misc/sgio2_btns.c
+@@ -0,0 +1,154 @@
++/*
++ *  Cobalt button interface driver.
++ *
++ *  Copyright (C) 2007-2008  Yoichi Yuasa <yoichi_yuasa at tripeaks.co.jp>
++ *
++ *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++#include <linux/init.h>
++#include <linux/input-polldev.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <asm/ip32/mace.h>
++
++#define BUTTONS_POLL_INTERVAL	30	/* msec */
++#define BUTTONS_COUNT_THRESHOLD	3
++
++static const unsigned short sgio2_map[] = {
++	KEY_VOLUMEUP,
++	KEY_VOLUMEDOWN
++};
++
++struct buttons_dev {
++	struct input_polled_dev *poll_dev;
++	unsigned short keymap[ARRAY_SIZE(sgio2_map)];
++	int count[ARRAY_SIZE(sgio2_map)];
++	void __iomem *reg;
++};
++
++static void handle_buttons(struct input_polled_dev *dev)
++{
++	struct buttons_dev *bdev = dev->private;
++	struct input_dev *input = dev->input;
++	u64 status;
++	int i;
++
++	status = (readq(&mace->perif.audio.control) >> 23) & 3;
++
++	for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
++		if (status & (1U << i)) {
++			writeq(status & ~(1U << i), &mace->perif.audio.control);
++			if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
++				input_event(input, EV_MSC, MSC_SCAN, i);
++				input_report_key(input, bdev->keymap[i], 1);
++				input_sync(input);
++			}
++		} else {
++			if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
++				input_event(input, EV_MSC, MSC_SCAN, i);
++				input_report_key(input, bdev->keymap[i], 0);
++				input_sync(input);
++			}
++			bdev->count[i] = 0;
++		}
++	}
++}
++
++static int __devinit sgio2_buttons_probe(struct platform_device *pdev)
++{
++	struct buttons_dev *bdev;
++	struct input_polled_dev *poll_dev;
++	struct input_dev *input;
++	int error, i;
++
++	bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
++	poll_dev = input_allocate_polled_device();
++	if (!bdev || !poll_dev) {
++		error = -ENOMEM;
++		goto err_free_mem;
++	}
++
++	memcpy(bdev->keymap, sgio2_map, sizeof(bdev->keymap));
++
++	poll_dev->private = bdev;
++	poll_dev->poll = handle_buttons;
++	poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
++
++	input = poll_dev->input;
++	input->name = "SGI O2 buttons";
++	input->phys = "sgio2/input0";
++	input->id.bustype = BUS_HOST;
++	input->dev.parent = &pdev->dev;
++
++	input->keycode = bdev->keymap;
++	input->keycodemax = ARRAY_SIZE(bdev->keymap);
++	input->keycodesize = sizeof(unsigned short);
++
++	input_set_capability(input, EV_MSC, MSC_SCAN);
++	__set_bit(EV_KEY, input->evbit);
++	for (i = 0; i < ARRAY_SIZE(sgio2_map); i++)
++		__set_bit(bdev->keymap[i], input->keybit);
++	__clear_bit(KEY_RESERVED, input->keybit);
++
++	bdev->poll_dev = poll_dev;
++	dev_set_drvdata(&pdev->dev, bdev);
++
++	error = input_register_polled_device(poll_dev);
++	if (!error)
++		return 0;
++
++ err_free_mem:
++	input_free_polled_device(poll_dev);
++	kfree(bdev);
++	dev_set_drvdata(&pdev->dev, NULL);
++	return error;
++}
++
++static int __devexit sgio2_buttons_remove(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct buttons_dev *bdev = dev_get_drvdata(dev);
++
++	input_unregister_polled_device(bdev->poll_dev);
++	input_free_polled_device(bdev->poll_dev);
++	kfree(bdev);
++	dev_set_drvdata(dev, NULL);
++
++	return 0;
++}
++
++static struct platform_driver sgio2_buttons_driver = {
++	.probe	= sgio2_buttons_probe,
++	.remove	= __devexit_p(sgio2_buttons_remove),
++	.driver	= {
++		.name	= "sgio2btns",
++		.owner	= THIS_MODULE,
++	},
++};
++
++static int __init sgio2_buttons_init(void)
++{
++	return platform_driver_register(&sgio2_buttons_driver);
++}
++
++static void __exit sgio2_buttons_exit(void)
++{
++	platform_driver_unregister(&sgio2_buttons_driver);
++}
++
++module_init(sgio2_buttons_init);
++module_exit(sgio2_buttons_exit);
+diff --git a/include/sound/ad1843.h b/include/sound/ad1843.h
+new file mode 100644
+index 0000000..b236a9d
+--- /dev/null
++++ b/include/sound/ad1843.h
+@@ -0,0 +1,46 @@
++/*
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright 2003 Vivien Chappelier <vivien.chappelier at linux-mips.org>
++ * Copyright 2008 Thomas Bogendoerfer <tsbogend at franken.de>
++ */
++
++#ifndef __SOUND_AD1843_H
++#define __SOUND_AD1843_H
++
++struct snd_ad1843 {
++	void *chip;
++	int (*read)(void *chip, int reg);
++	int (*write)(void *chip, int reg, int val);
++};
++
++#define AD1843_GAIN_RECLEV 0
++#define AD1843_GAIN_LINE   1
++#define AD1843_GAIN_LINE_2 2
++#define AD1843_GAIN_MIC    3
++#define AD1843_GAIN_PCM_0  4
++#define AD1843_GAIN_PCM_1  5
++#define AD1843_GAIN_SIZE   (AD1843_GAIN_PCM_1+1)
++
++int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id);
++int ad1843_get_gain(struct snd_ad1843 *ad1843, int id);
++int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval);
++int ad1843_get_recsrc(struct snd_ad1843 *ad1843);
++int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc);
++void ad1843_setup_dac(struct snd_ad1843 *ad1843,
++		      unsigned int id,
++		      unsigned int framerate,
++		      snd_pcm_format_t fmt,
++		      unsigned int channels);
++void ad1843_shutdown_dac(struct snd_ad1843 *ad1843,
++			 unsigned int id);
++void ad1843_setup_adc(struct snd_ad1843 *ad1843,
++		      unsigned int framerate,
++		      snd_pcm_format_t fmt,
++		      unsigned int channels);
++void ad1843_shutdown_adc(struct snd_ad1843 *ad1843);
++int ad1843_init(struct snd_ad1843 *ad1843);
++
++#endif /* __SOUND_AD1843_H */
+diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
+index 531f8ba..a3e202e 100644
+--- a/sound/mips/Kconfig
++++ b/sound/mips/Kconfig
+@@ -11,5 +11,11 @@ config SND_AU1X00
+ 	help
+ 	  ALSA Sound driver for the Au1x00's AC97 port.
+ 
++config SND_SGI_O2
++	tristate "SGI O2 Audio"
++	depends on  SND && SGI_IP32
++        help
++                Sound support for the SGI O2 Workstation. 
++
+ endmenu
+ 
+diff --git a/sound/mips/Makefile b/sound/mips/Makefile
+index 47afed9..55624d8 100644
+--- a/sound/mips/Makefile
++++ b/sound/mips/Makefile
+@@ -2,7 +2,9 @@
+ # Makefile for ALSA
+ #
+ 
++snd-sgi-o2-objs := sgio2audio.o ad1843.o
+ snd-au1x00-objs := au1x00.o
+ 
+ # Toplevel Module Dependency
+ obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
++obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
+diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c
+new file mode 100644
+index 0000000..76bdf68
+--- /dev/null
++++ b/sound/mips/ad1843.c
+@@ -0,0 +1,539 @@
++/*
++ *   AD1843 low level driver
++ *
++ *   Copyright 2003 Vivien Chappelier <vivien.chappelier at linux-mips.org>
++ *   Copyright 2008 Thomas Bogendoerfer <tsbogend at alpha.franken.de>
++ *
++ *   inspired from vwsnd.c (SGI VW audio driver)
++ *     Copyright 1999 Silicon Graphics, Inc.  All rights reserved.
++ *
++ *   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 <linux/init.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ad1843.h>
++
++/*
++ * AD1843 bitfield definitions.  All are named as in the AD1843 data
++ * sheet, with ad1843_ prepended and individual bit numbers removed.
++ *
++ * E.g., bits LSS0 through LSS2 become ad1843_LSS.
++ *
++ * Only the bitfields we need are defined.
++ */
++
++struct ad1843_bitfield {
++	char reg;
++	char lo_bit;
++	char nbits;
++};
++
++static const struct ad1843_bitfield
++	ad1843_PDNO   = {  0, 14,  1 },	/* Converter Power-Down Flag */
++	ad1843_INIT   = {  0, 15,  1 },	/* Clock Initialization Flag */
++	ad1843_RIG    = {  2,  0,  4 },	/* Right ADC Input Gain */
++	ad1843_RMGE   = {  2,  4,  1 },	/* Right ADC Mic Gain Enable */
++	ad1843_RSS    = {  2,  5,  3 },	/* Right ADC Source Select */
++	ad1843_LIG    = {  2,  8,  4 },	/* Left ADC Input Gain */
++	ad1843_LMGE   = {  2, 12,  1 },	/* Left ADC Mic Gain Enable */
++	ad1843_LSS    = {  2, 13,  3 },	/* Left ADC Source Select */
++	ad1843_RD2M   = {  3,  0,  5 },	/* Right DAC 2 Mix Gain/Atten */
++	ad1843_RD2MM  = {  3,  7,  1 },	/* Right DAC 2 Mix Mute */
++	ad1843_LD2M   = {  3,  8,  5 },	/* Left DAC 2 Mix Gain/Atten */
++	ad1843_LD2MM  = {  3, 15,  1 },	/* Left DAC 2 Mix Mute */
++	ad1843_RX1M   = {  4,  0,  5 },	/* Right Aux 1 Mix Gain/Atten */
++	ad1843_RX1MM  = {  4,  7,  1 },	/* Right Aux 1 Mix Mute */
++	ad1843_LX1M   = {  4,  8,  5 },	/* Left Aux 1 Mix Gain/Atten */
++	ad1843_LX1MM  = {  4, 15,  1 },	/* Left Aux 1 Mix Mute */
++	ad1843_RX2M   = {  5,  0,  5 },	/* Right Aux 2 Mix Gain/Atten */
++	ad1843_RX2MM  = {  5,  7,  1 },	/* Right Aux 2 Mix Mute */
++	ad1843_LX2M   = {  5,  8,  5 },	/* Left Aux 2 Mix Gain/Atten */
++	ad1843_LX2MM  = {  5, 15,  1 },	/* Left Aux 2 Mix Mute */
++	ad1843_RMCM   = {  7,  0,  5 },	/* Right Mic Mix Gain/Atten */
++	ad1843_RMCMM  = {  7,  7,  1 },	/* Right Mic Mix Mute */
++	ad1843_LMCM   = {  7,  8,  5 },	/* Left Mic Mix Gain/Atten */
++	ad1843_LMCMM  = {  7, 15,  1 },	/* Left Mic Mix Mute */
++	ad1843_HPOS   = {  8,  4,  1 },	/* Headphone Output Voltage Swing */
++	ad1843_HPOM   = {  8,  5,  1 },	/* Headphone Output Mute */
++	ad1843_MPOM   = {  8,  6,  1 },	/* Mono Output Mute */
++	ad1843_RDA1G  = {  9,  0,  6 },	/* Right DAC1 Analog/Digital Gain */
++	ad1843_RDA1GM = {  9,  7,  1 },	/* Right DAC1 Analog Mute */
++	ad1843_LDA1G  = {  9,  8,  6 },	/* Left DAC1 Analog/Digital Gain */
++	ad1843_LDA1GM = {  9, 15,  1 },	/* Left DAC1 Analog Mute */
++	ad1843_RDA2G  = { 10,  0,  6 },	/* Right DAC2 Analog/Digital Gain */
++	ad1843_RDA2GM = { 10,  7,  1 },	/* Right DAC2 Analog Mute */
++	ad1843_LDA2G  = { 10,  8,  6 },	/* Left DAC2 Analog/Digital Gain */
++	ad1843_LDA2GM = { 10, 15,  1 },	/* Left DAC2 Analog Mute */
++	ad1843_RDA1AM = { 11,  7,  1 },	/* Right DAC1 Digital Mute */
++	ad1843_LDA1AM = { 11, 15,  1 },	/* Left DAC1 Digital Mute */
++	ad1843_RDA2AM = { 12,  7,  1 },	/* Right DAC2 Digital Mute */
++	ad1843_LDA2AM = { 12, 15,  1 },	/* Left DAC2 Digital Mute */
++	ad1843_ADLC   = { 15,  0,  2 },	/* ADC Left Sample Rate Source */
++	ad1843_ADRC   = { 15,  2,  2 },	/* ADC Right Sample Rate Source */
++	ad1843_DA1C   = { 15,  8,  2 },	/* DAC1 Sample Rate Source */
++	ad1843_DA2C   = { 15, 10,  2 },	/* DAC2 Sample Rate Source */
++	ad1843_C1C    = { 17,  0, 16 },	/* Clock 1 Sample Rate Select */
++	ad1843_C2C    = { 20,  0, 16 },	/* Clock 2 Sample Rate Select */
++	ad1843_C3C    = { 23,  0, 16 },	/* Clock 3 Sample Rate Select */
++	ad1843_DAADL  = { 25,  4,  2 },	/* Digital ADC Left Source Select */
++	ad1843_DAADR  = { 25,  6,  2 },	/* Digital ADC Right Source Select */
++	ad1843_DAMIX  = { 25, 14,  1 },	/* DAC Digital Mix Enable */
++	ad1843_DRSFLT = { 25, 15,  1 },	/* Digital Reampler Filter Mode */
++	ad1843_ADLF   = { 26,  0,  2 }, /* ADC Left Channel Data Format */
++	ad1843_ADRF   = { 26,  2,  2 }, /* ADC Right Channel Data Format */
++	ad1843_ADTLK  = { 26,  4,  1 },	/* ADC Transmit Lock Mode Select */
++	ad1843_SCF    = { 26,  7,  1 },	/* SCLK Frequency Select */
++	ad1843_DA1F   = { 26,  8,  2 },	/* DAC1 Data Format Select */
++	ad1843_DA2F   = { 26, 10,  2 },	/* DAC2 Data Format Select */
++	ad1843_DA1SM  = { 26, 14,  1 },	/* DAC1 Stereo/Mono Mode Select */
++	ad1843_DA2SM  = { 26, 15,  1 },	/* DAC2 Stereo/Mono Mode Select */
++	ad1843_ADLEN  = { 27,  0,  1 },	/* ADC Left Channel Enable */
++	ad1843_ADREN  = { 27,  1,  1 },	/* ADC Right Channel Enable */
++	ad1843_AAMEN  = { 27,  4,  1 },	/* Analog to Analog Mix Enable */
++	ad1843_ANAEN  = { 27,  7,  1 },	/* Analog Channel Enable */
++	ad1843_DA1EN  = { 27,  8,  1 },	/* DAC1 Enable */
++	ad1843_DA2EN  = { 27,  9,  1 },	/* DAC2 Enable */
++	ad1843_DDMEN  = { 27, 12,  1 },	/* DAC2 to DAC1 Mix  Enable */
++	ad1843_C1EN   = { 28, 11,  1 },	/* Clock Generator 1 Enable */
++	ad1843_C2EN   = { 28, 12,  1 },	/* Clock Generator 2 Enable */
++	ad1843_C3EN   = { 28, 13,  1 },	/* Clock Generator 3 Enable */
++	ad1843_PDNI   = { 28, 15,  1 };	/* Converter Power Down */
++
++/*
++ * The various registers of the AD1843 use three different formats for
++ * specifying gain.  The ad1843_gain structure parameterizes the
++ * formats.
++ */
++
++struct ad1843_gain {
++	int	negative;		/* nonzero if gain is negative. */
++	const struct ad1843_bitfield *lfield;
++	const struct ad1843_bitfield *rfield;
++	const struct ad1843_bitfield *lmute;
++	const struct ad1843_bitfield *rmute;
++};
++
++const struct ad1843_gain ad1843_gain_RECLEV = {
++	0, &ad1843_LIG,   &ad1843_RIG
++};
++const struct ad1843_gain ad1843_gain_LINE = {
++	1, &ad1843_LX1M,  &ad1843_RX1M,  &ad1843_LX1MM, &ad1843_RX1MM
++};
++const struct ad1843_gain ad1843_gain_LINE_2 = {
++	1, &ad1843_LDA2G, &ad1843_RDA2G, &ad1843_LDA2GM, &ad1843_RDA2GM
++};
++const struct ad1843_gain ad1843_gain_MIC = {
++	1, &ad1843_LMCM,  &ad1843_RMCM,  &ad1843_LMCMM,  &ad1843_RMCMM
++};
++const struct ad1843_gain ad1843_gain_PCM_0 = {
++	1, &ad1843_LDA1G, &ad1843_RDA1G, &ad1843_LDA1GM, &ad1843_RDA1GM
++};
++const struct ad1843_gain ad1843_gain_PCM_1 = {
++	1, &ad1843_LD2M,  &ad1843_RD2M,  &ad1843_LD2MM,  &ad1843_RD2MM
++};
++
++const struct ad1843_gain *ad1843_gain[AD1843_GAIN_SIZE] =
++{
++	&ad1843_gain_RECLEV,
++	&ad1843_gain_LINE,
++	&ad1843_gain_LINE_2,
++	&ad1843_gain_MIC,
++	&ad1843_gain_PCM_0,
++	&ad1843_gain_PCM_1,
++};
++
++/* read the current value of an AD1843 bitfield. */
++
++static int ad1843_read_bits(struct snd_ad1843 *ad1843,
++			    const struct ad1843_bitfield *field)
++{
++	int w;
++
++	w = ad1843->read(ad1843->chip, field->reg);
++	return w >> field->lo_bit & ((1 << field->nbits) - 1);
++}
++
++/*
++ * write a new value to an AD1843 bitfield and return the old value.
++ */
++
++static int ad1843_write_bits(struct snd_ad1843 *ad1843,
++			     const struct ad1843_bitfield *field,
++			     int newval)
++{
++	int w, mask, oldval, newbits;
++
++	w = ad1843->read(ad1843->chip, field->reg);
++	mask = ((1 << field->nbits) - 1) << field->lo_bit;
++	oldval = (w & mask) >> field->lo_bit;
++	newbits = (newval << field->lo_bit) & mask;
++	w = (w & ~mask) | newbits;
++	ad1843->write(ad1843->chip, field->reg, w);
++
++	return oldval;
++}
++
++/*
++ * ad1843_read_multi reads multiple bitfields from the same AD1843
++ * register.  It uses a single read cycle to do it.  (Reading the
++ * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20
++ * microseconds.)
++ *
++ * Called like this.
++ *
++ *  ad1843_read_multi(ad1843, nfields,
++ *		      &ad1843_FIELD1, &val1,
++ *		      &ad1843_FIELD2, &val2, ...);
++ */
++
++static void ad1843_read_multi(struct snd_ad1843 *ad1843, int argcount, ...)
++{
++	va_list ap;
++	const struct ad1843_bitfield *fp;
++	int w = 0, mask, *value, reg = -1;
++
++	va_start(ap, argcount);
++	while (--argcount >= 0) {
++		fp = va_arg(ap, const struct ad1843_bitfield *);
++		value = va_arg(ap, int *);
++		if (reg == -1) {
++			reg = fp->reg;
++			w = ad1843->read(ad1843->chip, reg);
++		}
++
++		mask = (1 << fp->nbits) - 1;
++		*value = w >> fp->lo_bit & mask;
++	}
++	va_end(ap);
++}
++
++/*
++ * ad1843_write_multi stores multiple bitfields into the same AD1843
++ * register.  It uses one read and one write cycle to do it.
++ *
++ * Called like this.
++ *
++ *  ad1843_write_multi(ad1843, nfields,
++ *		       &ad1843_FIELD1, val1,
++ *		       &ad1843_FIELF2, val2, ...);
++ */
++
++static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...)
++{
++	va_list ap;
++	int reg;
++	const struct ad1843_bitfield *fp;
++	int value;
++	int w, m, mask, bits;
++
++	mask = 0;
++	bits = 0;
++	reg = -1;
++
++	va_start(ap, argcount);
++	while (--argcount >= 0) {
++		fp = va_arg(ap, const struct ad1843_bitfield *);
++		value = va_arg(ap, int);
++		if (reg == -1)
++			reg = fp->reg;
++		else
++			BUG_ON(reg != fp->reg);
++		m = ((1 << fp->nbits) - 1) << fp->lo_bit;
++		mask |= m;
++		bits |= (value << fp->lo_bit) & m;
++	}
++	va_end(ap);
++
++	if (~mask & 0xFFFF)
++		w = ad1843->read(ad1843->chip, reg);
++	else
++		w = 0;
++	w = (w & ~mask) | bits;
++	ad1843->write(ad1843->chip, reg, w);
++}
++
++int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id)
++{
++	const struct ad1843_gain *gp = ad1843_gain[id];
++	int ret;
++
++	ret = (1 << gp->lfield->nbits);
++	if (!gp->lmute)
++		ret -= 1;
++	return ret;
++}
++
++/*
++ * ad1843_get_gain reads the specified register and extracts the gain value
++ * using the supplied gain type.
++ */
++
++int ad1843_get_gain(struct snd_ad1843 *ad1843, int id)
++{
++	int lg, rg, lm, rm;
++	const struct ad1843_gain *gp = ad1843_gain[id];
++	unsigned short mask = (1 << gp->lfield->nbits) - 1;
++
++	ad1843_read_multi(ad1843, 2, gp->lfield, &lg, gp->rfield, &rg);
++	if (gp->negative) {
++		lg = mask - lg;
++		rg = mask - rg;
++	}
++	if (gp->lmute) {
++		ad1843_read_multi(ad1843, 2, gp->lmute, &lm, gp->rmute, &rm);
++		if (lm)
++			lg = 0;
++		if (rm)
++			rg = 0;
++	}
++	return lg << 0 | rg << 8;
++}
++
++/*
++ * Set an audio channel's gain.
++ *
++ * Returns the new gain, which may be lower than the old gain.
++ */
++
++int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval)
++{
++	const struct ad1843_gain *gp = ad1843_gain[id];
++	unsigned short mask = (1 << gp->lfield->nbits) - 1;
++
++	int lg = (newval >> 0) & mask;
++	int rg = (newval >> 8) & mask;
++	int lm = (lg == 0) ? 1 : 0;
++	int rm = (rg == 0) ? 1 : 0;
++
++	if (gp->negative) {
++		lg = mask - lg;
++		rg = mask - rg;
++	}
++	if (gp->lmute)
++		ad1843_write_multi(ad1843, 2, gp->lmute, lm, gp->rmute, rm);
++	ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg);
++	return ad1843_get_gain(ad1843, id);
++}
++
++/* Returns the current recording source */
++
++int ad1843_get_recsrc(struct snd_ad1843 *ad1843)
++{
++	int val = ad1843_read_bits(ad1843, &ad1843_LSS);
++
++	if (val < 0 || val > 2) {
++		val = 2;
++		ad1843_write_multi(ad1843, 2,
++				   &ad1843_LSS, val, &ad1843_RSS, val);
++	}
++	return val;
++}
++
++/*
++ * Set recording source.
++ *
++ * Returns newsrc on success, -errno on failure.
++ */
++
++int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc)
++{
++	if (newsrc < 0 || newsrc > 2)
++		return -EINVAL;
++
++	ad1843_write_multi(ad1843, 2, &ad1843_LSS, newsrc, &ad1843_RSS, newsrc);
++	return newsrc;
++}
++
++/* Setup ad1843 for D/A conversion. */
++
++void ad1843_setup_dac(struct snd_ad1843 *ad1843,
++		      unsigned int id,
++		      unsigned int framerate,
++		      snd_pcm_format_t fmt,
++		      unsigned int channels)
++{
++	int ad_fmt = 0, ad_mode = 0;
++
++	switch (fmt) {
++	case SNDRV_PCM_FORMAT_S8:
++		ad_fmt = 0;
++		break;
++	case SNDRV_PCM_FORMAT_U8:
++		ad_fmt = 0;
++		break;
++	case SNDRV_PCM_FORMAT_S16_LE:
++		ad_fmt = 1;
++		break;
++	case SNDRV_PCM_FORMAT_MU_LAW:
++		ad_fmt = 2;
++		break;
++	case SNDRV_PCM_FORMAT_A_LAW:
++		ad_fmt = 3;
++		break;
++	default:
++		break;
++	}
++
++	switch (channels) {
++	case 2:
++		ad_mode = 0;
++		break;
++	case 1:
++		ad_mode = 1;
++		break;
++	default:
++		break;
++	}
++
++	if (id) {
++		ad1843_write_bits(ad1843, &ad1843_C2C, framerate);
++		ad1843_write_multi(ad1843, 2,
++				   &ad1843_DA2SM, ad_mode,
++				   &ad1843_DA2F, ad_fmt);
++	} else {
++		ad1843_write_bits(ad1843, &ad1843_C1C, framerate);
++		ad1843_write_multi(ad1843, 2,
++				   &ad1843_DA1SM, ad_mode,
++				   &ad1843_DA1F, ad_fmt);
++	}
++}
++
++void ad1843_shutdown_dac(struct snd_ad1843 *ad1843, unsigned int id)
++{
++	if (id)
++		ad1843_write_bits(ad1843, &ad1843_DA2F, 1);
++	else
++		ad1843_write_bits(ad1843, &ad1843_DA1F, 1);
++}
++
++void ad1843_setup_adc(struct snd_ad1843 *ad1843,
++		      unsigned int framerate,
++		      snd_pcm_format_t fmt,
++		      unsigned int channels)
++{
++	int da_fmt = 0;
++
++	switch (fmt) {
++	case SNDRV_PCM_FORMAT_S8:	da_fmt = 0; break;
++	case SNDRV_PCM_FORMAT_U8:	da_fmt = 0; break;
++	case SNDRV_PCM_FORMAT_S16_LE:	da_fmt = 1; break;
++	case SNDRV_PCM_FORMAT_MU_LAW:	da_fmt = 2; break;
++	case SNDRV_PCM_FORMAT_A_LAW:	da_fmt = 3; break;
++	default:		break;
++	}
++
++	ad1843_write_bits(ad1843, &ad1843_C3C, framerate);
++	ad1843_write_multi(ad1843, 2,
++			   &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt);
++}
++
++void ad1843_shutdown_adc(struct snd_ad1843 *ad1843)
++{
++	/* nothing to do */
++}
++
++/*
++ * Fully initialize the ad1843.  As described in the AD1843 data
++ * sheet, section "START-UP SEQUENCE".  The numbered comments are
++ * subsection headings from the data sheet.  See the data sheet, pages
++ * 52-54, for more info.
++ *
++ * return 0 on success, -errno on failure.  */
++
++int ad1843_init(struct snd_ad1843 *ad1843)
++{
++	unsigned long later;
++
++	if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) {
++		printk(KERN_ERR "ad1843: AD1843 won't initialize\n");
++		return -EIO;
++	}
++
++	ad1843_write_bits(ad1843, &ad1843_SCF, 1);
++
++	/* 4. Put the conversion resources into standby. */
++	ad1843_write_bits(ad1843, &ad1843_PDNI, 0);
++	later = jiffies + HZ / 2;	/* roughly half a second */
++
++	while (ad1843_read_bits(ad1843, &ad1843_PDNO)) {
++		if (time_after(jiffies, later)) {
++			printk(KERN_ERR
++			       "ad1843: AD1843 won't power up\n");
++			return -EIO;
++		}
++		schedule();
++	}
++
++	/* 5. Power up the clock generators and enable clock output pins. */
++	ad1843_write_multi(ad1843, 3,
++			   &ad1843_C1EN, 1,
++			   &ad1843_C2EN, 1,
++			   &ad1843_C3EN, 1);
++
++	/* 6. Configure conversion resources while they are in standby. */
++
++	/* DAC1/2 use clock 1/2 as source, ADC uses clock 3.  Always. */
++	ad1843_write_multi(ad1843, 4,
++			   &ad1843_DA1C, 1,
++			   &ad1843_DA2C, 2,
++			   &ad1843_ADLC, 3,
++			   &ad1843_ADRC, 3);
++
++	/* 7. Enable conversion resources. */
++	ad1843_write_bits(ad1843, &ad1843_ADTLK, 1);
++	ad1843_write_multi(ad1843, 7,
++			   &ad1843_ANAEN, 1,
++			   &ad1843_AAMEN, 1,
++			   &ad1843_DA1EN, 1,
++			   &ad1843_DA2EN, 1,
++			   &ad1843_DDMEN, 1,
++			   &ad1843_ADLEN, 1,
++			   &ad1843_ADREN, 1);
++
++	/* 8. Configure conversion resources while they are enabled. */
++
++	/* set gain to 0 for all channels */
++	ad1843_set_gain(ad1843, AD1843_GAIN_RECLEV, 0);
++	ad1843_set_gain(ad1843, AD1843_GAIN_LINE, 0);
++	ad1843_set_gain(ad1843, AD1843_GAIN_LINE_2, 0);
++	ad1843_set_gain(ad1843, AD1843_GAIN_MIC, 0);
++	ad1843_set_gain(ad1843, AD1843_GAIN_PCM_0, 0);
++	ad1843_set_gain(ad1843, AD1843_GAIN_PCM_1, 0);
++
++	/* Unmute all channels. */
++	/* DAC1 */
++	ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0);
++	/* DAC2 */
++	ad1843_write_multi(ad1843, 2, &ad1843_LDA2GM, 0, &ad1843_RDA2GM, 0);
++
++	/* Set default recording source to Line In and set
++	 * mic gain to +20 dB.
++	 */
++	ad1843_set_recsrc(ad1843, 2);
++	ad1843_write_multi(ad1843, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);
++
++	/* Set Speaker Out level to +/- 4V and unmute it. */
++	ad1843_write_multi(ad1843, 3,
++			   &ad1843_HPOS, 1,
++			   &ad1843_HPOM, 0,
++			   &ad1843_MPOM, 0);
++
++	return 0;
++}
+diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
+new file mode 100644
+index 0000000..6630af3
+--- /dev/null
++++ b/sound/mips/sgio2audio.c
+@@ -0,0 +1,1013 @@
++/*
++ *   Sound driver for Silicon Graphics O2 Workstations A/V board audio.
++ *
++ *   Copyright 2003 Vivien Chappelier <vivien.chappelier at linux-mips.org>
++ *   Copyright 2008 Thomas Bogendoerfer <tsbogend at alpha.franken.de>
++ *   Mxier part taken from mace_audio.c:
++ *   Copyright 2007 Thorben Jändling <tj.trevelyan at gmail.com>
++ *
++ *   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 <linux/init.h>
++#include <linux/delay.h>
++#include <linux/spinlock.h>
++#include <linux/gfp.h>
++#include <linux/vmalloc.h>
++#include <linux/interrupt.h>
++#include <linux/dma-mapping.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++
++#include <asm/ip32/ip32_ints.h>
++#include <asm/ip32/mace.h>
++
++#include <sound/core.h>
++#include <sound/control.h>
++#include <sound/pcm.h>
++#define SNDRV_GET_ID
++#include <sound/initval.h>
++#include <sound/ad1843.h>
++
++
++MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier at linux-mips.org>");
++MODULE_DESCRIPTION("SGI O2 Audio");
++MODULE_LICENSE("GPL");
++MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}");
++
++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 SGI O2 soundcard.");
++module_param(id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for SGI O2 soundcard.");
++
++
++#define SGIO2AUDIO_MAX_VOLUME 31
++
++#define AUDIO_CONTROL_RESET              BIT(0) /* 1: reset audio interface */
++#define AUDIO_CONTROL_CODEC_PRESENT      BIT(1) /* 1: codec detected */
++
++#define CODEC_CONTROL_WORD_SHIFT        0
++#define CODEC_CONTROL_READ              BIT(16)
++#define CODEC_CONTROL_ADDRESS_SHIFT     17
++
++#define CHANNEL_CONTROL_RESET           BIT(10) /* 1: reset channel */
++#define CHANNEL_DMA_ENABLE              BIT(9)  /* 1: enable DMA transfer */
++#define CHANNEL_INT_THRESHOLD_DISABLED  (0 << 5) /* interrupt disabled */
++#define CHANNEL_INT_THRESHOLD_25        (1 << 5) /* int on buffer >25% full */
++#define CHANNEL_INT_THRESHOLD_50        (2 << 5) /* int on buffer >50% full */
++#define CHANNEL_INT_THRESHOLD_75        (3 << 5) /* int on buffer >75% full */
++#define CHANNEL_INT_THRESHOLD_EMPTY     (4 << 5) /* int on buffer empty */
++#define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */
++#define CHANNEL_INT_THRESHOLD_FULL      (6 << 5) /* int on buffer empty */
++#define CHANNEL_INT_THRESHOLD_NOT_FULL  (7 << 5) /* int on buffer !empty */
++
++#define CHANNEL_RING_SHIFT              12
++#define CHANNEL_RING_SIZE               (1 << CHANNEL_RING_SHIFT)
++#define CHANNEL_RING_MASK               (CHANNEL_RING_SIZE - 1)
++
++#define CHANNEL_LEFT_SHIFT 40
++#define CHANNEL_RIGHT_SHIFT 8
++
++struct snd_sgio2audio_chan {
++	int idx;
++	struct snd_pcm_substream *substream;
++	int pos;
++	snd_pcm_uframes_t size;
++	spinlock_t lock;
++};
++
++/* definition of the chip-specific record */
++struct snd_sgio2audio {
++	struct snd_card *card;
++
++	/* codec */
++	struct snd_ad1843 ad1843;
++	spinlock_t ad1843_lock;
++
++	/* channels */
++	struct snd_sgio2audio_chan channel[3];
++
++	/* properties */
++	int volume;
++
++	/* resources */
++	void *ring_base;
++	dma_addr_t ring_base_dma;
++};
++
++/* AD1843 access */
++
++/*
++ * read_ad1843_reg returns the current contents of a 16 bit AD1843 register.
++ *
++ * Returns unsigned register value on success, -errno on failure.
++ */
++static int read_ad1843_reg(void *priv, int reg)
++{
++	struct snd_sgio2audio *chip = priv;
++	int val;
++	unsigned long flags;
++
++	spin_lock_irqsave(&chip->ad1843_lock, flags);
++
++	writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
++	       CODEC_CONTROL_READ, &mace->perif.audio.codec_control);
++	wmb();
++	val = readq(&mace->perif.audio.codec_control); /* flush bus */
++	udelay(200);
++
++	val = readq(&mace->perif.audio.codec_read);
++
++	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
++	return val;
++}
++
++/*
++ * write_ad1843_reg writes the specified value to a 16 bit AD1843 register.
++ */
++static int write_ad1843_reg(void *priv, int reg, int word)
++{
++	struct snd_sgio2audio *chip = priv;
++	int val;
++	unsigned long flags;
++
++	spin_lock_irqsave(&chip->ad1843_lock, flags);
++
++	writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) |
++	       (word << CODEC_CONTROL_WORD_SHIFT),
++	       &mace->perif.audio.codec_control);
++	wmb();
++	val = readq(&mace->perif.audio.codec_control); /* flush bus */
++	udelay(200);
++
++	spin_unlock_irqrestore(&chip->ad1843_lock, flags);
++	return 0;
++}
++
++static int sgio2audio_gain_info(struct snd_kcontrol *kcontrol,
++			       struct snd_ctl_elem_info *uinfo)
++{
++	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
++
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++	uinfo->count = 2;
++	uinfo->value.integer.min = 0;
++	uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843,
++					     (int)kcontrol->private_value);
++	return 0;
++}
++
++static int sgio2audio_gain_get(struct snd_kcontrol *kcontrol,
++			       struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
++	int vol;
++
++	vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value);
++
++	ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF;
++	ucontrol->value.integer.value[1] = vol & 0xFF;
++
++	return 0;
++}
++
++static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol,
++			struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
++	int newvol, oldvol;
++
++	oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value);
++	newvol = (ucontrol->value.integer.value[0] << 8) |
++		ucontrol->value.integer.value[1];
++
++	newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value,
++		newvol);
++
++	return newvol != oldvol;
++}
++
++static int sgio2audio_source_info(struct snd_kcontrol *kcontrol,
++			       struct snd_ctl_elem_info *uinfo)
++{
++	static const char *texts[3] = {
++		"Cam Mic", "Mic", "Line"
++	};
++	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
++	uinfo->count = 1;
++	uinfo->value.enumerated.items = 3;
++	if (uinfo->value.enumerated.item >= 3)
++		uinfo->value.enumerated.item = 1;
++	strcpy(uinfo->value.enumerated.name,
++	       texts[uinfo->value.enumerated.item]);
++	return 0;
++}
++
++static int sgio2audio_source_get(struct snd_kcontrol *kcontrol,
++			       struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
++
++	ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843);
++	return 0;
++}
++
++static int sgio2audio_source_put(struct snd_kcontrol *kcontrol,
++			struct snd_ctl_elem_value *ucontrol)
++{
++	struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol);
++	int newsrc, oldsrc;
++
++	oldsrc = ad1843_get_recsrc(&chip->ad1843);
++	newsrc = ad1843_set_recsrc(&chip->ad1843,
++				   ucontrol->value.enumerated.item[0]);
++
++	return newsrc != oldsrc;
++}
++
++/* dac1/pcm0 mixer control */
++static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = {
++	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name           = "PCM Playback Volume",
++	.index          = 0,
++	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.private_value  = AD1843_GAIN_PCM_0,
++	.info           = sgio2audio_gain_info,
++	.get            = sgio2audio_gain_get,
++	.put            = sgio2audio_gain_put,
++};
++
++/* dac2/pcm1 mixer control */
++static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = {
++	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name           = "PCM Playback Volume",
++	.index          = 1,
++	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.private_value  = AD1843_GAIN_PCM_1,
++	.info           = sgio2audio_gain_info,
++	.get            = sgio2audio_gain_get,
++	.put            = sgio2audio_gain_put,
++};
++
++/* record level mixer control */
++static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = {
++	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name           = "Capture Volume",
++	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.private_value  = AD1843_GAIN_RECLEV,
++	.info           = sgio2audio_gain_info,
++	.get            = sgio2audio_gain_get,
++	.put            = sgio2audio_gain_put,
++};
++
++/* record level source control */
++static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = {
++	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name           = "Capture Source",
++	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.info           = sgio2audio_source_info,
++	.get            = sgio2audio_source_get,
++	.put            = sgio2audio_source_put,
++};
++
++/* line mixer control */
++static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = {
++	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name           = "Line Playback Volume",
++	.index          = 0,
++	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.private_value  = AD1843_GAIN_LINE,
++	.info           = sgio2audio_gain_info,
++	.get            = sgio2audio_gain_get,
++	.put            = sgio2audio_gain_put,
++};
++
++/* cd mixer control */
++static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = {
++	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name           = "Line Playback Volume",
++	.index          = 1,
++	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.private_value  = AD1843_GAIN_LINE_2,
++	.info           = sgio2audio_gain_info,
++	.get            = sgio2audio_gain_get,
++	.put            = sgio2audio_gain_put,
++};
++
++/* mic mixer control */
++static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = {
++	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
++	.name           = "Mic Playback Volume",
++	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
++	.private_value  = AD1843_GAIN_MIC,
++	.info           = sgio2audio_gain_info,
++	.get            = sgio2audio_gain_get,
++	.put            = sgio2audio_gain_put,
++};
++
++
++static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip)
++{
++	int err;
++
++	err = snd_ctl_add(chip->card,
++			  snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip));
++	if (err < 0)
++		return err;
++
++	err = snd_ctl_add(chip->card,
++			  snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip));
++	if (err < 0)
++		return err;
++
++	err = snd_ctl_add(chip->card,
++			  snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip));
++	if (err < 0)
++		return err;
++
++	err = snd_ctl_add(chip->card,
++			  snd_ctl_new1(&sgio2audio_ctrl_recsource, chip));
++	if (err < 0)
++		return err;
++	err = snd_ctl_add(chip->card,
++			  snd_ctl_new1(&sgio2audio_ctrl_line, chip));
++	if (err < 0)
++		return err;
++
++	err = snd_ctl_add(chip->card,
++			  snd_ctl_new1(&sgio2audio_ctrl_cd, chip));
++	if (err < 0)
++		return err;
++
++	err = snd_ctl_add(chip->card,
++			  snd_ctl_new1(&sgio2audio_ctrl_mic, chip));
++	if (err < 0)
++		return err;
++
++	return 0;
++}
++
++/* low-level audio interface DMA */
++
++/* get data out of bounce buffer, count must be a multiple of 32 */
++/* returns 1 if a period has elapsed */
++static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip,
++					unsigned int ch, unsigned int count)
++{
++	int ret;
++	unsigned long src_base, src_pos, dst_mask;
++	unsigned char *dst_base;
++	int dst_pos;
++	u64 *src;
++	s16 *dst;
++	u64 x;
++	unsigned long flags;
++	struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
++
++	spin_lock_irqsave(&chip->channel[ch].lock, flags);
++
++	src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT);
++	src_pos = readq(&mace->perif.audio.chan[ch].read_ptr);
++	dst_base = runtime->dma_area;
++	dst_pos = chip->channel[ch].pos;
++	dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
++
++	/* check if a period has elapsed */
++	chip->channel[ch].size += (count >> 3); /* in frames */
++	ret = chip->channel[ch].size >= runtime->period_size;
++	chip->channel[ch].size %= runtime->period_size;
++
++	while (count) {
++		src = (u64 *)(src_base + src_pos);
++		dst = (s16 *)(dst_base + dst_pos);
++
++		x = *src;
++		dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff;
++		dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff;
++
++		src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK;
++		dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask;
++		count -= sizeof(u64);
++	}
++
++	writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */
++	chip->channel[ch].pos = dst_pos;
++
++	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
++	return ret;
++}
++
++/* put some DMA data in bounce buffer, count must be a multiple of 32 */
++/* returns 1 if a period has elapsed */
++static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip,
++					unsigned int ch, unsigned int count)
++{
++	int ret;
++	s64 l, r;
++	unsigned long dst_base, dst_pos, src_mask;
++	unsigned char *src_base;
++	int src_pos;
++	u64 *dst;
++	s16 *src;
++	unsigned long flags;
++	struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime;
++
++	spin_lock_irqsave(&chip->channel[ch].lock, flags);
++
++	dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT);
++	dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr);
++	src_base = runtime->dma_area;
++	src_pos = chip->channel[ch].pos;
++	src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1;
++
++	/* check if a period has elapsed */
++	chip->channel[ch].size += (count >> 3); /* in frames */
++	ret = chip->channel[ch].size >= runtime->period_size;
++	chip->channel[ch].size %= runtime->period_size;
++
++	while (count) {
++		src = (s16 *)(src_base + src_pos);
++		dst = (u64 *)(dst_base + dst_pos);
++
++		l = src[0]; /* sign extend */
++		r = src[1]; /* sign extend */
++
++		*dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) |
++			((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT);
++
++		dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK;
++		src_pos = (src_pos + 2 * sizeof(s16)) & src_mask;
++		count -= sizeof(u64);
++	}
++
++	writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */
++	chip->channel[ch].pos = src_pos;
++
++	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
++	return ret;
++}
++
++static int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream)
++{
++	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
++	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
++	int ch = chan->idx;
++
++	/* reset DMA channel */
++	writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control);
++	udelay(10);
++	writeq(0, &mace->perif.audio.chan[ch].control);
++
++	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++		/* push a full buffer */
++		snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32);
++	}
++	/* set DMA to wake on 50% empty and enable interrupt */
++	writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50,
++	       &mace->perif.audio.chan[ch].control);
++	return 0;
++}
++
++static int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream)
++{
++	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
++
++	writeq(0, &mace->perif.audio.chan[chan->idx].control);
++	return 0;
++}
++
++static irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id)
++{
++	struct snd_sgio2audio_chan *chan = dev_id;
++	struct snd_pcm_substream *substream;
++	struct snd_sgio2audio *chip;
++	int count, ch;
++
++	substream = chan->substream;
++	chip = snd_pcm_substream_chip(substream);
++	ch = chan->idx;
++
++	/* empty the ring */
++	count = CHANNEL_RING_SIZE -
++		readq(&mace->perif.audio.chan[ch].depth) - 32;
++	if (snd_sgio2audio_dma_pull_frag(chip, ch, count))
++		snd_pcm_period_elapsed(substream);
++
++	return IRQ_HANDLED;
++}
++
++static irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id)
++{
++	struct snd_sgio2audio_chan *chan = dev_id;
++	struct snd_pcm_substream *substream;
++	struct snd_sgio2audio *chip;
++	int count, ch;
++
++	substream = chan->substream;
++	chip = snd_pcm_substream_chip(substream);
++	ch = chan->idx;
++	/* fill the ring */
++	count = CHANNEL_RING_SIZE -
++		readq(&mace->perif.audio.chan[ch].depth) - 32;
++	if (snd_sgio2audio_dma_push_frag(chip, ch, count))
++		snd_pcm_period_elapsed(substream);
++
++	return IRQ_HANDLED;
++}
++
++static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id)
++{
++	struct snd_sgio2audio_chan *chan = dev_id;
++	struct snd_pcm_substream *substream;
++
++	substream = chan->substream;
++	snd_sgio2audio_dma_stop(substream);
++	snd_sgio2audio_dma_start(substream);
++	return IRQ_HANDLED;
++}
++
++/* PCM part */
++/* PCM hardware definition */
++static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = {
++	.info = (SNDRV_PCM_INFO_MMAP |
++		 SNDRV_PCM_INFO_MMAP_VALID |
++		 SNDRV_PCM_INFO_INTERLEAVED |
++		 SNDRV_PCM_INFO_BLOCK_TRANSFER),
++	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
++	.rates =            SNDRV_PCM_RATE_8000_48000,
++	.rate_min =         8000,
++	.rate_max =         48000,
++	.channels_min =     2,
++	.channels_max =     2,
++	.buffer_bytes_max = 65536,
++	.period_bytes_min = 32768,
++	.period_bytes_max = 65536,
++	.periods_min =      1,
++	.periods_max =      1024,
++};
++
++/* PCM playback open callback */
++static int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream)
++{
++	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++
++	runtime->hw = snd_sgio2audio_pcm_hw;
++	runtime->private_data = &chip->channel[1];
++	return 0;
++}
++
++static int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream)
++{
++	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++
++	runtime->hw = snd_sgio2audio_pcm_hw;
++	runtime->private_data = &chip->channel[2];
++	return 0;
++}
++
++/* PCM capture open callback */
++static int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream)
++{
++	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++
++	runtime->hw = snd_sgio2audio_pcm_hw;
++	runtime->private_data = &chip->channel[0];
++	return 0;
++}
++
++/* PCM close callback */
++static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++
++	runtime->private_data = NULL;
++	return 0;
++}
++
++
++/* hw_params callback */
++static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
++					struct snd_pcm_hw_params *hw_params)
++{
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	int size = params_buffer_bytes(hw_params);
++
++	/* alloc virtual 'dma' area */
++	if (runtime->dma_area)
++		vfree(runtime->dma_area);
++	runtime->dma_area = vmalloc(size);
++	if (runtime->dma_area == NULL)
++		return -ENOMEM;
++	runtime->dma_bytes = size;
++	return 0;
++}
++
++/* hw_free callback */
++static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
++{
++	if (substream->runtime->dma_area)
++		vfree(substream->runtime->dma_area);
++	substream->runtime->dma_area = NULL;
++	return 0;
++}
++
++/* prepare callback */
++static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream)
++{
++	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
++	struct snd_pcm_runtime *runtime = substream->runtime;
++	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
++	int ch = chan->idx;
++	unsigned long flags;
++
++	spin_lock_irqsave(&chip->channel[ch].lock, flags);
++
++	/* Setup the pseudo-dma transfer pointers.  */
++	chip->channel[ch].pos = 0;
++	chip->channel[ch].size = 0;
++	chip->channel[ch].substream = substream;
++
++	/* set AD1843 format */
++	/* hardware format is always S16_LE */
++	switch (substream->stream) {
++	case SNDRV_PCM_STREAM_PLAYBACK:
++		ad1843_setup_dac(&chip->ad1843,
++				 ch - 1,
++				 runtime->rate,
++				 SNDRV_PCM_FORMAT_S16_LE,
++				 runtime->channels);
++		break;
++	case SNDRV_PCM_STREAM_CAPTURE:
++		ad1843_setup_adc(&chip->ad1843,
++				 runtime->rate,
++				 SNDRV_PCM_FORMAT_S16_LE,
++				 runtime->channels);
++		break;
++	}
++	spin_unlock_irqrestore(&chip->channel[ch].lock, flags);
++	return 0;
++}
++
++/* trigger callback */
++static int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream,
++				      int cmd)
++{
++	switch (cmd) {
++	case SNDRV_PCM_TRIGGER_START:
++		/* start the PCM engine */
++		snd_sgio2audio_dma_start(substream);
++		break;
++	case SNDRV_PCM_TRIGGER_STOP:
++		/* stop the PCM engine */
++		snd_sgio2audio_dma_stop(substream);
++		break;
++	default:
++		return -EINVAL;
++	}
++	return 0;
++}
++
++/* pointer callback */
++static snd_pcm_uframes_t
++snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
++{
++	struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream);
++	struct snd_sgio2audio_chan *chan = substream->runtime->private_data;
++
++	/* get the current hardware pointer */
++	return bytes_to_frames(substream->runtime,
++			       chip->channel[chan->idx].pos);
++}
++
++/* get the physical page pointer on the given offset */
++static struct page *snd_sgio2audio_page(struct snd_pcm_substream *substream,
++					unsigned long offset)
++{
++	return vmalloc_to_page(substream->runtime->dma_area + offset);
++}
++
++/* operators */
++static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
++	.open =        snd_sgio2audio_playback1_open,
++	.close =       snd_sgio2audio_pcm_close,
++	.ioctl =       snd_pcm_lib_ioctl,
++	.hw_params =   snd_sgio2audio_pcm_hw_params,
++	.hw_free =     snd_sgio2audio_pcm_hw_free,
++	.prepare =     snd_sgio2audio_pcm_prepare,
++	.trigger =     snd_sgio2audio_pcm_trigger,
++	.pointer =     snd_sgio2audio_pcm_pointer,
++	.page =        snd_sgio2audio_page,
++};
++
++static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
++	.open =        snd_sgio2audio_playback2_open,
++	.close =       snd_sgio2audio_pcm_close,
++	.ioctl =       snd_pcm_lib_ioctl,
++	.hw_params =   snd_sgio2audio_pcm_hw_params,
++	.hw_free =     snd_sgio2audio_pcm_hw_free,
++	.prepare =     snd_sgio2audio_pcm_prepare,
++	.trigger =     snd_sgio2audio_pcm_trigger,
++	.pointer =     snd_sgio2audio_pcm_pointer,
++	.page =        snd_sgio2audio_page,
++};
++
++static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
++	.open =        snd_sgio2audio_capture_open,
++	.close =       snd_sgio2audio_pcm_close,
++	.ioctl =       snd_pcm_lib_ioctl,
++	.hw_params =   snd_sgio2audio_pcm_hw_params,
++	.hw_free =     snd_sgio2audio_pcm_hw_free,
++	.prepare =     snd_sgio2audio_pcm_prepare,
++	.trigger =     snd_sgio2audio_pcm_trigger,
++	.pointer =     snd_sgio2audio_pcm_pointer,
++	.page =        snd_sgio2audio_page,
++};
++
++/*
++ *  definitions of capture are omitted here...
++ */
++
++/* create a pcm device */
++static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip)
++{
++	struct snd_pcm *pcm;
++	int err;
++
++	/* create first pcm device with one outputs and one input */
++	err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm);
++	if (err < 0)
++		return err;
++
++	pcm->private_data = chip;
++	strcpy(pcm->name, "SGI O2 DAC1");
++
++	/* set operators */
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++			&snd_sgio2audio_playback1_ops);
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++			&snd_sgio2audio_capture_ops);
++
++	/* create second  pcm device with one outputs and no input */
++	err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm);
++	if (err < 0)
++		return err;
++
++	pcm->private_data = chip;
++	strcpy(pcm->name, "SGI O2 DAC2");
++
++	/* set operators */
++	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++			&snd_sgio2audio_playback2_ops);
++
++	return 0;
++}
++
++static struct {
++	int idx;
++	int irq;
++	irqreturn_t (*isr)(int, void *);
++	const char *desc;
++} snd_sgio2_isr_table[] = {
++	{
++		.idx = 0,
++		.irq = MACEISA_AUDIO1_DMAT_IRQ,
++		.isr = snd_sgio2audio_dma_in_isr,
++		.desc = "Capture DMA Channel 0"
++	}, {
++		.idx = 0,
++		.irq = MACEISA_AUDIO1_OF_IRQ,
++		.isr = snd_sgio2audio_error_isr,
++		.desc = "Capture Overflow"
++	}, {
++		.idx = 1,
++		.irq = MACEISA_AUDIO2_DMAT_IRQ,
++		.isr = snd_sgio2audio_dma_out_isr,
++		.desc = "Playback DMA Channel 1"
++	}, {
++		.idx = 1,
++		.irq = MACEISA_AUDIO2_MERR_IRQ,
++		.isr = snd_sgio2audio_error_isr,
++		.desc = "Memory Error Channel 1"
++	}, {
++		.idx = 2,
++		.irq = MACEISA_AUDIO3_DMAT_IRQ,
++		.isr = snd_sgio2audio_dma_out_isr,
++		.desc = "Playback DMA Channel 2"
++	}, {
++		.idx = 2,
++		.irq = MACEISA_AUDIO3_MERR_IRQ,
++		.isr = snd_sgio2audio_error_isr,
++		.desc = "Memory Error Channel 2"
++	}
++};
++
++/* ALSA driver */
++
++static int snd_sgio2audio_free(struct snd_sgio2audio *chip)
++{
++	int i;
++
++	/* reset interface */
++	writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
++	udelay(1);
++	writeq(0, &mace->perif.audio.control);
++
++	/* release IRQ's */
++	for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++)
++		free_irq(snd_sgio2_isr_table[i].irq,
++			 &chip->channel[snd_sgio2_isr_table[i].idx]);
++
++	dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
++			  chip->ring_base, chip->ring_base_dma);
++
++	/* release card data */
++	kfree(chip);
++	return 0;
++}
++
++static int snd_sgio2audio_dev_free(struct snd_device *device)
++{
++	struct snd_sgio2audio *chip = device->device_data;
++
++	return snd_sgio2audio_free(chip);
++}
++
++static struct snd_device_ops ops = {
++	.dev_free = snd_sgio2audio_dev_free,
++};
++
++static int __devinit snd_sgio2audio_create(struct snd_card *card,
++					   struct snd_sgio2audio **rchip)
++{
++	struct snd_sgio2audio *chip;
++	int i, err;
++
++	*rchip = NULL;
++
++	/* check if a codec is attached to the interface */
++	/* (Audio or Audio/Video board present) */
++	if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT))
++		return -ENOENT;
++
++	chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL);
++	if (chip == NULL)
++		return -ENOMEM;
++
++	chip->card = card;
++
++	chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE,
++					     &chip->ring_base_dma, GFP_USER);
++	if (chip->ring_base == NULL) {
++		printk(KERN_ERR
++		       "sgio2audio: could not allocate ring buffers\n");
++		kfree(chip);
++		return -ENOMEM;
++	}
++
++	spin_lock_init(&chip->ad1843_lock);
++
++	chip->volume = SGIO2AUDIO_MAX_VOLUME;
++
++	/* initialize channels */
++	for (i = 0; i < 3; i++) {
++		spin_lock_init(&chip->channel[i].lock);
++		chip->channel[i].idx = i;
++	}
++
++	/* allocate IRQs */
++	for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) {
++		if (request_irq(snd_sgio2_isr_table[i].irq,
++				snd_sgio2_isr_table[i].isr,
++				IRQF_SHARED,
++				snd_sgio2_isr_table[i].desc,
++				&chip->channel[snd_sgio2_isr_table[i].idx])) {
++			snd_sgio2audio_free(chip);
++			printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n",
++			       snd_sgio2_isr_table[i].irq);
++			return -EBUSY;
++		}
++	}
++
++	/* reset the interface */
++	writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control);
++	udelay(1);
++	writeq(0, &mace->perif.audio.control);
++	udelay(100); /* give time to recover */
++
++	/* set ring base */
++	writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase);
++
++	/* attach the AD1843 codec */
++	chip->ad1843.read = read_ad1843_reg;
++	chip->ad1843.write = write_ad1843_reg;
++	chip->ad1843.chip = chip;
++
++	/* initialize the AD1843 codec */
++	err = ad1843_init(&chip->ad1843);
++	if (err < 0) {
++		snd_sgio2audio_free(chip);
++		return err;
++	}
++
++	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
++	if (err < 0) {
++		snd_sgio2audio_free(chip);
++		return err;
++	}
++	*rchip = chip;
++	return 0;
++}
++
++static int __devinit snd_sgio2audio_probe(struct platform_device *pdev)
++{
++	struct snd_card *card;
++	struct snd_sgio2audio *chip;
++	int err;
++
++	card = snd_card_new(index, id, THIS_MODULE, 0);
++	if (card == NULL)
++		return -ENOMEM;
++
++	err = snd_sgio2audio_create(card, &chip);
++	if (err < 0) {
++		snd_card_free(card);
++		return err;
++	}
++	snd_card_set_dev(card, &pdev->dev);
++
++	err = snd_sgio2audio_new_pcm(chip);
++	if (err < 0) {
++		snd_card_free(card);
++		return err;
++	}
++	err = snd_sgio2audio_new_mixer(chip);
++	if (err < 0) {
++		snd_card_free(card);
++		return err;
++	}
++
++	strcpy(card->driver, "SGI O2 Audio");
++	strcpy(card->shortname, "SGI O2 Audio");
++	sprintf(card->longname, "%s irq %i-%i",
++		card->shortname,
++		MACEISA_AUDIO1_DMAT_IRQ,
++		MACEISA_AUDIO3_MERR_IRQ);
++
++	err = snd_card_register(card);
++	if (err < 0) {
++		snd_card_free(card);
++		return err;
++	}
++	platform_set_drvdata(pdev, card);
++	return 0;
++}
++
++static int __exit snd_sgio2audio_remove(struct platform_device *pdev)
++{
++	struct snd_card *card = platform_get_drvdata(pdev);
++
++	snd_card_free(card);
++	platform_set_drvdata(pdev, NULL);
++	return 0;
++}
++
++static struct platform_driver sgio2audio_driver = {
++	.probe	= snd_sgio2audio_probe,
++	.remove	= __devexit_p(snd_sgio2audio_remove),
++	.driver = {
++		.name	= "sgio2audio",
++		.owner	= THIS_MODULE,
++	}
++};
++
++static int __init alsa_card_sgio2audio_init(void)
++{
++	return platform_driver_register(&sgio2audio_driver);
++}
++
++static void __exit alsa_card_sgio2audio_exit(void)
++{
++	platform_driver_unregister(&sgio2audio_driver);
++}
++
++module_init(alsa_card_sgio2audio_init)
++module_exit(alsa_card_sgio2audio_exit)
+-- 
+Crap can work. Given enough thrust pigs will fly, but it's not necessary a
+good idea.                                                [ RFC1925, 2.3 ]

Modified: dists/trunk/linux-2.6/debian/patches/series/1~experimental.1
==============================================================================
--- dists/trunk/linux-2.6/debian/patches/series/1~experimental.1	(original)
+++ dists/trunk/linux-2.6/debian/patches/series/1~experimental.1	Wed Jul  9 11:07:40 2008
@@ -42,6 +42,7 @@
 + features/arm/led-pca9532-n2100.patch
 + features/arm/ts409-mv2120-support.patch
 + features/arm/ixp4xx-4874-fsg-board-support.patch
++ features/mips/ip32_sound.patch
 + features/all/at76.patch 
 + bugfix/fix-hifn_795X-divdi3.patch
 + bugfix/all/mtd-prevent-physmap-from-causing-request_module-runaway-loop-modprobe-net-pf-1.patch



More information about the Kernel-svn-changes mailing list