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