1862c2c0aSThomas Bogendoerfer /* 2862c2c0aSThomas Bogendoerfer * Sound driver for Silicon Graphics O2 Workstations A/V board audio. 3862c2c0aSThomas Bogendoerfer * 4862c2c0aSThomas Bogendoerfer * Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org> 5862c2c0aSThomas Bogendoerfer * Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de> 6862c2c0aSThomas Bogendoerfer * Mxier part taken from mace_audio.c: 7862c2c0aSThomas Bogendoerfer * Copyright 2007 Thorben Jändling <tj.trevelyan@gmail.com> 8862c2c0aSThomas Bogendoerfer * 9862c2c0aSThomas Bogendoerfer * This program is free software; you can redistribute it and/or modify 10862c2c0aSThomas Bogendoerfer * it under the terms of the GNU General Public License as published by 11862c2c0aSThomas Bogendoerfer * the Free Software Foundation; either version 2 of the License, or 12862c2c0aSThomas Bogendoerfer * (at your option) any later version. 13862c2c0aSThomas Bogendoerfer * 14862c2c0aSThomas Bogendoerfer * This program is distributed in the hope that it will be useful, 15862c2c0aSThomas Bogendoerfer * but WITHOUT ANY WARRANTY; without even the implied warranty of 16862c2c0aSThomas Bogendoerfer * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17862c2c0aSThomas Bogendoerfer * GNU General Public License for more details. 18862c2c0aSThomas Bogendoerfer * 19862c2c0aSThomas Bogendoerfer * You should have received a copy of the GNU General Public License 20862c2c0aSThomas Bogendoerfer * along with this program; if not, write to the Free Software 21862c2c0aSThomas Bogendoerfer * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22862c2c0aSThomas Bogendoerfer * 23862c2c0aSThomas Bogendoerfer */ 24862c2c0aSThomas Bogendoerfer 25862c2c0aSThomas Bogendoerfer #include <linux/init.h> 26862c2c0aSThomas Bogendoerfer #include <linux/delay.h> 27862c2c0aSThomas Bogendoerfer #include <linux/spinlock.h> 28862c2c0aSThomas Bogendoerfer #include <linux/gfp.h> 29862c2c0aSThomas Bogendoerfer #include <linux/interrupt.h> 30862c2c0aSThomas Bogendoerfer #include <linux/dma-mapping.h> 31862c2c0aSThomas Bogendoerfer #include <linux/platform_device.h> 32862c2c0aSThomas Bogendoerfer #include <linux/io.h> 33862c2c0aSThomas Bogendoerfer 34862c2c0aSThomas Bogendoerfer #include <asm/ip32/ip32_ints.h> 35862c2c0aSThomas Bogendoerfer #include <asm/ip32/mace.h> 36862c2c0aSThomas Bogendoerfer 37862c2c0aSThomas Bogendoerfer #include <sound/core.h> 38862c2c0aSThomas Bogendoerfer #include <sound/control.h> 39862c2c0aSThomas Bogendoerfer #include <sound/pcm.h> 40862c2c0aSThomas Bogendoerfer #define SNDRV_GET_ID 41862c2c0aSThomas Bogendoerfer #include <sound/initval.h> 42862c2c0aSThomas Bogendoerfer #include <sound/ad1843.h> 43862c2c0aSThomas Bogendoerfer 44862c2c0aSThomas Bogendoerfer 45862c2c0aSThomas Bogendoerfer MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org>"); 46862c2c0aSThomas Bogendoerfer MODULE_DESCRIPTION("SGI O2 Audio"); 47862c2c0aSThomas Bogendoerfer MODULE_LICENSE("GPL"); 48862c2c0aSThomas Bogendoerfer MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}"); 49862c2c0aSThomas Bogendoerfer 50862c2c0aSThomas Bogendoerfer static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 51862c2c0aSThomas Bogendoerfer static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 52862c2c0aSThomas Bogendoerfer 53862c2c0aSThomas Bogendoerfer module_param(index, int, 0444); 54862c2c0aSThomas Bogendoerfer MODULE_PARM_DESC(index, "Index value for SGI O2 soundcard."); 55862c2c0aSThomas Bogendoerfer module_param(id, charp, 0444); 56862c2c0aSThomas Bogendoerfer MODULE_PARM_DESC(id, "ID string for SGI O2 soundcard."); 57862c2c0aSThomas Bogendoerfer 58862c2c0aSThomas Bogendoerfer 59862c2c0aSThomas Bogendoerfer #define AUDIO_CONTROL_RESET BIT(0) /* 1: reset audio interface */ 60862c2c0aSThomas Bogendoerfer #define AUDIO_CONTROL_CODEC_PRESENT BIT(1) /* 1: codec detected */ 61862c2c0aSThomas Bogendoerfer 62862c2c0aSThomas Bogendoerfer #define CODEC_CONTROL_WORD_SHIFT 0 63862c2c0aSThomas Bogendoerfer #define CODEC_CONTROL_READ BIT(16) 64862c2c0aSThomas Bogendoerfer #define CODEC_CONTROL_ADDRESS_SHIFT 17 65862c2c0aSThomas Bogendoerfer 66862c2c0aSThomas Bogendoerfer #define CHANNEL_CONTROL_RESET BIT(10) /* 1: reset channel */ 67862c2c0aSThomas Bogendoerfer #define CHANNEL_DMA_ENABLE BIT(9) /* 1: enable DMA transfer */ 68862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_DISABLED (0 << 5) /* interrupt disabled */ 69862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */ 70862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_50 (2 << 5) /* int on buffer >50% full */ 71862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */ 72862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_EMPTY (4 << 5) /* int on buffer empty */ 73862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */ 74862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_FULL (6 << 5) /* int on buffer empty */ 75862c2c0aSThomas Bogendoerfer #define CHANNEL_INT_THRESHOLD_NOT_FULL (7 << 5) /* int on buffer !empty */ 76862c2c0aSThomas Bogendoerfer 77862c2c0aSThomas Bogendoerfer #define CHANNEL_RING_SHIFT 12 78862c2c0aSThomas Bogendoerfer #define CHANNEL_RING_SIZE (1 << CHANNEL_RING_SHIFT) 79862c2c0aSThomas Bogendoerfer #define CHANNEL_RING_MASK (CHANNEL_RING_SIZE - 1) 80862c2c0aSThomas Bogendoerfer 81862c2c0aSThomas Bogendoerfer #define CHANNEL_LEFT_SHIFT 40 82862c2c0aSThomas Bogendoerfer #define CHANNEL_RIGHT_SHIFT 8 83862c2c0aSThomas Bogendoerfer 84862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan { 85862c2c0aSThomas Bogendoerfer int idx; 86862c2c0aSThomas Bogendoerfer struct snd_pcm_substream *substream; 87862c2c0aSThomas Bogendoerfer int pos; 88862c2c0aSThomas Bogendoerfer snd_pcm_uframes_t size; 89862c2c0aSThomas Bogendoerfer spinlock_t lock; 90862c2c0aSThomas Bogendoerfer }; 91862c2c0aSThomas Bogendoerfer 92862c2c0aSThomas Bogendoerfer /* definition of the chip-specific record */ 93862c2c0aSThomas Bogendoerfer struct snd_sgio2audio { 94862c2c0aSThomas Bogendoerfer struct snd_card *card; 95862c2c0aSThomas Bogendoerfer 96862c2c0aSThomas Bogendoerfer /* codec */ 97862c2c0aSThomas Bogendoerfer struct snd_ad1843 ad1843; 98862c2c0aSThomas Bogendoerfer spinlock_t ad1843_lock; 99862c2c0aSThomas Bogendoerfer 100862c2c0aSThomas Bogendoerfer /* channels */ 101862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan channel[3]; 102862c2c0aSThomas Bogendoerfer 103862c2c0aSThomas Bogendoerfer /* resources */ 104862c2c0aSThomas Bogendoerfer void *ring_base; 105862c2c0aSThomas Bogendoerfer dma_addr_t ring_base_dma; 106862c2c0aSThomas Bogendoerfer }; 107862c2c0aSThomas Bogendoerfer 108862c2c0aSThomas Bogendoerfer /* AD1843 access */ 109862c2c0aSThomas Bogendoerfer 110862c2c0aSThomas Bogendoerfer /* 111862c2c0aSThomas Bogendoerfer * read_ad1843_reg returns the current contents of a 16 bit AD1843 register. 112862c2c0aSThomas Bogendoerfer * 113862c2c0aSThomas Bogendoerfer * Returns unsigned register value on success, -errno on failure. 114862c2c0aSThomas Bogendoerfer */ 115862c2c0aSThomas Bogendoerfer static int read_ad1843_reg(void *priv, int reg) 116862c2c0aSThomas Bogendoerfer { 117862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = priv; 118862c2c0aSThomas Bogendoerfer int val; 119862c2c0aSThomas Bogendoerfer unsigned long flags; 120862c2c0aSThomas Bogendoerfer 121862c2c0aSThomas Bogendoerfer spin_lock_irqsave(&chip->ad1843_lock, flags); 122862c2c0aSThomas Bogendoerfer 123862c2c0aSThomas Bogendoerfer writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) | 124862c2c0aSThomas Bogendoerfer CODEC_CONTROL_READ, &mace->perif.audio.codec_control); 125862c2c0aSThomas Bogendoerfer wmb(); 126862c2c0aSThomas Bogendoerfer val = readq(&mace->perif.audio.codec_control); /* flush bus */ 127862c2c0aSThomas Bogendoerfer udelay(200); 128862c2c0aSThomas Bogendoerfer 129862c2c0aSThomas Bogendoerfer val = readq(&mace->perif.audio.codec_read); 130862c2c0aSThomas Bogendoerfer 131862c2c0aSThomas Bogendoerfer spin_unlock_irqrestore(&chip->ad1843_lock, flags); 132862c2c0aSThomas Bogendoerfer return val; 133862c2c0aSThomas Bogendoerfer } 134862c2c0aSThomas Bogendoerfer 135862c2c0aSThomas Bogendoerfer /* 136862c2c0aSThomas Bogendoerfer * write_ad1843_reg writes the specified value to a 16 bit AD1843 register. 137862c2c0aSThomas Bogendoerfer */ 138862c2c0aSThomas Bogendoerfer static int write_ad1843_reg(void *priv, int reg, int word) 139862c2c0aSThomas Bogendoerfer { 140862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = priv; 141862c2c0aSThomas Bogendoerfer int val; 142862c2c0aSThomas Bogendoerfer unsigned long flags; 143862c2c0aSThomas Bogendoerfer 144862c2c0aSThomas Bogendoerfer spin_lock_irqsave(&chip->ad1843_lock, flags); 145862c2c0aSThomas Bogendoerfer 146862c2c0aSThomas Bogendoerfer writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) | 147862c2c0aSThomas Bogendoerfer (word << CODEC_CONTROL_WORD_SHIFT), 148862c2c0aSThomas Bogendoerfer &mace->perif.audio.codec_control); 149862c2c0aSThomas Bogendoerfer wmb(); 150862c2c0aSThomas Bogendoerfer val = readq(&mace->perif.audio.codec_control); /* flush bus */ 151862c2c0aSThomas Bogendoerfer udelay(200); 152862c2c0aSThomas Bogendoerfer 153862c2c0aSThomas Bogendoerfer spin_unlock_irqrestore(&chip->ad1843_lock, flags); 154862c2c0aSThomas Bogendoerfer return 0; 155862c2c0aSThomas Bogendoerfer } 156862c2c0aSThomas Bogendoerfer 157862c2c0aSThomas Bogendoerfer static int sgio2audio_gain_info(struct snd_kcontrol *kcontrol, 158862c2c0aSThomas Bogendoerfer struct snd_ctl_elem_info *uinfo) 159862c2c0aSThomas Bogendoerfer { 160862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 161862c2c0aSThomas Bogendoerfer 162862c2c0aSThomas Bogendoerfer uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 163862c2c0aSThomas Bogendoerfer uinfo->count = 2; 164862c2c0aSThomas Bogendoerfer uinfo->value.integer.min = 0; 165862c2c0aSThomas Bogendoerfer uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843, 166862c2c0aSThomas Bogendoerfer (int)kcontrol->private_value); 167862c2c0aSThomas Bogendoerfer return 0; 168862c2c0aSThomas Bogendoerfer } 169862c2c0aSThomas Bogendoerfer 170862c2c0aSThomas Bogendoerfer static int sgio2audio_gain_get(struct snd_kcontrol *kcontrol, 171862c2c0aSThomas Bogendoerfer struct snd_ctl_elem_value *ucontrol) 172862c2c0aSThomas Bogendoerfer { 173862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 174862c2c0aSThomas Bogendoerfer int vol; 175862c2c0aSThomas Bogendoerfer 176862c2c0aSThomas Bogendoerfer vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value); 177862c2c0aSThomas Bogendoerfer 178862c2c0aSThomas Bogendoerfer ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF; 179862c2c0aSThomas Bogendoerfer ucontrol->value.integer.value[1] = vol & 0xFF; 180862c2c0aSThomas Bogendoerfer 181862c2c0aSThomas Bogendoerfer return 0; 182862c2c0aSThomas Bogendoerfer } 183862c2c0aSThomas Bogendoerfer 184862c2c0aSThomas Bogendoerfer static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol, 185862c2c0aSThomas Bogendoerfer struct snd_ctl_elem_value *ucontrol) 186862c2c0aSThomas Bogendoerfer { 187862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 188862c2c0aSThomas Bogendoerfer int newvol, oldvol; 189862c2c0aSThomas Bogendoerfer 190862c2c0aSThomas Bogendoerfer oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value); 191862c2c0aSThomas Bogendoerfer newvol = (ucontrol->value.integer.value[0] << 8) | 192862c2c0aSThomas Bogendoerfer ucontrol->value.integer.value[1]; 193862c2c0aSThomas Bogendoerfer 194862c2c0aSThomas Bogendoerfer newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value, 195862c2c0aSThomas Bogendoerfer newvol); 196862c2c0aSThomas Bogendoerfer 197862c2c0aSThomas Bogendoerfer return newvol != oldvol; 198862c2c0aSThomas Bogendoerfer } 199862c2c0aSThomas Bogendoerfer 200862c2c0aSThomas Bogendoerfer static int sgio2audio_source_info(struct snd_kcontrol *kcontrol, 201862c2c0aSThomas Bogendoerfer struct snd_ctl_elem_info *uinfo) 202862c2c0aSThomas Bogendoerfer { 203862c2c0aSThomas Bogendoerfer static const char *texts[3] = { 204862c2c0aSThomas Bogendoerfer "Cam Mic", "Mic", "Line" 205862c2c0aSThomas Bogendoerfer }; 206862c2c0aSThomas Bogendoerfer uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 207862c2c0aSThomas Bogendoerfer uinfo->count = 1; 208862c2c0aSThomas Bogendoerfer uinfo->value.enumerated.items = 3; 209862c2c0aSThomas Bogendoerfer if (uinfo->value.enumerated.item >= 3) 210862c2c0aSThomas Bogendoerfer uinfo->value.enumerated.item = 1; 211862c2c0aSThomas Bogendoerfer strcpy(uinfo->value.enumerated.name, 212862c2c0aSThomas Bogendoerfer texts[uinfo->value.enumerated.item]); 213862c2c0aSThomas Bogendoerfer return 0; 214862c2c0aSThomas Bogendoerfer } 215862c2c0aSThomas Bogendoerfer 216862c2c0aSThomas Bogendoerfer static int sgio2audio_source_get(struct snd_kcontrol *kcontrol, 217862c2c0aSThomas Bogendoerfer struct snd_ctl_elem_value *ucontrol) 218862c2c0aSThomas Bogendoerfer { 219862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 220862c2c0aSThomas Bogendoerfer 221862c2c0aSThomas Bogendoerfer ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843); 222862c2c0aSThomas Bogendoerfer return 0; 223862c2c0aSThomas Bogendoerfer } 224862c2c0aSThomas Bogendoerfer 225862c2c0aSThomas Bogendoerfer static int sgio2audio_source_put(struct snd_kcontrol *kcontrol, 226862c2c0aSThomas Bogendoerfer struct snd_ctl_elem_value *ucontrol) 227862c2c0aSThomas Bogendoerfer { 228862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 229862c2c0aSThomas Bogendoerfer int newsrc, oldsrc; 230862c2c0aSThomas Bogendoerfer 231862c2c0aSThomas Bogendoerfer oldsrc = ad1843_get_recsrc(&chip->ad1843); 232862c2c0aSThomas Bogendoerfer newsrc = ad1843_set_recsrc(&chip->ad1843, 233862c2c0aSThomas Bogendoerfer ucontrol->value.enumerated.item[0]); 234862c2c0aSThomas Bogendoerfer 235862c2c0aSThomas Bogendoerfer return newsrc != oldsrc; 236862c2c0aSThomas Bogendoerfer } 237862c2c0aSThomas Bogendoerfer 238862c2c0aSThomas Bogendoerfer /* dac1/pcm0 mixer control */ 239862c2c0aSThomas Bogendoerfer static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = { 240862c2c0aSThomas Bogendoerfer .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 241862c2c0aSThomas Bogendoerfer .name = "PCM Playback Volume", 242862c2c0aSThomas Bogendoerfer .index = 0, 243862c2c0aSThomas Bogendoerfer .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 244862c2c0aSThomas Bogendoerfer .private_value = AD1843_GAIN_PCM_0, 245862c2c0aSThomas Bogendoerfer .info = sgio2audio_gain_info, 246862c2c0aSThomas Bogendoerfer .get = sgio2audio_gain_get, 247862c2c0aSThomas Bogendoerfer .put = sgio2audio_gain_put, 248862c2c0aSThomas Bogendoerfer }; 249862c2c0aSThomas Bogendoerfer 250862c2c0aSThomas Bogendoerfer /* dac2/pcm1 mixer control */ 251862c2c0aSThomas Bogendoerfer static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = { 252862c2c0aSThomas Bogendoerfer .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 253862c2c0aSThomas Bogendoerfer .name = "PCM Playback Volume", 254862c2c0aSThomas Bogendoerfer .index = 1, 255862c2c0aSThomas Bogendoerfer .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 256862c2c0aSThomas Bogendoerfer .private_value = AD1843_GAIN_PCM_1, 257862c2c0aSThomas Bogendoerfer .info = sgio2audio_gain_info, 258862c2c0aSThomas Bogendoerfer .get = sgio2audio_gain_get, 259862c2c0aSThomas Bogendoerfer .put = sgio2audio_gain_put, 260862c2c0aSThomas Bogendoerfer }; 261862c2c0aSThomas Bogendoerfer 262862c2c0aSThomas Bogendoerfer /* record level mixer control */ 263862c2c0aSThomas Bogendoerfer static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = { 264862c2c0aSThomas Bogendoerfer .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 265862c2c0aSThomas Bogendoerfer .name = "Capture Volume", 266862c2c0aSThomas Bogendoerfer .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 267862c2c0aSThomas Bogendoerfer .private_value = AD1843_GAIN_RECLEV, 268862c2c0aSThomas Bogendoerfer .info = sgio2audio_gain_info, 269862c2c0aSThomas Bogendoerfer .get = sgio2audio_gain_get, 270862c2c0aSThomas Bogendoerfer .put = sgio2audio_gain_put, 271862c2c0aSThomas Bogendoerfer }; 272862c2c0aSThomas Bogendoerfer 273862c2c0aSThomas Bogendoerfer /* record level source control */ 274862c2c0aSThomas Bogendoerfer static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = { 275862c2c0aSThomas Bogendoerfer .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 276862c2c0aSThomas Bogendoerfer .name = "Capture Source", 277862c2c0aSThomas Bogendoerfer .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 278862c2c0aSThomas Bogendoerfer .info = sgio2audio_source_info, 279862c2c0aSThomas Bogendoerfer .get = sgio2audio_source_get, 280862c2c0aSThomas Bogendoerfer .put = sgio2audio_source_put, 281862c2c0aSThomas Bogendoerfer }; 282862c2c0aSThomas Bogendoerfer 283862c2c0aSThomas Bogendoerfer /* line mixer control */ 284862c2c0aSThomas Bogendoerfer static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = { 285862c2c0aSThomas Bogendoerfer .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 286862c2c0aSThomas Bogendoerfer .name = "Line Playback Volume", 287862c2c0aSThomas Bogendoerfer .index = 0, 288862c2c0aSThomas Bogendoerfer .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 289862c2c0aSThomas Bogendoerfer .private_value = AD1843_GAIN_LINE, 290862c2c0aSThomas Bogendoerfer .info = sgio2audio_gain_info, 291862c2c0aSThomas Bogendoerfer .get = sgio2audio_gain_get, 292862c2c0aSThomas Bogendoerfer .put = sgio2audio_gain_put, 293862c2c0aSThomas Bogendoerfer }; 294862c2c0aSThomas Bogendoerfer 295862c2c0aSThomas Bogendoerfer /* cd mixer control */ 296862c2c0aSThomas Bogendoerfer static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = { 297862c2c0aSThomas Bogendoerfer .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 298862c2c0aSThomas Bogendoerfer .name = "Line Playback Volume", 299862c2c0aSThomas Bogendoerfer .index = 1, 300862c2c0aSThomas Bogendoerfer .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 301862c2c0aSThomas Bogendoerfer .private_value = AD1843_GAIN_LINE_2, 302862c2c0aSThomas Bogendoerfer .info = sgio2audio_gain_info, 303862c2c0aSThomas Bogendoerfer .get = sgio2audio_gain_get, 304862c2c0aSThomas Bogendoerfer .put = sgio2audio_gain_put, 305862c2c0aSThomas Bogendoerfer }; 306862c2c0aSThomas Bogendoerfer 307862c2c0aSThomas Bogendoerfer /* mic mixer control */ 308862c2c0aSThomas Bogendoerfer static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = { 309862c2c0aSThomas Bogendoerfer .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 310862c2c0aSThomas Bogendoerfer .name = "Mic Playback Volume", 311862c2c0aSThomas Bogendoerfer .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 312862c2c0aSThomas Bogendoerfer .private_value = AD1843_GAIN_MIC, 313862c2c0aSThomas Bogendoerfer .info = sgio2audio_gain_info, 314862c2c0aSThomas Bogendoerfer .get = sgio2audio_gain_get, 315862c2c0aSThomas Bogendoerfer .put = sgio2audio_gain_put, 316862c2c0aSThomas Bogendoerfer }; 317862c2c0aSThomas Bogendoerfer 318862c2c0aSThomas Bogendoerfer 319862c2c0aSThomas Bogendoerfer static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip) 320862c2c0aSThomas Bogendoerfer { 321862c2c0aSThomas Bogendoerfer int err; 322862c2c0aSThomas Bogendoerfer 323862c2c0aSThomas Bogendoerfer err = snd_ctl_add(chip->card, 324862c2c0aSThomas Bogendoerfer snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip)); 325862c2c0aSThomas Bogendoerfer if (err < 0) 326862c2c0aSThomas Bogendoerfer return err; 327862c2c0aSThomas Bogendoerfer 328862c2c0aSThomas Bogendoerfer err = snd_ctl_add(chip->card, 329862c2c0aSThomas Bogendoerfer snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip)); 330862c2c0aSThomas Bogendoerfer if (err < 0) 331862c2c0aSThomas Bogendoerfer return err; 332862c2c0aSThomas Bogendoerfer 333862c2c0aSThomas Bogendoerfer err = snd_ctl_add(chip->card, 334862c2c0aSThomas Bogendoerfer snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip)); 335862c2c0aSThomas Bogendoerfer if (err < 0) 336862c2c0aSThomas Bogendoerfer return err; 337862c2c0aSThomas Bogendoerfer 338862c2c0aSThomas Bogendoerfer err = snd_ctl_add(chip->card, 339862c2c0aSThomas Bogendoerfer snd_ctl_new1(&sgio2audio_ctrl_recsource, chip)); 340862c2c0aSThomas Bogendoerfer if (err < 0) 341862c2c0aSThomas Bogendoerfer return err; 342862c2c0aSThomas Bogendoerfer err = snd_ctl_add(chip->card, 343862c2c0aSThomas Bogendoerfer snd_ctl_new1(&sgio2audio_ctrl_line, chip)); 344862c2c0aSThomas Bogendoerfer if (err < 0) 345862c2c0aSThomas Bogendoerfer return err; 346862c2c0aSThomas Bogendoerfer 347862c2c0aSThomas Bogendoerfer err = snd_ctl_add(chip->card, 348862c2c0aSThomas Bogendoerfer snd_ctl_new1(&sgio2audio_ctrl_cd, chip)); 349862c2c0aSThomas Bogendoerfer if (err < 0) 350862c2c0aSThomas Bogendoerfer return err; 351862c2c0aSThomas Bogendoerfer 352862c2c0aSThomas Bogendoerfer err = snd_ctl_add(chip->card, 353862c2c0aSThomas Bogendoerfer snd_ctl_new1(&sgio2audio_ctrl_mic, chip)); 354862c2c0aSThomas Bogendoerfer if (err < 0) 355862c2c0aSThomas Bogendoerfer return err; 356862c2c0aSThomas Bogendoerfer 357862c2c0aSThomas Bogendoerfer return 0; 358862c2c0aSThomas Bogendoerfer } 359862c2c0aSThomas Bogendoerfer 360862c2c0aSThomas Bogendoerfer /* low-level audio interface DMA */ 361862c2c0aSThomas Bogendoerfer 362862c2c0aSThomas Bogendoerfer /* get data out of bounce buffer, count must be a multiple of 32 */ 363862c2c0aSThomas Bogendoerfer /* returns 1 if a period has elapsed */ 364862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip, 365862c2c0aSThomas Bogendoerfer unsigned int ch, unsigned int count) 366862c2c0aSThomas Bogendoerfer { 367862c2c0aSThomas Bogendoerfer int ret; 368862c2c0aSThomas Bogendoerfer unsigned long src_base, src_pos, dst_mask; 369862c2c0aSThomas Bogendoerfer unsigned char *dst_base; 370862c2c0aSThomas Bogendoerfer int dst_pos; 371862c2c0aSThomas Bogendoerfer u64 *src; 372862c2c0aSThomas Bogendoerfer s16 *dst; 373862c2c0aSThomas Bogendoerfer u64 x; 374862c2c0aSThomas Bogendoerfer unsigned long flags; 375862c2c0aSThomas Bogendoerfer struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime; 376862c2c0aSThomas Bogendoerfer 377862c2c0aSThomas Bogendoerfer spin_lock_irqsave(&chip->channel[ch].lock, flags); 378862c2c0aSThomas Bogendoerfer 379862c2c0aSThomas Bogendoerfer src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT); 380862c2c0aSThomas Bogendoerfer src_pos = readq(&mace->perif.audio.chan[ch].read_ptr); 381862c2c0aSThomas Bogendoerfer dst_base = runtime->dma_area; 382862c2c0aSThomas Bogendoerfer dst_pos = chip->channel[ch].pos; 383862c2c0aSThomas Bogendoerfer dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1; 384862c2c0aSThomas Bogendoerfer 385862c2c0aSThomas Bogendoerfer /* check if a period has elapsed */ 386862c2c0aSThomas Bogendoerfer chip->channel[ch].size += (count >> 3); /* in frames */ 387862c2c0aSThomas Bogendoerfer ret = chip->channel[ch].size >= runtime->period_size; 388862c2c0aSThomas Bogendoerfer chip->channel[ch].size %= runtime->period_size; 389862c2c0aSThomas Bogendoerfer 390862c2c0aSThomas Bogendoerfer while (count) { 391862c2c0aSThomas Bogendoerfer src = (u64 *)(src_base + src_pos); 392862c2c0aSThomas Bogendoerfer dst = (s16 *)(dst_base + dst_pos); 393862c2c0aSThomas Bogendoerfer 394862c2c0aSThomas Bogendoerfer x = *src; 395862c2c0aSThomas Bogendoerfer dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff; 396862c2c0aSThomas Bogendoerfer dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff; 397862c2c0aSThomas Bogendoerfer 398862c2c0aSThomas Bogendoerfer src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK; 399862c2c0aSThomas Bogendoerfer dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask; 400862c2c0aSThomas Bogendoerfer count -= sizeof(u64); 401862c2c0aSThomas Bogendoerfer } 402862c2c0aSThomas Bogendoerfer 403862c2c0aSThomas Bogendoerfer writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */ 404862c2c0aSThomas Bogendoerfer chip->channel[ch].pos = dst_pos; 405862c2c0aSThomas Bogendoerfer 406862c2c0aSThomas Bogendoerfer spin_unlock_irqrestore(&chip->channel[ch].lock, flags); 407862c2c0aSThomas Bogendoerfer return ret; 408862c2c0aSThomas Bogendoerfer } 409862c2c0aSThomas Bogendoerfer 410862c2c0aSThomas Bogendoerfer /* put some DMA data in bounce buffer, count must be a multiple of 32 */ 411862c2c0aSThomas Bogendoerfer /* returns 1 if a period has elapsed */ 412862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip, 413862c2c0aSThomas Bogendoerfer unsigned int ch, unsigned int count) 414862c2c0aSThomas Bogendoerfer { 415862c2c0aSThomas Bogendoerfer int ret; 416862c2c0aSThomas Bogendoerfer s64 l, r; 417862c2c0aSThomas Bogendoerfer unsigned long dst_base, dst_pos, src_mask; 418862c2c0aSThomas Bogendoerfer unsigned char *src_base; 419862c2c0aSThomas Bogendoerfer int src_pos; 420862c2c0aSThomas Bogendoerfer u64 *dst; 421862c2c0aSThomas Bogendoerfer s16 *src; 422862c2c0aSThomas Bogendoerfer unsigned long flags; 423862c2c0aSThomas Bogendoerfer struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime; 424862c2c0aSThomas Bogendoerfer 425862c2c0aSThomas Bogendoerfer spin_lock_irqsave(&chip->channel[ch].lock, flags); 426862c2c0aSThomas Bogendoerfer 427862c2c0aSThomas Bogendoerfer dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT); 428862c2c0aSThomas Bogendoerfer dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr); 429862c2c0aSThomas Bogendoerfer src_base = runtime->dma_area; 430862c2c0aSThomas Bogendoerfer src_pos = chip->channel[ch].pos; 431862c2c0aSThomas Bogendoerfer src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1; 432862c2c0aSThomas Bogendoerfer 433862c2c0aSThomas Bogendoerfer /* check if a period has elapsed */ 434862c2c0aSThomas Bogendoerfer chip->channel[ch].size += (count >> 3); /* in frames */ 435862c2c0aSThomas Bogendoerfer ret = chip->channel[ch].size >= runtime->period_size; 436862c2c0aSThomas Bogendoerfer chip->channel[ch].size %= runtime->period_size; 437862c2c0aSThomas Bogendoerfer 438862c2c0aSThomas Bogendoerfer while (count) { 439862c2c0aSThomas Bogendoerfer src = (s16 *)(src_base + src_pos); 440862c2c0aSThomas Bogendoerfer dst = (u64 *)(dst_base + dst_pos); 441862c2c0aSThomas Bogendoerfer 442862c2c0aSThomas Bogendoerfer l = src[0]; /* sign extend */ 443862c2c0aSThomas Bogendoerfer r = src[1]; /* sign extend */ 444862c2c0aSThomas Bogendoerfer 445862c2c0aSThomas Bogendoerfer *dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) | 446862c2c0aSThomas Bogendoerfer ((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT); 447862c2c0aSThomas Bogendoerfer 448862c2c0aSThomas Bogendoerfer dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK; 449862c2c0aSThomas Bogendoerfer src_pos = (src_pos + 2 * sizeof(s16)) & src_mask; 450862c2c0aSThomas Bogendoerfer count -= sizeof(u64); 451862c2c0aSThomas Bogendoerfer } 452862c2c0aSThomas Bogendoerfer 453862c2c0aSThomas Bogendoerfer writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */ 454862c2c0aSThomas Bogendoerfer chip->channel[ch].pos = src_pos; 455862c2c0aSThomas Bogendoerfer 456862c2c0aSThomas Bogendoerfer spin_unlock_irqrestore(&chip->channel[ch].lock, flags); 457862c2c0aSThomas Bogendoerfer return ret; 458862c2c0aSThomas Bogendoerfer } 459862c2c0aSThomas Bogendoerfer 460862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream) 461862c2c0aSThomas Bogendoerfer { 462862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 463862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 464862c2c0aSThomas Bogendoerfer int ch = chan->idx; 465862c2c0aSThomas Bogendoerfer 466862c2c0aSThomas Bogendoerfer /* reset DMA channel */ 467862c2c0aSThomas Bogendoerfer writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control); 468862c2c0aSThomas Bogendoerfer udelay(10); 469862c2c0aSThomas Bogendoerfer writeq(0, &mace->perif.audio.chan[ch].control); 470862c2c0aSThomas Bogendoerfer 471862c2c0aSThomas Bogendoerfer if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 472862c2c0aSThomas Bogendoerfer /* push a full buffer */ 473862c2c0aSThomas Bogendoerfer snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32); 474862c2c0aSThomas Bogendoerfer } 475862c2c0aSThomas Bogendoerfer /* set DMA to wake on 50% empty and enable interrupt */ 476862c2c0aSThomas Bogendoerfer writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50, 477862c2c0aSThomas Bogendoerfer &mace->perif.audio.chan[ch].control); 478862c2c0aSThomas Bogendoerfer return 0; 479862c2c0aSThomas Bogendoerfer } 480862c2c0aSThomas Bogendoerfer 481862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream) 482862c2c0aSThomas Bogendoerfer { 483862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 484862c2c0aSThomas Bogendoerfer 485862c2c0aSThomas Bogendoerfer writeq(0, &mace->perif.audio.chan[chan->idx].control); 486862c2c0aSThomas Bogendoerfer return 0; 487862c2c0aSThomas Bogendoerfer } 488862c2c0aSThomas Bogendoerfer 489862c2c0aSThomas Bogendoerfer static irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id) 490862c2c0aSThomas Bogendoerfer { 491862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan *chan = dev_id; 492862c2c0aSThomas Bogendoerfer struct snd_pcm_substream *substream; 493862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip; 494862c2c0aSThomas Bogendoerfer int count, ch; 495862c2c0aSThomas Bogendoerfer 496862c2c0aSThomas Bogendoerfer substream = chan->substream; 497862c2c0aSThomas Bogendoerfer chip = snd_pcm_substream_chip(substream); 498862c2c0aSThomas Bogendoerfer ch = chan->idx; 499862c2c0aSThomas Bogendoerfer 500862c2c0aSThomas Bogendoerfer /* empty the ring */ 501862c2c0aSThomas Bogendoerfer count = CHANNEL_RING_SIZE - 502862c2c0aSThomas Bogendoerfer readq(&mace->perif.audio.chan[ch].depth) - 32; 503862c2c0aSThomas Bogendoerfer if (snd_sgio2audio_dma_pull_frag(chip, ch, count)) 504862c2c0aSThomas Bogendoerfer snd_pcm_period_elapsed(substream); 505862c2c0aSThomas Bogendoerfer 506862c2c0aSThomas Bogendoerfer return IRQ_HANDLED; 507862c2c0aSThomas Bogendoerfer } 508862c2c0aSThomas Bogendoerfer 509862c2c0aSThomas Bogendoerfer static irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id) 510862c2c0aSThomas Bogendoerfer { 511862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan *chan = dev_id; 512862c2c0aSThomas Bogendoerfer struct snd_pcm_substream *substream; 513862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip; 514862c2c0aSThomas Bogendoerfer int count, ch; 515862c2c0aSThomas Bogendoerfer 516862c2c0aSThomas Bogendoerfer substream = chan->substream; 517862c2c0aSThomas Bogendoerfer chip = snd_pcm_substream_chip(substream); 518862c2c0aSThomas Bogendoerfer ch = chan->idx; 519862c2c0aSThomas Bogendoerfer /* fill the ring */ 520862c2c0aSThomas Bogendoerfer count = CHANNEL_RING_SIZE - 521862c2c0aSThomas Bogendoerfer readq(&mace->perif.audio.chan[ch].depth) - 32; 522862c2c0aSThomas Bogendoerfer if (snd_sgio2audio_dma_push_frag(chip, ch, count)) 523862c2c0aSThomas Bogendoerfer snd_pcm_period_elapsed(substream); 524862c2c0aSThomas Bogendoerfer 525862c2c0aSThomas Bogendoerfer return IRQ_HANDLED; 526862c2c0aSThomas Bogendoerfer } 527862c2c0aSThomas Bogendoerfer 528862c2c0aSThomas Bogendoerfer static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id) 529862c2c0aSThomas Bogendoerfer { 530862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan *chan = dev_id; 531862c2c0aSThomas Bogendoerfer struct snd_pcm_substream *substream; 532862c2c0aSThomas Bogendoerfer 533862c2c0aSThomas Bogendoerfer substream = chan->substream; 534862c2c0aSThomas Bogendoerfer snd_sgio2audio_dma_stop(substream); 535862c2c0aSThomas Bogendoerfer snd_sgio2audio_dma_start(substream); 536862c2c0aSThomas Bogendoerfer return IRQ_HANDLED; 537862c2c0aSThomas Bogendoerfer } 538862c2c0aSThomas Bogendoerfer 539862c2c0aSThomas Bogendoerfer /* PCM part */ 540862c2c0aSThomas Bogendoerfer /* PCM hardware definition */ 541862c2c0aSThomas Bogendoerfer static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = { 542862c2c0aSThomas Bogendoerfer .info = (SNDRV_PCM_INFO_MMAP | 543862c2c0aSThomas Bogendoerfer SNDRV_PCM_INFO_MMAP_VALID | 544862c2c0aSThomas Bogendoerfer SNDRV_PCM_INFO_INTERLEAVED | 545862c2c0aSThomas Bogendoerfer SNDRV_PCM_INFO_BLOCK_TRANSFER), 546862c2c0aSThomas Bogendoerfer .formats = SNDRV_PCM_FMTBIT_S16_BE, 547862c2c0aSThomas Bogendoerfer .rates = SNDRV_PCM_RATE_8000_48000, 548862c2c0aSThomas Bogendoerfer .rate_min = 8000, 549862c2c0aSThomas Bogendoerfer .rate_max = 48000, 550862c2c0aSThomas Bogendoerfer .channels_min = 2, 551862c2c0aSThomas Bogendoerfer .channels_max = 2, 552862c2c0aSThomas Bogendoerfer .buffer_bytes_max = 65536, 553862c2c0aSThomas Bogendoerfer .period_bytes_min = 32768, 554862c2c0aSThomas Bogendoerfer .period_bytes_max = 65536, 555862c2c0aSThomas Bogendoerfer .periods_min = 1, 556862c2c0aSThomas Bogendoerfer .periods_max = 1024, 557862c2c0aSThomas Bogendoerfer }; 558862c2c0aSThomas Bogendoerfer 559862c2c0aSThomas Bogendoerfer /* PCM playback open callback */ 560862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream) 561862c2c0aSThomas Bogendoerfer { 562862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 563862c2c0aSThomas Bogendoerfer struct snd_pcm_runtime *runtime = substream->runtime; 564862c2c0aSThomas Bogendoerfer 565862c2c0aSThomas Bogendoerfer runtime->hw = snd_sgio2audio_pcm_hw; 566862c2c0aSThomas Bogendoerfer runtime->private_data = &chip->channel[1]; 567862c2c0aSThomas Bogendoerfer return 0; 568862c2c0aSThomas Bogendoerfer } 569862c2c0aSThomas Bogendoerfer 570862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream) 571862c2c0aSThomas Bogendoerfer { 572862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 573862c2c0aSThomas Bogendoerfer struct snd_pcm_runtime *runtime = substream->runtime; 574862c2c0aSThomas Bogendoerfer 575862c2c0aSThomas Bogendoerfer runtime->hw = snd_sgio2audio_pcm_hw; 576862c2c0aSThomas Bogendoerfer runtime->private_data = &chip->channel[2]; 577862c2c0aSThomas Bogendoerfer return 0; 578862c2c0aSThomas Bogendoerfer } 579862c2c0aSThomas Bogendoerfer 580862c2c0aSThomas Bogendoerfer /* PCM capture open callback */ 581862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream) 582862c2c0aSThomas Bogendoerfer { 583862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 584862c2c0aSThomas Bogendoerfer struct snd_pcm_runtime *runtime = substream->runtime; 585862c2c0aSThomas Bogendoerfer 586862c2c0aSThomas Bogendoerfer runtime->hw = snd_sgio2audio_pcm_hw; 587862c2c0aSThomas Bogendoerfer runtime->private_data = &chip->channel[0]; 588862c2c0aSThomas Bogendoerfer return 0; 589862c2c0aSThomas Bogendoerfer } 590862c2c0aSThomas Bogendoerfer 591862c2c0aSThomas Bogendoerfer /* PCM close callback */ 592862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream) 593862c2c0aSThomas Bogendoerfer { 594862c2c0aSThomas Bogendoerfer struct snd_pcm_runtime *runtime = substream->runtime; 595862c2c0aSThomas Bogendoerfer 596862c2c0aSThomas Bogendoerfer runtime->private_data = NULL; 597862c2c0aSThomas Bogendoerfer return 0; 598862c2c0aSThomas Bogendoerfer } 599862c2c0aSThomas Bogendoerfer 600862c2c0aSThomas Bogendoerfer 601862c2c0aSThomas Bogendoerfer /* hw_params callback */ 602862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream, 603862c2c0aSThomas Bogendoerfer struct snd_pcm_hw_params *hw_params) 604862c2c0aSThomas Bogendoerfer { 6056cedf869SClemens Ladisch return snd_pcm_lib_alloc_vmalloc_buffer(substream, 6066cedf869SClemens Ladisch params_buffer_bytes(hw_params)); 607862c2c0aSThomas Bogendoerfer } 608862c2c0aSThomas Bogendoerfer 609862c2c0aSThomas Bogendoerfer /* hw_free callback */ 610862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream) 611862c2c0aSThomas Bogendoerfer { 6126cedf869SClemens Ladisch return snd_pcm_lib_free_vmalloc_buffer(substream); 613862c2c0aSThomas Bogendoerfer } 614862c2c0aSThomas Bogendoerfer 615862c2c0aSThomas Bogendoerfer /* prepare callback */ 616862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream) 617862c2c0aSThomas Bogendoerfer { 618862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 619862c2c0aSThomas Bogendoerfer struct snd_pcm_runtime *runtime = substream->runtime; 620862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 621862c2c0aSThomas Bogendoerfer int ch = chan->idx; 622862c2c0aSThomas Bogendoerfer unsigned long flags; 623862c2c0aSThomas Bogendoerfer 624862c2c0aSThomas Bogendoerfer spin_lock_irqsave(&chip->channel[ch].lock, flags); 625862c2c0aSThomas Bogendoerfer 626862c2c0aSThomas Bogendoerfer /* Setup the pseudo-dma transfer pointers. */ 627862c2c0aSThomas Bogendoerfer chip->channel[ch].pos = 0; 628862c2c0aSThomas Bogendoerfer chip->channel[ch].size = 0; 629862c2c0aSThomas Bogendoerfer chip->channel[ch].substream = substream; 630862c2c0aSThomas Bogendoerfer 631862c2c0aSThomas Bogendoerfer /* set AD1843 format */ 632862c2c0aSThomas Bogendoerfer /* hardware format is always S16_LE */ 633862c2c0aSThomas Bogendoerfer switch (substream->stream) { 634862c2c0aSThomas Bogendoerfer case SNDRV_PCM_STREAM_PLAYBACK: 635862c2c0aSThomas Bogendoerfer ad1843_setup_dac(&chip->ad1843, 636862c2c0aSThomas Bogendoerfer ch - 1, 637862c2c0aSThomas Bogendoerfer runtime->rate, 638862c2c0aSThomas Bogendoerfer SNDRV_PCM_FORMAT_S16_LE, 639862c2c0aSThomas Bogendoerfer runtime->channels); 640862c2c0aSThomas Bogendoerfer break; 641862c2c0aSThomas Bogendoerfer case SNDRV_PCM_STREAM_CAPTURE: 642862c2c0aSThomas Bogendoerfer ad1843_setup_adc(&chip->ad1843, 643862c2c0aSThomas Bogendoerfer runtime->rate, 644862c2c0aSThomas Bogendoerfer SNDRV_PCM_FORMAT_S16_LE, 645862c2c0aSThomas Bogendoerfer runtime->channels); 646862c2c0aSThomas Bogendoerfer break; 647862c2c0aSThomas Bogendoerfer } 648862c2c0aSThomas Bogendoerfer spin_unlock_irqrestore(&chip->channel[ch].lock, flags); 649862c2c0aSThomas Bogendoerfer return 0; 650862c2c0aSThomas Bogendoerfer } 651862c2c0aSThomas Bogendoerfer 652862c2c0aSThomas Bogendoerfer /* trigger callback */ 653862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream, 654862c2c0aSThomas Bogendoerfer int cmd) 655862c2c0aSThomas Bogendoerfer { 656862c2c0aSThomas Bogendoerfer switch (cmd) { 657862c2c0aSThomas Bogendoerfer case SNDRV_PCM_TRIGGER_START: 658862c2c0aSThomas Bogendoerfer /* start the PCM engine */ 659862c2c0aSThomas Bogendoerfer snd_sgio2audio_dma_start(substream); 660862c2c0aSThomas Bogendoerfer break; 661862c2c0aSThomas Bogendoerfer case SNDRV_PCM_TRIGGER_STOP: 662862c2c0aSThomas Bogendoerfer /* stop the PCM engine */ 663862c2c0aSThomas Bogendoerfer snd_sgio2audio_dma_stop(substream); 664862c2c0aSThomas Bogendoerfer break; 665862c2c0aSThomas Bogendoerfer default: 666862c2c0aSThomas Bogendoerfer return -EINVAL; 667862c2c0aSThomas Bogendoerfer } 668862c2c0aSThomas Bogendoerfer return 0; 669862c2c0aSThomas Bogendoerfer } 670862c2c0aSThomas Bogendoerfer 671862c2c0aSThomas Bogendoerfer /* pointer callback */ 672862c2c0aSThomas Bogendoerfer static snd_pcm_uframes_t 673862c2c0aSThomas Bogendoerfer snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream) 674862c2c0aSThomas Bogendoerfer { 675862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 676862c2c0aSThomas Bogendoerfer struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 677862c2c0aSThomas Bogendoerfer 678862c2c0aSThomas Bogendoerfer /* get the current hardware pointer */ 679862c2c0aSThomas Bogendoerfer return bytes_to_frames(substream->runtime, 680862c2c0aSThomas Bogendoerfer chip->channel[chan->idx].pos); 681862c2c0aSThomas Bogendoerfer } 682862c2c0aSThomas Bogendoerfer 683862c2c0aSThomas Bogendoerfer /* operators */ 684862c2c0aSThomas Bogendoerfer static struct snd_pcm_ops snd_sgio2audio_playback1_ops = { 685862c2c0aSThomas Bogendoerfer .open = snd_sgio2audio_playback1_open, 686862c2c0aSThomas Bogendoerfer .close = snd_sgio2audio_pcm_close, 687862c2c0aSThomas Bogendoerfer .ioctl = snd_pcm_lib_ioctl, 688862c2c0aSThomas Bogendoerfer .hw_params = snd_sgio2audio_pcm_hw_params, 689862c2c0aSThomas Bogendoerfer .hw_free = snd_sgio2audio_pcm_hw_free, 690862c2c0aSThomas Bogendoerfer .prepare = snd_sgio2audio_pcm_prepare, 691862c2c0aSThomas Bogendoerfer .trigger = snd_sgio2audio_pcm_trigger, 692862c2c0aSThomas Bogendoerfer .pointer = snd_sgio2audio_pcm_pointer, 6936cedf869SClemens Ladisch .page = snd_pcm_lib_get_vmalloc_page, 694*c32d977bSTakashi Iwai .mmap = snd_pcm_lib_mmap_vmalloc, 695862c2c0aSThomas Bogendoerfer }; 696862c2c0aSThomas Bogendoerfer 697862c2c0aSThomas Bogendoerfer static struct snd_pcm_ops snd_sgio2audio_playback2_ops = { 698862c2c0aSThomas Bogendoerfer .open = snd_sgio2audio_playback2_open, 699862c2c0aSThomas Bogendoerfer .close = snd_sgio2audio_pcm_close, 700862c2c0aSThomas Bogendoerfer .ioctl = snd_pcm_lib_ioctl, 701862c2c0aSThomas Bogendoerfer .hw_params = snd_sgio2audio_pcm_hw_params, 702862c2c0aSThomas Bogendoerfer .hw_free = snd_sgio2audio_pcm_hw_free, 703862c2c0aSThomas Bogendoerfer .prepare = snd_sgio2audio_pcm_prepare, 704862c2c0aSThomas Bogendoerfer .trigger = snd_sgio2audio_pcm_trigger, 705862c2c0aSThomas Bogendoerfer .pointer = snd_sgio2audio_pcm_pointer, 7066cedf869SClemens Ladisch .page = snd_pcm_lib_get_vmalloc_page, 707*c32d977bSTakashi Iwai .mmap = snd_pcm_lib_mmap_vmalloc, 708862c2c0aSThomas Bogendoerfer }; 709862c2c0aSThomas Bogendoerfer 710862c2c0aSThomas Bogendoerfer static struct snd_pcm_ops snd_sgio2audio_capture_ops = { 711862c2c0aSThomas Bogendoerfer .open = snd_sgio2audio_capture_open, 712862c2c0aSThomas Bogendoerfer .close = snd_sgio2audio_pcm_close, 713862c2c0aSThomas Bogendoerfer .ioctl = snd_pcm_lib_ioctl, 714862c2c0aSThomas Bogendoerfer .hw_params = snd_sgio2audio_pcm_hw_params, 715862c2c0aSThomas Bogendoerfer .hw_free = snd_sgio2audio_pcm_hw_free, 716862c2c0aSThomas Bogendoerfer .prepare = snd_sgio2audio_pcm_prepare, 717862c2c0aSThomas Bogendoerfer .trigger = snd_sgio2audio_pcm_trigger, 718862c2c0aSThomas Bogendoerfer .pointer = snd_sgio2audio_pcm_pointer, 7196cedf869SClemens Ladisch .page = snd_pcm_lib_get_vmalloc_page, 720*c32d977bSTakashi Iwai .mmap = snd_pcm_lib_mmap_vmalloc, 721862c2c0aSThomas Bogendoerfer }; 722862c2c0aSThomas Bogendoerfer 723862c2c0aSThomas Bogendoerfer /* 724862c2c0aSThomas Bogendoerfer * definitions of capture are omitted here... 725862c2c0aSThomas Bogendoerfer */ 726862c2c0aSThomas Bogendoerfer 727862c2c0aSThomas Bogendoerfer /* create a pcm device */ 728862c2c0aSThomas Bogendoerfer static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip) 729862c2c0aSThomas Bogendoerfer { 730862c2c0aSThomas Bogendoerfer struct snd_pcm *pcm; 731862c2c0aSThomas Bogendoerfer int err; 732862c2c0aSThomas Bogendoerfer 733862c2c0aSThomas Bogendoerfer /* create first pcm device with one outputs and one input */ 734862c2c0aSThomas Bogendoerfer err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm); 735862c2c0aSThomas Bogendoerfer if (err < 0) 736862c2c0aSThomas Bogendoerfer return err; 737862c2c0aSThomas Bogendoerfer 738862c2c0aSThomas Bogendoerfer pcm->private_data = chip; 739862c2c0aSThomas Bogendoerfer strcpy(pcm->name, "SGI O2 DAC1"); 740862c2c0aSThomas Bogendoerfer 741862c2c0aSThomas Bogendoerfer /* set operators */ 742862c2c0aSThomas Bogendoerfer snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 743862c2c0aSThomas Bogendoerfer &snd_sgio2audio_playback1_ops); 744862c2c0aSThomas Bogendoerfer snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 745862c2c0aSThomas Bogendoerfer &snd_sgio2audio_capture_ops); 746862c2c0aSThomas Bogendoerfer 747862c2c0aSThomas Bogendoerfer /* create second pcm device with one outputs and no input */ 748862c2c0aSThomas Bogendoerfer err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm); 749862c2c0aSThomas Bogendoerfer if (err < 0) 750862c2c0aSThomas Bogendoerfer return err; 751862c2c0aSThomas Bogendoerfer 752862c2c0aSThomas Bogendoerfer pcm->private_data = chip; 753862c2c0aSThomas Bogendoerfer strcpy(pcm->name, "SGI O2 DAC2"); 754862c2c0aSThomas Bogendoerfer 755862c2c0aSThomas Bogendoerfer /* set operators */ 756862c2c0aSThomas Bogendoerfer snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 757862c2c0aSThomas Bogendoerfer &snd_sgio2audio_playback2_ops); 758862c2c0aSThomas Bogendoerfer 759862c2c0aSThomas Bogendoerfer return 0; 760862c2c0aSThomas Bogendoerfer } 761862c2c0aSThomas Bogendoerfer 762862c2c0aSThomas Bogendoerfer static struct { 763862c2c0aSThomas Bogendoerfer int idx; 764862c2c0aSThomas Bogendoerfer int irq; 765862c2c0aSThomas Bogendoerfer irqreturn_t (*isr)(int, void *); 766862c2c0aSThomas Bogendoerfer const char *desc; 767862c2c0aSThomas Bogendoerfer } snd_sgio2_isr_table[] = { 768862c2c0aSThomas Bogendoerfer { 769862c2c0aSThomas Bogendoerfer .idx = 0, 770862c2c0aSThomas Bogendoerfer .irq = MACEISA_AUDIO1_DMAT_IRQ, 771862c2c0aSThomas Bogendoerfer .isr = snd_sgio2audio_dma_in_isr, 772862c2c0aSThomas Bogendoerfer .desc = "Capture DMA Channel 0" 773862c2c0aSThomas Bogendoerfer }, { 774862c2c0aSThomas Bogendoerfer .idx = 0, 775862c2c0aSThomas Bogendoerfer .irq = MACEISA_AUDIO1_OF_IRQ, 776862c2c0aSThomas Bogendoerfer .isr = snd_sgio2audio_error_isr, 777862c2c0aSThomas Bogendoerfer .desc = "Capture Overflow" 778862c2c0aSThomas Bogendoerfer }, { 779862c2c0aSThomas Bogendoerfer .idx = 1, 780862c2c0aSThomas Bogendoerfer .irq = MACEISA_AUDIO2_DMAT_IRQ, 781862c2c0aSThomas Bogendoerfer .isr = snd_sgio2audio_dma_out_isr, 782862c2c0aSThomas Bogendoerfer .desc = "Playback DMA Channel 1" 783862c2c0aSThomas Bogendoerfer }, { 784862c2c0aSThomas Bogendoerfer .idx = 1, 785862c2c0aSThomas Bogendoerfer .irq = MACEISA_AUDIO2_MERR_IRQ, 786862c2c0aSThomas Bogendoerfer .isr = snd_sgio2audio_error_isr, 787862c2c0aSThomas Bogendoerfer .desc = "Memory Error Channel 1" 788862c2c0aSThomas Bogendoerfer }, { 789862c2c0aSThomas Bogendoerfer .idx = 2, 790862c2c0aSThomas Bogendoerfer .irq = MACEISA_AUDIO3_DMAT_IRQ, 791862c2c0aSThomas Bogendoerfer .isr = snd_sgio2audio_dma_out_isr, 792862c2c0aSThomas Bogendoerfer .desc = "Playback DMA Channel 2" 793862c2c0aSThomas Bogendoerfer }, { 794862c2c0aSThomas Bogendoerfer .idx = 2, 795862c2c0aSThomas Bogendoerfer .irq = MACEISA_AUDIO3_MERR_IRQ, 796862c2c0aSThomas Bogendoerfer .isr = snd_sgio2audio_error_isr, 797862c2c0aSThomas Bogendoerfer .desc = "Memory Error Channel 2" 798862c2c0aSThomas Bogendoerfer } 799862c2c0aSThomas Bogendoerfer }; 800862c2c0aSThomas Bogendoerfer 801862c2c0aSThomas Bogendoerfer /* ALSA driver */ 802862c2c0aSThomas Bogendoerfer 803862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_free(struct snd_sgio2audio *chip) 804862c2c0aSThomas Bogendoerfer { 805862c2c0aSThomas Bogendoerfer int i; 806862c2c0aSThomas Bogendoerfer 807862c2c0aSThomas Bogendoerfer /* reset interface */ 808862c2c0aSThomas Bogendoerfer writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control); 809862c2c0aSThomas Bogendoerfer udelay(1); 810862c2c0aSThomas Bogendoerfer writeq(0, &mace->perif.audio.control); 811862c2c0aSThomas Bogendoerfer 812862c2c0aSThomas Bogendoerfer /* release IRQ's */ 813862c2c0aSThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) 814862c2c0aSThomas Bogendoerfer free_irq(snd_sgio2_isr_table[i].irq, 815862c2c0aSThomas Bogendoerfer &chip->channel[snd_sgio2_isr_table[i].idx]); 816862c2c0aSThomas Bogendoerfer 817862c2c0aSThomas Bogendoerfer dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE, 818862c2c0aSThomas Bogendoerfer chip->ring_base, chip->ring_base_dma); 819862c2c0aSThomas Bogendoerfer 820862c2c0aSThomas Bogendoerfer /* release card data */ 821862c2c0aSThomas Bogendoerfer kfree(chip); 822862c2c0aSThomas Bogendoerfer return 0; 823862c2c0aSThomas Bogendoerfer } 824862c2c0aSThomas Bogendoerfer 825862c2c0aSThomas Bogendoerfer static int snd_sgio2audio_dev_free(struct snd_device *device) 826862c2c0aSThomas Bogendoerfer { 827862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip = device->device_data; 828862c2c0aSThomas Bogendoerfer 829862c2c0aSThomas Bogendoerfer return snd_sgio2audio_free(chip); 830862c2c0aSThomas Bogendoerfer } 831862c2c0aSThomas Bogendoerfer 832862c2c0aSThomas Bogendoerfer static struct snd_device_ops ops = { 833862c2c0aSThomas Bogendoerfer .dev_free = snd_sgio2audio_dev_free, 834862c2c0aSThomas Bogendoerfer }; 835862c2c0aSThomas Bogendoerfer 836862c2c0aSThomas Bogendoerfer static int __devinit snd_sgio2audio_create(struct snd_card *card, 837862c2c0aSThomas Bogendoerfer struct snd_sgio2audio **rchip) 838862c2c0aSThomas Bogendoerfer { 839862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip; 840862c2c0aSThomas Bogendoerfer int i, err; 841862c2c0aSThomas Bogendoerfer 842862c2c0aSThomas Bogendoerfer *rchip = NULL; 843862c2c0aSThomas Bogendoerfer 844862c2c0aSThomas Bogendoerfer /* check if a codec is attached to the interface */ 845862c2c0aSThomas Bogendoerfer /* (Audio or Audio/Video board present) */ 846862c2c0aSThomas Bogendoerfer if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT)) 847862c2c0aSThomas Bogendoerfer return -ENOENT; 848862c2c0aSThomas Bogendoerfer 849862c2c0aSThomas Bogendoerfer chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL); 850862c2c0aSThomas Bogendoerfer if (chip == NULL) 851862c2c0aSThomas Bogendoerfer return -ENOMEM; 852862c2c0aSThomas Bogendoerfer 853862c2c0aSThomas Bogendoerfer chip->card = card; 854862c2c0aSThomas Bogendoerfer 855862c2c0aSThomas Bogendoerfer chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE, 856862c2c0aSThomas Bogendoerfer &chip->ring_base_dma, GFP_USER); 857862c2c0aSThomas Bogendoerfer if (chip->ring_base == NULL) { 858862c2c0aSThomas Bogendoerfer printk(KERN_ERR 859862c2c0aSThomas Bogendoerfer "sgio2audio: could not allocate ring buffers\n"); 860862c2c0aSThomas Bogendoerfer kfree(chip); 861862c2c0aSThomas Bogendoerfer return -ENOMEM; 862862c2c0aSThomas Bogendoerfer } 863862c2c0aSThomas Bogendoerfer 864862c2c0aSThomas Bogendoerfer spin_lock_init(&chip->ad1843_lock); 865862c2c0aSThomas Bogendoerfer 866862c2c0aSThomas Bogendoerfer /* initialize channels */ 867862c2c0aSThomas Bogendoerfer for (i = 0; i < 3; i++) { 868862c2c0aSThomas Bogendoerfer spin_lock_init(&chip->channel[i].lock); 869862c2c0aSThomas Bogendoerfer chip->channel[i].idx = i; 870862c2c0aSThomas Bogendoerfer } 871862c2c0aSThomas Bogendoerfer 872862c2c0aSThomas Bogendoerfer /* allocate IRQs */ 873862c2c0aSThomas Bogendoerfer for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) { 874862c2c0aSThomas Bogendoerfer if (request_irq(snd_sgio2_isr_table[i].irq, 875862c2c0aSThomas Bogendoerfer snd_sgio2_isr_table[i].isr, 876862c2c0aSThomas Bogendoerfer 0, 877862c2c0aSThomas Bogendoerfer snd_sgio2_isr_table[i].desc, 878862c2c0aSThomas Bogendoerfer &chip->channel[snd_sgio2_isr_table[i].idx])) { 879862c2c0aSThomas Bogendoerfer snd_sgio2audio_free(chip); 880862c2c0aSThomas Bogendoerfer printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n", 881862c2c0aSThomas Bogendoerfer snd_sgio2_isr_table[i].irq); 882862c2c0aSThomas Bogendoerfer return -EBUSY; 883862c2c0aSThomas Bogendoerfer } 884862c2c0aSThomas Bogendoerfer } 885862c2c0aSThomas Bogendoerfer 886862c2c0aSThomas Bogendoerfer /* reset the interface */ 887862c2c0aSThomas Bogendoerfer writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control); 888862c2c0aSThomas Bogendoerfer udelay(1); 889862c2c0aSThomas Bogendoerfer writeq(0, &mace->perif.audio.control); 890862c2c0aSThomas Bogendoerfer msleep_interruptible(1); /* give time to recover */ 891862c2c0aSThomas Bogendoerfer 892862c2c0aSThomas Bogendoerfer /* set ring base */ 893862c2c0aSThomas Bogendoerfer writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase); 894862c2c0aSThomas Bogendoerfer 895862c2c0aSThomas Bogendoerfer /* attach the AD1843 codec */ 896862c2c0aSThomas Bogendoerfer chip->ad1843.read = read_ad1843_reg; 897862c2c0aSThomas Bogendoerfer chip->ad1843.write = write_ad1843_reg; 898862c2c0aSThomas Bogendoerfer chip->ad1843.chip = chip; 899862c2c0aSThomas Bogendoerfer 900862c2c0aSThomas Bogendoerfer /* initialize the AD1843 codec */ 901862c2c0aSThomas Bogendoerfer err = ad1843_init(&chip->ad1843); 902862c2c0aSThomas Bogendoerfer if (err < 0) { 903862c2c0aSThomas Bogendoerfer snd_sgio2audio_free(chip); 904862c2c0aSThomas Bogendoerfer return err; 905862c2c0aSThomas Bogendoerfer } 906862c2c0aSThomas Bogendoerfer 907862c2c0aSThomas Bogendoerfer err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 908862c2c0aSThomas Bogendoerfer if (err < 0) { 909862c2c0aSThomas Bogendoerfer snd_sgio2audio_free(chip); 910862c2c0aSThomas Bogendoerfer return err; 911862c2c0aSThomas Bogendoerfer } 912862c2c0aSThomas Bogendoerfer *rchip = chip; 913862c2c0aSThomas Bogendoerfer return 0; 914862c2c0aSThomas Bogendoerfer } 915862c2c0aSThomas Bogendoerfer 916862c2c0aSThomas Bogendoerfer static int __devinit snd_sgio2audio_probe(struct platform_device *pdev) 917862c2c0aSThomas Bogendoerfer { 918862c2c0aSThomas Bogendoerfer struct snd_card *card; 919862c2c0aSThomas Bogendoerfer struct snd_sgio2audio *chip; 920862c2c0aSThomas Bogendoerfer int err; 921862c2c0aSThomas Bogendoerfer 922bd7dd77cSTakashi Iwai err = snd_card_create(index, id, THIS_MODULE, 0, &card); 923bd7dd77cSTakashi Iwai if (err < 0) 924bd7dd77cSTakashi Iwai return err; 925862c2c0aSThomas Bogendoerfer 926862c2c0aSThomas Bogendoerfer err = snd_sgio2audio_create(card, &chip); 927862c2c0aSThomas Bogendoerfer if (err < 0) { 928862c2c0aSThomas Bogendoerfer snd_card_free(card); 929862c2c0aSThomas Bogendoerfer return err; 930862c2c0aSThomas Bogendoerfer } 931862c2c0aSThomas Bogendoerfer snd_card_set_dev(card, &pdev->dev); 932862c2c0aSThomas Bogendoerfer 933862c2c0aSThomas Bogendoerfer err = snd_sgio2audio_new_pcm(chip); 934862c2c0aSThomas Bogendoerfer if (err < 0) { 935862c2c0aSThomas Bogendoerfer snd_card_free(card); 936862c2c0aSThomas Bogendoerfer return err; 937862c2c0aSThomas Bogendoerfer } 938862c2c0aSThomas Bogendoerfer err = snd_sgio2audio_new_mixer(chip); 939862c2c0aSThomas Bogendoerfer if (err < 0) { 940862c2c0aSThomas Bogendoerfer snd_card_free(card); 941862c2c0aSThomas Bogendoerfer return err; 942862c2c0aSThomas Bogendoerfer } 943862c2c0aSThomas Bogendoerfer 944862c2c0aSThomas Bogendoerfer strcpy(card->driver, "SGI O2 Audio"); 945862c2c0aSThomas Bogendoerfer strcpy(card->shortname, "SGI O2 Audio"); 946862c2c0aSThomas Bogendoerfer sprintf(card->longname, "%s irq %i-%i", 947862c2c0aSThomas Bogendoerfer card->shortname, 948862c2c0aSThomas Bogendoerfer MACEISA_AUDIO1_DMAT_IRQ, 949862c2c0aSThomas Bogendoerfer MACEISA_AUDIO3_MERR_IRQ); 950862c2c0aSThomas Bogendoerfer 951862c2c0aSThomas Bogendoerfer err = snd_card_register(card); 952862c2c0aSThomas Bogendoerfer if (err < 0) { 953862c2c0aSThomas Bogendoerfer snd_card_free(card); 954862c2c0aSThomas Bogendoerfer return err; 955862c2c0aSThomas Bogendoerfer } 956862c2c0aSThomas Bogendoerfer platform_set_drvdata(pdev, card); 957862c2c0aSThomas Bogendoerfer return 0; 958862c2c0aSThomas Bogendoerfer } 959862c2c0aSThomas Bogendoerfer 9602f229a31STakashi Iwai static int __devexit snd_sgio2audio_remove(struct platform_device *pdev) 961862c2c0aSThomas Bogendoerfer { 962862c2c0aSThomas Bogendoerfer struct snd_card *card = platform_get_drvdata(pdev); 963862c2c0aSThomas Bogendoerfer 964862c2c0aSThomas Bogendoerfer snd_card_free(card); 965862c2c0aSThomas Bogendoerfer platform_set_drvdata(pdev, NULL); 966862c2c0aSThomas Bogendoerfer return 0; 967862c2c0aSThomas Bogendoerfer } 968862c2c0aSThomas Bogendoerfer 969862c2c0aSThomas Bogendoerfer static struct platform_driver sgio2audio_driver = { 970862c2c0aSThomas Bogendoerfer .probe = snd_sgio2audio_probe, 971862c2c0aSThomas Bogendoerfer .remove = __devexit_p(snd_sgio2audio_remove), 972862c2c0aSThomas Bogendoerfer .driver = { 973862c2c0aSThomas Bogendoerfer .name = "sgio2audio", 974862c2c0aSThomas Bogendoerfer .owner = THIS_MODULE, 975862c2c0aSThomas Bogendoerfer } 976862c2c0aSThomas Bogendoerfer }; 977862c2c0aSThomas Bogendoerfer 978862c2c0aSThomas Bogendoerfer static int __init alsa_card_sgio2audio_init(void) 979862c2c0aSThomas Bogendoerfer { 980862c2c0aSThomas Bogendoerfer return platform_driver_register(&sgio2audio_driver); 981862c2c0aSThomas Bogendoerfer } 982862c2c0aSThomas Bogendoerfer 983862c2c0aSThomas Bogendoerfer static void __exit alsa_card_sgio2audio_exit(void) 984862c2c0aSThomas Bogendoerfer { 985862c2c0aSThomas Bogendoerfer platform_driver_unregister(&sgio2audio_driver); 986862c2c0aSThomas Bogendoerfer } 987862c2c0aSThomas Bogendoerfer 988862c2c0aSThomas Bogendoerfer module_init(alsa_card_sgio2audio_init) 989862c2c0aSThomas Bogendoerfer module_exit(alsa_card_sgio2audio_exit) 990