// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for A2 audio system used in SGI machines
* Copyright (c) 2008 Thomas Bogendoerfer <tsbogend@alpha.fanken.de>
*
* Based on OSS code from Ladislav Michl <ladis@linux-mips.org>, which
* was based on code from Ulf Carlsson
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm-indirect.h>
#include <sound/initval.h>
#include "hal2.h"
static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for SGI HAL2 soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for SGI HAL2 soundcard.");
MODULE_DESCRIPTION("ALSA driver for SGI HAL2 audio");
MODULE_AUTHOR("Thomas Bogendoerfer");
MODULE_LICENSE("GPL");
#define H2_BLOCK_SIZE 1024
#define H2_BUF_SIZE 16384
struct hal2_pbus {
struct hpc3_pbus_dmacregs *pbus;
int pbusnr;
unsigned int ctrl; /* Current state of pbus->pbdma_ctrl */
};
struct hal2_desc {
struct hpc_dma_desc desc;
u32 pad; /* padding */
};
struct hal2_codec {
struct snd_pcm_indirect pcm_indirect;
struct snd_pcm_substream *substream;
unsigned char *buffer;
dma_addr_t buffer_dma;
struct hal2_desc *desc;
dma_addr_t desc_dma;
int desc_count;
struct hal2_pbus pbus;
int voices; /* mono/stereo */
unsigned int sample_rate;
unsigned int master; /* Master frequency */
unsigned short mod; /* MOD value */
unsigned short inc; /* INC value */
};
#define H2_MIX_OUTPUT_ATT 0
#define H2_MIX_INPUT_GAIN 1
struct snd_hal2 {
struct snd_card *card;
struct hal2_ctl_regs *ctl_regs; /* HAL2 ctl registers */
struct hal2_aes_regs *aes_regs; /* HAL2 aes registers */
struct hal2_vol_regs *vol_regs; /* HAL2 vol registers */
struct hal2_syn_regs *syn_regs; /* HAL2 syn registers */
struct hal2_codec dac;
struct hal2_codec adc;
};
#define H2_INDIRECT_WAIT(regs) while (hal2_read(®s->isr) & H2_ISR_TSTATUS);
#define H2_READ_ADDR(addr) (addr | (1<<7))
#define H2_WRITE_ADDR(addr) (addr)
static inline u32 hal2_read(u32 *reg)
{
return __raw_readl(reg);
}
static inline void hal2_write(u32 val, u32 *reg)
{
__raw_writel(val, reg);
}
static u32 hal2_i_read32(struct snd_hal2 *hal2, u16 addr)
{
u32 ret;
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(H2_READ_ADDR(addr), ®s->iar);
H2_INDIRECT_WAIT(regs);
ret = hal2_read(®s->idr0) & 0xffff;
hal2_write(H2_READ_ADDR(addr) | 0x1, ®s->iar);
H2_INDIRECT_WAIT(regs);
ret |= (hal2_read(®s->idr0) & 0xffff) << 16;
return ret;
}
static void hal2_i_write16(struct snd_hal2 *hal2, u16 addr, u16 val)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(val, ®s->idr0);
hal2_write(0, ®s->idr1);
hal2_write(0, ®s->idr2);
hal2_write(0, ®s->idr3);
hal2_write(H2_WRITE_ADDR(addr), ®s->iar);
H2_INDIRECT_WAIT(regs);
}
static void hal2_i_write32(struct snd_hal2 *hal2, u16 addr, u32 val)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(val & 0xffff, ®s->idr0);
hal2_write(val >> 16, ®s->idr1);
hal2_write(0, ®s->idr2);
hal2_write(0, ®s->idr3);
hal2_write(H2_WRITE_ADDR(addr), ®s->iar);
H2_INDIRECT_WAIT(regs);
}
static void hal2_i_setbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(H2_READ_ADDR(addr), ®s->iar);
H2_INDIRECT_WAIT(regs);
hal2_write((hal2_read(®s->idr0) & 0xffff) | bit, ®s->idr0);
hal2_write(0, ®s->idr1);
hal2_write(0, ®s->idr2);
hal2_write(0, ®s->idr3);
hal2_write(H2_WRITE_ADDR(addr), ®s->iar);
H2_INDIRECT_WAIT(regs);
}
static void hal2_i_clearbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
{
struct hal2_ctl_regs *regs = hal2->ctl_regs;
hal2_write(H2_READ_ADDR(addr), ®s->