162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Sound driver for Silicon Graphics O2 Workstations A/V board audio. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2003 Vivien Chappelier <vivien.chappelier@linux-mips.org> 662306a36Sopenharmony_ci * Copyright 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de> 762306a36Sopenharmony_ci * Mxier part taken from mace_audio.c: 862306a36Sopenharmony_ci * Copyright 2007 Thorben Jändling <tj.trevelyan@gmail.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/ip32/ip32_ints.h> 2262306a36Sopenharmony_ci#include <asm/ip32/mace.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <sound/core.h> 2562306a36Sopenharmony_ci#include <sound/control.h> 2662306a36Sopenharmony_ci#include <sound/pcm.h> 2762306a36Sopenharmony_ci#define SNDRV_GET_ID 2862306a36Sopenharmony_ci#include <sound/initval.h> 2962306a36Sopenharmony_ci#include <sound/ad1843.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciMODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org>"); 3362306a36Sopenharmony_ciMODULE_DESCRIPTION("SGI O2 Audio"); 3462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 3762306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cimodule_param(index, int, 0444); 4062306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for SGI O2 soundcard."); 4162306a36Sopenharmony_cimodule_param(id, charp, 0444); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for SGI O2 soundcard."); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define AUDIO_CONTROL_RESET BIT(0) /* 1: reset audio interface */ 4662306a36Sopenharmony_ci#define AUDIO_CONTROL_CODEC_PRESENT BIT(1) /* 1: codec detected */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define CODEC_CONTROL_WORD_SHIFT 0 4962306a36Sopenharmony_ci#define CODEC_CONTROL_READ BIT(16) 5062306a36Sopenharmony_ci#define CODEC_CONTROL_ADDRESS_SHIFT 17 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define CHANNEL_CONTROL_RESET BIT(10) /* 1: reset channel */ 5362306a36Sopenharmony_ci#define CHANNEL_DMA_ENABLE BIT(9) /* 1: enable DMA transfer */ 5462306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_DISABLED (0 << 5) /* interrupt disabled */ 5562306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */ 5662306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_50 (2 << 5) /* int on buffer >50% full */ 5762306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */ 5862306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_EMPTY (4 << 5) /* int on buffer empty */ 5962306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */ 6062306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_FULL (6 << 5) /* int on buffer empty */ 6162306a36Sopenharmony_ci#define CHANNEL_INT_THRESHOLD_NOT_FULL (7 << 5) /* int on buffer !empty */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define CHANNEL_RING_SHIFT 12 6462306a36Sopenharmony_ci#define CHANNEL_RING_SIZE (1 << CHANNEL_RING_SHIFT) 6562306a36Sopenharmony_ci#define CHANNEL_RING_MASK (CHANNEL_RING_SIZE - 1) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define CHANNEL_LEFT_SHIFT 40 6862306a36Sopenharmony_ci#define CHANNEL_RIGHT_SHIFT 8 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct snd_sgio2audio_chan { 7162306a36Sopenharmony_ci int idx; 7262306a36Sopenharmony_ci struct snd_pcm_substream *substream; 7362306a36Sopenharmony_ci int pos; 7462306a36Sopenharmony_ci snd_pcm_uframes_t size; 7562306a36Sopenharmony_ci spinlock_t lock; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* definition of the chip-specific record */ 7962306a36Sopenharmony_cistruct snd_sgio2audio { 8062306a36Sopenharmony_ci struct snd_card *card; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* codec */ 8362306a36Sopenharmony_ci struct snd_ad1843 ad1843; 8462306a36Sopenharmony_ci spinlock_t ad1843_lock; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* channels */ 8762306a36Sopenharmony_ci struct snd_sgio2audio_chan channel[3]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* resources */ 9062306a36Sopenharmony_ci void *ring_base; 9162306a36Sopenharmony_ci dma_addr_t ring_base_dma; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* AD1843 access */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci * read_ad1843_reg returns the current contents of a 16 bit AD1843 register. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Returns unsigned register value on success, -errno on failure. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic int read_ad1843_reg(void *priv, int reg) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct snd_sgio2audio *chip = priv; 10462306a36Sopenharmony_ci int val; 10562306a36Sopenharmony_ci unsigned long flags; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci spin_lock_irqsave(&chip->ad1843_lock, flags); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) | 11062306a36Sopenharmony_ci CODEC_CONTROL_READ, &mace->perif.audio.codec_control); 11162306a36Sopenharmony_ci wmb(); 11262306a36Sopenharmony_ci val = readq(&mace->perif.audio.codec_control); /* flush bus */ 11362306a36Sopenharmony_ci udelay(200); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci val = readq(&mace->perif.audio.codec_read); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->ad1843_lock, flags); 11862306a36Sopenharmony_ci return val; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * write_ad1843_reg writes the specified value to a 16 bit AD1843 register. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistatic int write_ad1843_reg(void *priv, int reg, int word) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct snd_sgio2audio *chip = priv; 12762306a36Sopenharmony_ci int val; 12862306a36Sopenharmony_ci unsigned long flags; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci spin_lock_irqsave(&chip->ad1843_lock, flags); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) | 13362306a36Sopenharmony_ci (word << CODEC_CONTROL_WORD_SHIFT), 13462306a36Sopenharmony_ci &mace->perif.audio.codec_control); 13562306a36Sopenharmony_ci wmb(); 13662306a36Sopenharmony_ci val = readq(&mace->perif.audio.codec_control); /* flush bus */ 13762306a36Sopenharmony_ci udelay(200); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->ad1843_lock, flags); 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int sgio2audio_gain_info(struct snd_kcontrol *kcontrol, 14462306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 14962306a36Sopenharmony_ci uinfo->count = 2; 15062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 15162306a36Sopenharmony_ci uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843, 15262306a36Sopenharmony_ci (int)kcontrol->private_value); 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int sgio2audio_gain_get(struct snd_kcontrol *kcontrol, 15762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 16062306a36Sopenharmony_ci int vol; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF; 16562306a36Sopenharmony_ci ucontrol->value.integer.value[1] = vol & 0xFF; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int sgio2audio_gain_put(struct snd_kcontrol *kcontrol, 17162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 17462306a36Sopenharmony_ci int newvol, oldvol; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value); 17762306a36Sopenharmony_ci newvol = (ucontrol->value.integer.value[0] << 8) | 17862306a36Sopenharmony_ci ucontrol->value.integer.value[1]; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value, 18162306a36Sopenharmony_ci newvol); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return newvol != oldvol; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int sgio2audio_source_info(struct snd_kcontrol *kcontrol, 18762306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci static const char * const texts[3] = { 19062306a36Sopenharmony_ci "Cam Mic", "Mic", "Line" 19162306a36Sopenharmony_ci }; 19262306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 3, texts); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int sgio2audio_source_get(struct snd_kcontrol *kcontrol, 19662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int sgio2audio_source_put(struct snd_kcontrol *kcontrol, 20562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); 20862306a36Sopenharmony_ci int newsrc, oldsrc; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci oldsrc = ad1843_get_recsrc(&chip->ad1843); 21162306a36Sopenharmony_ci newsrc = ad1843_set_recsrc(&chip->ad1843, 21262306a36Sopenharmony_ci ucontrol->value.enumerated.item[0]); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return newsrc != oldsrc; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* dac1/pcm0 mixer control */ 21862306a36Sopenharmony_cistatic const struct snd_kcontrol_new sgio2audio_ctrl_pcm0 = { 21962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 22062306a36Sopenharmony_ci .name = "PCM Playback Volume", 22162306a36Sopenharmony_ci .index = 0, 22262306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 22362306a36Sopenharmony_ci .private_value = AD1843_GAIN_PCM_0, 22462306a36Sopenharmony_ci .info = sgio2audio_gain_info, 22562306a36Sopenharmony_ci .get = sgio2audio_gain_get, 22662306a36Sopenharmony_ci .put = sgio2audio_gain_put, 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* dac2/pcm1 mixer control */ 23062306a36Sopenharmony_cistatic const struct snd_kcontrol_new sgio2audio_ctrl_pcm1 = { 23162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 23262306a36Sopenharmony_ci .name = "PCM Playback Volume", 23362306a36Sopenharmony_ci .index = 1, 23462306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 23562306a36Sopenharmony_ci .private_value = AD1843_GAIN_PCM_1, 23662306a36Sopenharmony_ci .info = sgio2audio_gain_info, 23762306a36Sopenharmony_ci .get = sgio2audio_gain_get, 23862306a36Sopenharmony_ci .put = sgio2audio_gain_put, 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* record level mixer control */ 24262306a36Sopenharmony_cistatic const struct snd_kcontrol_new sgio2audio_ctrl_reclevel = { 24362306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 24462306a36Sopenharmony_ci .name = "Capture Volume", 24562306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 24662306a36Sopenharmony_ci .private_value = AD1843_GAIN_RECLEV, 24762306a36Sopenharmony_ci .info = sgio2audio_gain_info, 24862306a36Sopenharmony_ci .get = sgio2audio_gain_get, 24962306a36Sopenharmony_ci .put = sgio2audio_gain_put, 25062306a36Sopenharmony_ci}; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* record level source control */ 25362306a36Sopenharmony_cistatic const struct snd_kcontrol_new sgio2audio_ctrl_recsource = { 25462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25562306a36Sopenharmony_ci .name = "Capture Source", 25662306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 25762306a36Sopenharmony_ci .info = sgio2audio_source_info, 25862306a36Sopenharmony_ci .get = sgio2audio_source_get, 25962306a36Sopenharmony_ci .put = sgio2audio_source_put, 26062306a36Sopenharmony_ci}; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* line mixer control */ 26362306a36Sopenharmony_cistatic const struct snd_kcontrol_new sgio2audio_ctrl_line = { 26462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26562306a36Sopenharmony_ci .name = "Line Playback Volume", 26662306a36Sopenharmony_ci .index = 0, 26762306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 26862306a36Sopenharmony_ci .private_value = AD1843_GAIN_LINE, 26962306a36Sopenharmony_ci .info = sgio2audio_gain_info, 27062306a36Sopenharmony_ci .get = sgio2audio_gain_get, 27162306a36Sopenharmony_ci .put = sgio2audio_gain_put, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* cd mixer control */ 27562306a36Sopenharmony_cistatic const struct snd_kcontrol_new sgio2audio_ctrl_cd = { 27662306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 27762306a36Sopenharmony_ci .name = "Line Playback Volume", 27862306a36Sopenharmony_ci .index = 1, 27962306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 28062306a36Sopenharmony_ci .private_value = AD1843_GAIN_LINE_2, 28162306a36Sopenharmony_ci .info = sgio2audio_gain_info, 28262306a36Sopenharmony_ci .get = sgio2audio_gain_get, 28362306a36Sopenharmony_ci .put = sgio2audio_gain_put, 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* mic mixer control */ 28762306a36Sopenharmony_cistatic const struct snd_kcontrol_new sgio2audio_ctrl_mic = { 28862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 28962306a36Sopenharmony_ci .name = "Mic Playback Volume", 29062306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 29162306a36Sopenharmony_ci .private_value = AD1843_GAIN_MIC, 29262306a36Sopenharmony_ci .info = sgio2audio_gain_info, 29362306a36Sopenharmony_ci .get = sgio2audio_gain_get, 29462306a36Sopenharmony_ci .put = sgio2audio_gain_put, 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci int err; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 30362306a36Sopenharmony_ci snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip)); 30462306a36Sopenharmony_ci if (err < 0) 30562306a36Sopenharmony_ci return err; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 30862306a36Sopenharmony_ci snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip)); 30962306a36Sopenharmony_ci if (err < 0) 31062306a36Sopenharmony_ci return err; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 31362306a36Sopenharmony_ci snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip)); 31462306a36Sopenharmony_ci if (err < 0) 31562306a36Sopenharmony_ci return err; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 31862306a36Sopenharmony_ci snd_ctl_new1(&sgio2audio_ctrl_recsource, chip)); 31962306a36Sopenharmony_ci if (err < 0) 32062306a36Sopenharmony_ci return err; 32162306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 32262306a36Sopenharmony_ci snd_ctl_new1(&sgio2audio_ctrl_line, chip)); 32362306a36Sopenharmony_ci if (err < 0) 32462306a36Sopenharmony_ci return err; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 32762306a36Sopenharmony_ci snd_ctl_new1(&sgio2audio_ctrl_cd, chip)); 32862306a36Sopenharmony_ci if (err < 0) 32962306a36Sopenharmony_ci return err; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci err = snd_ctl_add(chip->card, 33262306a36Sopenharmony_ci snd_ctl_new1(&sgio2audio_ctrl_mic, chip)); 33362306a36Sopenharmony_ci if (err < 0) 33462306a36Sopenharmony_ci return err; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* low-level audio interface DMA */ 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* get data out of bounce buffer, count must be a multiple of 32 */ 34262306a36Sopenharmony_ci/* returns 1 if a period has elapsed */ 34362306a36Sopenharmony_cistatic int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip, 34462306a36Sopenharmony_ci unsigned int ch, unsigned int count) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci int ret; 34762306a36Sopenharmony_ci unsigned long src_base, src_pos, dst_mask; 34862306a36Sopenharmony_ci unsigned char *dst_base; 34962306a36Sopenharmony_ci int dst_pos; 35062306a36Sopenharmony_ci u64 *src; 35162306a36Sopenharmony_ci s16 *dst; 35262306a36Sopenharmony_ci u64 x; 35362306a36Sopenharmony_ci unsigned long flags; 35462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci spin_lock_irqsave(&chip->channel[ch].lock, flags); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT); 35962306a36Sopenharmony_ci src_pos = readq(&mace->perif.audio.chan[ch].read_ptr); 36062306a36Sopenharmony_ci dst_base = runtime->dma_area; 36162306a36Sopenharmony_ci dst_pos = chip->channel[ch].pos; 36262306a36Sopenharmony_ci dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* check if a period has elapsed */ 36562306a36Sopenharmony_ci chip->channel[ch].size += (count >> 3); /* in frames */ 36662306a36Sopenharmony_ci ret = chip->channel[ch].size >= runtime->period_size; 36762306a36Sopenharmony_ci chip->channel[ch].size %= runtime->period_size; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci while (count) { 37062306a36Sopenharmony_ci src = (u64 *)(src_base + src_pos); 37162306a36Sopenharmony_ci dst = (s16 *)(dst_base + dst_pos); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci x = *src; 37462306a36Sopenharmony_ci dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff; 37562306a36Sopenharmony_ci dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK; 37862306a36Sopenharmony_ci dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask; 37962306a36Sopenharmony_ci count -= sizeof(u64); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */ 38362306a36Sopenharmony_ci chip->channel[ch].pos = dst_pos; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->channel[ch].lock, flags); 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* put some DMA data in bounce buffer, count must be a multiple of 32 */ 39062306a36Sopenharmony_ci/* returns 1 if a period has elapsed */ 39162306a36Sopenharmony_cistatic int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip, 39262306a36Sopenharmony_ci unsigned int ch, unsigned int count) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci int ret; 39562306a36Sopenharmony_ci s64 l, r; 39662306a36Sopenharmony_ci unsigned long dst_base, dst_pos, src_mask; 39762306a36Sopenharmony_ci unsigned char *src_base; 39862306a36Sopenharmony_ci int src_pos; 39962306a36Sopenharmony_ci u64 *dst; 40062306a36Sopenharmony_ci s16 *src; 40162306a36Sopenharmony_ci unsigned long flags; 40262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci spin_lock_irqsave(&chip->channel[ch].lock, flags); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT); 40762306a36Sopenharmony_ci dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr); 40862306a36Sopenharmony_ci src_base = runtime->dma_area; 40962306a36Sopenharmony_ci src_pos = chip->channel[ch].pos; 41062306a36Sopenharmony_ci src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* check if a period has elapsed */ 41362306a36Sopenharmony_ci chip->channel[ch].size += (count >> 3); /* in frames */ 41462306a36Sopenharmony_ci ret = chip->channel[ch].size >= runtime->period_size; 41562306a36Sopenharmony_ci chip->channel[ch].size %= runtime->period_size; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci while (count) { 41862306a36Sopenharmony_ci src = (s16 *)(src_base + src_pos); 41962306a36Sopenharmony_ci dst = (u64 *)(dst_base + dst_pos); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci l = src[0]; /* sign extend */ 42262306a36Sopenharmony_ci r = src[1]; /* sign extend */ 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci *dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) | 42562306a36Sopenharmony_ci ((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK; 42862306a36Sopenharmony_ci src_pos = (src_pos + 2 * sizeof(s16)) & src_mask; 42962306a36Sopenharmony_ci count -= sizeof(u64); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */ 43362306a36Sopenharmony_ci chip->channel[ch].pos = src_pos; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->channel[ch].lock, flags); 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 44262306a36Sopenharmony_ci struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 44362306a36Sopenharmony_ci int ch = chan->idx; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* reset DMA channel */ 44662306a36Sopenharmony_ci writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control); 44762306a36Sopenharmony_ci udelay(10); 44862306a36Sopenharmony_ci writeq(0, &mace->perif.audio.chan[ch].control); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 45162306a36Sopenharmony_ci /* push a full buffer */ 45262306a36Sopenharmony_ci snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci /* set DMA to wake on 50% empty and enable interrupt */ 45562306a36Sopenharmony_ci writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50, 45662306a36Sopenharmony_ci &mace->perif.audio.chan[ch].control); 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci writeq(0, &mace->perif.audio.chan[chan->idx].control); 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cistatic irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct snd_sgio2audio_chan *chan = dev_id; 47162306a36Sopenharmony_ci struct snd_pcm_substream *substream; 47262306a36Sopenharmony_ci struct snd_sgio2audio *chip; 47362306a36Sopenharmony_ci int count, ch; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci substream = chan->substream; 47662306a36Sopenharmony_ci chip = snd_pcm_substream_chip(substream); 47762306a36Sopenharmony_ci ch = chan->idx; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* empty the ring */ 48062306a36Sopenharmony_ci count = CHANNEL_RING_SIZE - 48162306a36Sopenharmony_ci readq(&mace->perif.audio.chan[ch].depth) - 32; 48262306a36Sopenharmony_ci if (snd_sgio2audio_dma_pull_frag(chip, ch, count)) 48362306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return IRQ_HANDLED; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct snd_sgio2audio_chan *chan = dev_id; 49162306a36Sopenharmony_ci struct snd_pcm_substream *substream; 49262306a36Sopenharmony_ci struct snd_sgio2audio *chip; 49362306a36Sopenharmony_ci int count, ch; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci substream = chan->substream; 49662306a36Sopenharmony_ci chip = snd_pcm_substream_chip(substream); 49762306a36Sopenharmony_ci ch = chan->idx; 49862306a36Sopenharmony_ci /* fill the ring */ 49962306a36Sopenharmony_ci count = CHANNEL_RING_SIZE - 50062306a36Sopenharmony_ci readq(&mace->perif.audio.chan[ch].depth) - 32; 50162306a36Sopenharmony_ci if (snd_sgio2audio_dma_push_frag(chip, ch, count)) 50262306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return IRQ_HANDLED; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct snd_sgio2audio_chan *chan = dev_id; 51062306a36Sopenharmony_ci struct snd_pcm_substream *substream; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci substream = chan->substream; 51362306a36Sopenharmony_ci snd_sgio2audio_dma_stop(substream); 51462306a36Sopenharmony_ci snd_sgio2audio_dma_start(substream); 51562306a36Sopenharmony_ci return IRQ_HANDLED; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* PCM part */ 51962306a36Sopenharmony_ci/* PCM hardware definition */ 52062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_sgio2audio_pcm_hw = { 52162306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 52262306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 52362306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 52462306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER), 52562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_BE, 52662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_48000, 52762306a36Sopenharmony_ci .rate_min = 8000, 52862306a36Sopenharmony_ci .rate_max = 48000, 52962306a36Sopenharmony_ci .channels_min = 2, 53062306a36Sopenharmony_ci .channels_max = 2, 53162306a36Sopenharmony_ci .buffer_bytes_max = 65536, 53262306a36Sopenharmony_ci .period_bytes_min = 32768, 53362306a36Sopenharmony_ci .period_bytes_max = 65536, 53462306a36Sopenharmony_ci .periods_min = 1, 53562306a36Sopenharmony_ci .periods_max = 1024, 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/* PCM playback open callback */ 53962306a36Sopenharmony_cistatic int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 54262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci runtime->hw = snd_sgio2audio_pcm_hw; 54562306a36Sopenharmony_ci runtime->private_data = &chip->channel[1]; 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 55262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci runtime->hw = snd_sgio2audio_pcm_hw; 55562306a36Sopenharmony_ci runtime->private_data = &chip->channel[2]; 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci/* PCM capture open callback */ 56062306a36Sopenharmony_cistatic int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 56362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci runtime->hw = snd_sgio2audio_pcm_hw; 56662306a36Sopenharmony_ci runtime->private_data = &chip->channel[0]; 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* PCM close callback */ 57162306a36Sopenharmony_cistatic int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci runtime->private_data = NULL; 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci/* prepare callback */ 58062306a36Sopenharmony_cistatic int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 58362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 58462306a36Sopenharmony_ci struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 58562306a36Sopenharmony_ci int ch = chan->idx; 58662306a36Sopenharmony_ci unsigned long flags; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci spin_lock_irqsave(&chip->channel[ch].lock, flags); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Setup the pseudo-dma transfer pointers. */ 59162306a36Sopenharmony_ci chip->channel[ch].pos = 0; 59262306a36Sopenharmony_ci chip->channel[ch].size = 0; 59362306a36Sopenharmony_ci chip->channel[ch].substream = substream; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* set AD1843 format */ 59662306a36Sopenharmony_ci /* hardware format is always S16_LE */ 59762306a36Sopenharmony_ci switch (substream->stream) { 59862306a36Sopenharmony_ci case SNDRV_PCM_STREAM_PLAYBACK: 59962306a36Sopenharmony_ci ad1843_setup_dac(&chip->ad1843, 60062306a36Sopenharmony_ci ch - 1, 60162306a36Sopenharmony_ci runtime->rate, 60262306a36Sopenharmony_ci SNDRV_PCM_FORMAT_S16_LE, 60362306a36Sopenharmony_ci runtime->channels); 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci case SNDRV_PCM_STREAM_CAPTURE: 60662306a36Sopenharmony_ci ad1843_setup_adc(&chip->ad1843, 60762306a36Sopenharmony_ci runtime->rate, 60862306a36Sopenharmony_ci SNDRV_PCM_FORMAT_S16_LE, 60962306a36Sopenharmony_ci runtime->channels); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->channel[ch].lock, flags); 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/* trigger callback */ 61762306a36Sopenharmony_cistatic int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream, 61862306a36Sopenharmony_ci int cmd) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci switch (cmd) { 62162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 62262306a36Sopenharmony_ci /* start the PCM engine */ 62362306a36Sopenharmony_ci snd_sgio2audio_dma_start(substream); 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 62662306a36Sopenharmony_ci /* stop the PCM engine */ 62762306a36Sopenharmony_ci snd_sgio2audio_dma_stop(substream); 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci default: 63062306a36Sopenharmony_ci return -EINVAL; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/* pointer callback */ 63662306a36Sopenharmony_cistatic snd_pcm_uframes_t 63762306a36Sopenharmony_cisnd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); 64062306a36Sopenharmony_ci struct snd_sgio2audio_chan *chan = substream->runtime->private_data; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* get the current hardware pointer */ 64362306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, 64462306a36Sopenharmony_ci chip->channel[chan->idx].pos); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/* operators */ 64862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_sgio2audio_playback1_ops = { 64962306a36Sopenharmony_ci .open = snd_sgio2audio_playback1_open, 65062306a36Sopenharmony_ci .close = snd_sgio2audio_pcm_close, 65162306a36Sopenharmony_ci .prepare = snd_sgio2audio_pcm_prepare, 65262306a36Sopenharmony_ci .trigger = snd_sgio2audio_pcm_trigger, 65362306a36Sopenharmony_ci .pointer = snd_sgio2audio_pcm_pointer, 65462306a36Sopenharmony_ci}; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_sgio2audio_playback2_ops = { 65762306a36Sopenharmony_ci .open = snd_sgio2audio_playback2_open, 65862306a36Sopenharmony_ci .close = snd_sgio2audio_pcm_close, 65962306a36Sopenharmony_ci .prepare = snd_sgio2audio_pcm_prepare, 66062306a36Sopenharmony_ci .trigger = snd_sgio2audio_pcm_trigger, 66162306a36Sopenharmony_ci .pointer = snd_sgio2audio_pcm_pointer, 66262306a36Sopenharmony_ci}; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_sgio2audio_capture_ops = { 66562306a36Sopenharmony_ci .open = snd_sgio2audio_capture_open, 66662306a36Sopenharmony_ci .close = snd_sgio2audio_pcm_close, 66762306a36Sopenharmony_ci .prepare = snd_sgio2audio_pcm_prepare, 66862306a36Sopenharmony_ci .trigger = snd_sgio2audio_pcm_trigger, 66962306a36Sopenharmony_ci .pointer = snd_sgio2audio_pcm_pointer, 67062306a36Sopenharmony_ci}; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci/* 67362306a36Sopenharmony_ci * definitions of capture are omitted here... 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/* create a pcm device */ 67762306a36Sopenharmony_cistatic int snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct snd_pcm *pcm; 68062306a36Sopenharmony_ci int err; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* create first pcm device with one outputs and one input */ 68362306a36Sopenharmony_ci err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm); 68462306a36Sopenharmony_ci if (err < 0) 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci pcm->private_data = chip; 68862306a36Sopenharmony_ci strcpy(pcm->name, "SGI O2 DAC1"); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* set operators */ 69162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 69262306a36Sopenharmony_ci &snd_sgio2audio_playback1_ops); 69362306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 69462306a36Sopenharmony_ci &snd_sgio2audio_capture_ops); 69562306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* create second pcm device with one outputs and no input */ 69862306a36Sopenharmony_ci err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm); 69962306a36Sopenharmony_ci if (err < 0) 70062306a36Sopenharmony_ci return err; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci pcm->private_data = chip; 70362306a36Sopenharmony_ci strcpy(pcm->name, "SGI O2 DAC2"); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* set operators */ 70662306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 70762306a36Sopenharmony_ci &snd_sgio2audio_playback2_ops); 70862306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic struct { 71462306a36Sopenharmony_ci int idx; 71562306a36Sopenharmony_ci int irq; 71662306a36Sopenharmony_ci irqreturn_t (*isr)(int, void *); 71762306a36Sopenharmony_ci const char *desc; 71862306a36Sopenharmony_ci} snd_sgio2_isr_table[] = { 71962306a36Sopenharmony_ci { 72062306a36Sopenharmony_ci .idx = 0, 72162306a36Sopenharmony_ci .irq = MACEISA_AUDIO1_DMAT_IRQ, 72262306a36Sopenharmony_ci .isr = snd_sgio2audio_dma_in_isr, 72362306a36Sopenharmony_ci .desc = "Capture DMA Channel 0" 72462306a36Sopenharmony_ci }, { 72562306a36Sopenharmony_ci .idx = 0, 72662306a36Sopenharmony_ci .irq = MACEISA_AUDIO1_OF_IRQ, 72762306a36Sopenharmony_ci .isr = snd_sgio2audio_error_isr, 72862306a36Sopenharmony_ci .desc = "Capture Overflow" 72962306a36Sopenharmony_ci }, { 73062306a36Sopenharmony_ci .idx = 1, 73162306a36Sopenharmony_ci .irq = MACEISA_AUDIO2_DMAT_IRQ, 73262306a36Sopenharmony_ci .isr = snd_sgio2audio_dma_out_isr, 73362306a36Sopenharmony_ci .desc = "Playback DMA Channel 1" 73462306a36Sopenharmony_ci }, { 73562306a36Sopenharmony_ci .idx = 1, 73662306a36Sopenharmony_ci .irq = MACEISA_AUDIO2_MERR_IRQ, 73762306a36Sopenharmony_ci .isr = snd_sgio2audio_error_isr, 73862306a36Sopenharmony_ci .desc = "Memory Error Channel 1" 73962306a36Sopenharmony_ci }, { 74062306a36Sopenharmony_ci .idx = 2, 74162306a36Sopenharmony_ci .irq = MACEISA_AUDIO3_DMAT_IRQ, 74262306a36Sopenharmony_ci .isr = snd_sgio2audio_dma_out_isr, 74362306a36Sopenharmony_ci .desc = "Playback DMA Channel 2" 74462306a36Sopenharmony_ci }, { 74562306a36Sopenharmony_ci .idx = 2, 74662306a36Sopenharmony_ci .irq = MACEISA_AUDIO3_MERR_IRQ, 74762306a36Sopenharmony_ci .isr = snd_sgio2audio_error_isr, 74862306a36Sopenharmony_ci .desc = "Memory Error Channel 2" 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci}; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci/* ALSA driver */ 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int snd_sgio2audio_free(struct snd_sgio2audio *chip) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci int i; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* reset interface */ 75962306a36Sopenharmony_ci writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control); 76062306a36Sopenharmony_ci udelay(1); 76162306a36Sopenharmony_ci writeq(0, &mace->perif.audio.control); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* release IRQ's */ 76462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) 76562306a36Sopenharmony_ci free_irq(snd_sgio2_isr_table[i].irq, 76662306a36Sopenharmony_ci &chip->channel[snd_sgio2_isr_table[i].idx]); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci dma_free_coherent(chip->card->dev, MACEISA_RINGBUFFERS_SIZE, 76962306a36Sopenharmony_ci chip->ring_base, chip->ring_base_dma); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* release card data */ 77262306a36Sopenharmony_ci kfree(chip); 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic int snd_sgio2audio_dev_free(struct snd_device *device) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct snd_sgio2audio *chip = device->device_data; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return snd_sgio2audio_free(chip); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic const struct snd_device_ops ops = { 78462306a36Sopenharmony_ci .dev_free = snd_sgio2audio_dev_free, 78562306a36Sopenharmony_ci}; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int snd_sgio2audio_create(struct snd_card *card, 78862306a36Sopenharmony_ci struct snd_sgio2audio **rchip) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct snd_sgio2audio *chip; 79162306a36Sopenharmony_ci int i, err; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci *rchip = NULL; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* check if a codec is attached to the interface */ 79662306a36Sopenharmony_ci /* (Audio or Audio/Video board present) */ 79762306a36Sopenharmony_ci if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT)) 79862306a36Sopenharmony_ci return -ENOENT; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 80162306a36Sopenharmony_ci if (chip == NULL) 80262306a36Sopenharmony_ci return -ENOMEM; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci chip->card = card; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci chip->ring_base = dma_alloc_coherent(card->dev, 80762306a36Sopenharmony_ci MACEISA_RINGBUFFERS_SIZE, 80862306a36Sopenharmony_ci &chip->ring_base_dma, GFP_KERNEL); 80962306a36Sopenharmony_ci if (chip->ring_base == NULL) { 81062306a36Sopenharmony_ci printk(KERN_ERR 81162306a36Sopenharmony_ci "sgio2audio: could not allocate ring buffers\n"); 81262306a36Sopenharmony_ci kfree(chip); 81362306a36Sopenharmony_ci return -ENOMEM; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci spin_lock_init(&chip->ad1843_lock); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* initialize channels */ 81962306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 82062306a36Sopenharmony_ci spin_lock_init(&chip->channel[i].lock); 82162306a36Sopenharmony_ci chip->channel[i].idx = i; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* allocate IRQs */ 82562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) { 82662306a36Sopenharmony_ci if (request_irq(snd_sgio2_isr_table[i].irq, 82762306a36Sopenharmony_ci snd_sgio2_isr_table[i].isr, 82862306a36Sopenharmony_ci 0, 82962306a36Sopenharmony_ci snd_sgio2_isr_table[i].desc, 83062306a36Sopenharmony_ci &chip->channel[snd_sgio2_isr_table[i].idx])) { 83162306a36Sopenharmony_ci snd_sgio2audio_free(chip); 83262306a36Sopenharmony_ci printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n", 83362306a36Sopenharmony_ci snd_sgio2_isr_table[i].irq); 83462306a36Sopenharmony_ci return -EBUSY; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* reset the interface */ 83962306a36Sopenharmony_ci writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control); 84062306a36Sopenharmony_ci udelay(1); 84162306a36Sopenharmony_ci writeq(0, &mace->perif.audio.control); 84262306a36Sopenharmony_ci msleep_interruptible(1); /* give time to recover */ 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* set ring base */ 84562306a36Sopenharmony_ci writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* attach the AD1843 codec */ 84862306a36Sopenharmony_ci chip->ad1843.read = read_ad1843_reg; 84962306a36Sopenharmony_ci chip->ad1843.write = write_ad1843_reg; 85062306a36Sopenharmony_ci chip->ad1843.chip = chip; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* initialize the AD1843 codec */ 85362306a36Sopenharmony_ci err = ad1843_init(&chip->ad1843); 85462306a36Sopenharmony_ci if (err < 0) { 85562306a36Sopenharmony_ci snd_sgio2audio_free(chip); 85662306a36Sopenharmony_ci return err; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 86062306a36Sopenharmony_ci if (err < 0) { 86162306a36Sopenharmony_ci snd_sgio2audio_free(chip); 86262306a36Sopenharmony_ci return err; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci *rchip = chip; 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic int snd_sgio2audio_probe(struct platform_device *pdev) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci struct snd_card *card; 87162306a36Sopenharmony_ci struct snd_sgio2audio *chip; 87262306a36Sopenharmony_ci int err; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card); 87562306a36Sopenharmony_ci if (err < 0) 87662306a36Sopenharmony_ci return err; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci err = snd_sgio2audio_create(card, &chip); 87962306a36Sopenharmony_ci if (err < 0) { 88062306a36Sopenharmony_ci snd_card_free(card); 88162306a36Sopenharmony_ci return err; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci err = snd_sgio2audio_new_pcm(chip); 88562306a36Sopenharmony_ci if (err < 0) { 88662306a36Sopenharmony_ci snd_card_free(card); 88762306a36Sopenharmony_ci return err; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci err = snd_sgio2audio_new_mixer(chip); 89062306a36Sopenharmony_ci if (err < 0) { 89162306a36Sopenharmony_ci snd_card_free(card); 89262306a36Sopenharmony_ci return err; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci strcpy(card->driver, "SGI O2 Audio"); 89662306a36Sopenharmony_ci strcpy(card->shortname, "SGI O2 Audio"); 89762306a36Sopenharmony_ci sprintf(card->longname, "%s irq %i-%i", 89862306a36Sopenharmony_ci card->shortname, 89962306a36Sopenharmony_ci MACEISA_AUDIO1_DMAT_IRQ, 90062306a36Sopenharmony_ci MACEISA_AUDIO3_MERR_IRQ); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci err = snd_card_register(card); 90362306a36Sopenharmony_ci if (err < 0) { 90462306a36Sopenharmony_ci snd_card_free(card); 90562306a36Sopenharmony_ci return err; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci platform_set_drvdata(pdev, card); 90862306a36Sopenharmony_ci return 0; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic void snd_sgio2audio_remove(struct platform_device *pdev) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct snd_card *card = platform_get_drvdata(pdev); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci snd_card_free(card); 91662306a36Sopenharmony_ci} 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic struct platform_driver sgio2audio_driver = { 91962306a36Sopenharmony_ci .probe = snd_sgio2audio_probe, 92062306a36Sopenharmony_ci .remove_new = snd_sgio2audio_remove, 92162306a36Sopenharmony_ci .driver = { 92262306a36Sopenharmony_ci .name = "sgio2audio", 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci}; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cimodule_platform_driver(sgio2audio_driver); 927