From 4329d2384ae6304aa29e19be8e4b1913f0c46c63 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Wed, 9 Jul 2008 11:07:40 +0000 Subject: [PATCH] add SGI O2 sound driver svn path=/dists/trunk/linux-2.6/; revision=11816 --- debian/changelog | 3 +- debian/config/mips/config.r5k-ip32 | 5 +- debian/patches/features/mips/ip32_sound.patch | 1914 +++++++++++++++++ debian/patches/series/1~experimental.1 | 1 + 4 files changed, 1921 insertions(+), 2 deletions(-) create mode 100644 debian/patches/features/mips/ip32_sound.patch diff --git a/debian/changelog b/debian/changelog index aac5aea1f..4ff24349c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -88,9 +88,10 @@ linux-2.6 (2.6.26~rc9-1~experimental.1) UNRELEASED; urgency=low 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 diff --git a/debian/config/mips/config.r5k-ip32 b/debian/config/mips/config.r5k-ip32 index 43fd7d82c..2f53cc4d5 100644 --- a/debian/config/mips/config.r5k-ip32 +++ b/debian/config/mips/config.r5k-ip32 @@ -286,7 +286,8 @@ CONFIG_KEYBOARD_ATKBD=y ## ## 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_KORG1212=m # CONFIG_SND_VX222 is not set # CONFIG_SND_YMFPCI is not set +CONFIG_SND_SGI_O2=m + diff --git a/debian/patches/features/mips/ip32_sound.patch b/debian/patches/features/mips/ip32_sound.patch new file mode 100644 index 000000000..391ae42e8 --- /dev/null +++ b/debian/patches/features/mips/ip32_sound.patch @@ -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 "); + 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 ++ * Copyright 2008 Thomas Bogendoerfer ++ */ ++ ++#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 ++ * Copyright 2008 Thomas Bogendoerfer ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 ++ * Copyright 2008 Thomas Bogendoerfer ++ * Mxier part taken from mace_audio.c: ++ * Copyright 2007 Thorben Jändling ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#define SNDRV_GET_ID ++#include ++#include ++ ++ ++MODULE_AUTHOR("Vivien Chappelier "); ++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 ] diff --git a/debian/patches/series/1~experimental.1 b/debian/patches/series/1~experimental.1 index 29a58ed20..198ac5b8f 100644 --- a/debian/patches/series/1~experimental.1 +++ b/debian/patches/series/1~experimental.1 @@ -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