18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Driver for A2 audio system used in SGI machines
48c2ecf20Sopenharmony_ci *  Copyright (c) 2008 Thomas Bogendoerfer <tsbogend@alpha.fanken.de>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  Based on OSS code from Ladislav Michl <ladis@linux-mips.org>, which
78c2ecf20Sopenharmony_ci *  was based on code from Ulf Carlsson
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <asm/sgi/hpc3.h>
198c2ecf20Sopenharmony_ci#include <asm/sgi/ip22.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <sound/core.h>
228c2ecf20Sopenharmony_ci#include <sound/control.h>
238c2ecf20Sopenharmony_ci#include <sound/pcm.h>
248c2ecf20Sopenharmony_ci#include <sound/pcm-indirect.h>
258c2ecf20Sopenharmony_ci#include <sound/initval.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "hal2.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1;  /* Index 0-MAX */
308c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1;   /* ID for this card */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cimodule_param(index, int, 0444);
338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for SGI HAL2 soundcard.");
348c2ecf20Sopenharmony_cimodule_param(id, charp, 0444);
358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for SGI HAL2 soundcard.");
368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA driver for SGI HAL2 audio");
378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thomas Bogendoerfer");
388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define H2_BLOCK_SIZE	1024
428c2ecf20Sopenharmony_ci#define H2_BUF_SIZE	16384
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct hal2_pbus {
458c2ecf20Sopenharmony_ci	struct hpc3_pbus_dmacregs *pbus;
468c2ecf20Sopenharmony_ci	int pbusnr;
478c2ecf20Sopenharmony_ci	unsigned int ctrl;		/* Current state of pbus->pbdma_ctrl */
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct hal2_desc {
518c2ecf20Sopenharmony_ci	struct hpc_dma_desc desc;
528c2ecf20Sopenharmony_ci	u32 pad;			/* padding */
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct hal2_codec {
568c2ecf20Sopenharmony_ci	struct snd_pcm_indirect pcm_indirect;
578c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	unsigned char *buffer;
608c2ecf20Sopenharmony_ci	dma_addr_t buffer_dma;
618c2ecf20Sopenharmony_ci	struct hal2_desc *desc;
628c2ecf20Sopenharmony_ci	dma_addr_t desc_dma;
638c2ecf20Sopenharmony_ci	int desc_count;
648c2ecf20Sopenharmony_ci	struct hal2_pbus pbus;
658c2ecf20Sopenharmony_ci	int voices;			/* mono/stereo */
668c2ecf20Sopenharmony_ci	unsigned int sample_rate;
678c2ecf20Sopenharmony_ci	unsigned int master;		/* Master frequency */
688c2ecf20Sopenharmony_ci	unsigned short mod;		/* MOD value */
698c2ecf20Sopenharmony_ci	unsigned short inc;		/* INC value */
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define H2_MIX_OUTPUT_ATT	0
738c2ecf20Sopenharmony_ci#define H2_MIX_INPUT_GAIN	1
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistruct snd_hal2 {
768c2ecf20Sopenharmony_ci	struct snd_card *card;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	struct hal2_ctl_regs *ctl_regs;	/* HAL2 ctl registers */
798c2ecf20Sopenharmony_ci	struct hal2_aes_regs *aes_regs;	/* HAL2 aes registers */
808c2ecf20Sopenharmony_ci	struct hal2_vol_regs *vol_regs;	/* HAL2 vol registers */
818c2ecf20Sopenharmony_ci	struct hal2_syn_regs *syn_regs;	/* HAL2 syn registers */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	struct hal2_codec dac;
848c2ecf20Sopenharmony_ci	struct hal2_codec adc;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define H2_INDIRECT_WAIT(regs)	while (hal2_read(&regs->isr) & H2_ISR_TSTATUS);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#define H2_READ_ADDR(addr)	(addr | (1<<7))
908c2ecf20Sopenharmony_ci#define H2_WRITE_ADDR(addr)	(addr)
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic inline u32 hal2_read(u32 *reg)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	return __raw_readl(reg);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic inline void hal2_write(u32 val, u32 *reg)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	__raw_writel(val, reg);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic u32 hal2_i_read32(struct snd_hal2 *hal2, u16 addr)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	u32 ret;
1068c2ecf20Sopenharmony_ci	struct hal2_ctl_regs *regs = hal2->ctl_regs;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	hal2_write(H2_READ_ADDR(addr), &regs->iar);
1098c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1108c2ecf20Sopenharmony_ci	ret = hal2_read(&regs->idr0) & 0xffff;
1118c2ecf20Sopenharmony_ci	hal2_write(H2_READ_ADDR(addr) | 0x1, &regs->iar);
1128c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1138c2ecf20Sopenharmony_ci	ret |= (hal2_read(&regs->idr0) & 0xffff) << 16;
1148c2ecf20Sopenharmony_ci	return ret;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic void hal2_i_write16(struct snd_hal2 *hal2, u16 addr, u16 val)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct hal2_ctl_regs *regs = hal2->ctl_regs;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	hal2_write(val, &regs->idr0);
1228c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr1);
1238c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr2);
1248c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr3);
1258c2ecf20Sopenharmony_ci	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
1268c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void hal2_i_write32(struct snd_hal2 *hal2, u16 addr, u32 val)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct hal2_ctl_regs *regs = hal2->ctl_regs;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	hal2_write(val & 0xffff, &regs->idr0);
1348c2ecf20Sopenharmony_ci	hal2_write(val >> 16, &regs->idr1);
1358c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr2);
1368c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr3);
1378c2ecf20Sopenharmony_ci	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
1388c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic void hal2_i_setbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct hal2_ctl_regs *regs = hal2->ctl_regs;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	hal2_write(H2_READ_ADDR(addr), &regs->iar);
1468c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1478c2ecf20Sopenharmony_ci	hal2_write((hal2_read(&regs->idr0) & 0xffff) | bit, &regs->idr0);
1488c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr1);
1498c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr2);
1508c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr3);
1518c2ecf20Sopenharmony_ci	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
1528c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void hal2_i_clearbit16(struct snd_hal2 *hal2, u16 addr, u16 bit)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct hal2_ctl_regs *regs = hal2->ctl_regs;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	hal2_write(H2_READ_ADDR(addr), &regs->iar);
1608c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1618c2ecf20Sopenharmony_ci	hal2_write((hal2_read(&regs->idr0) & 0xffff) & ~bit, &regs->idr0);
1628c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr1);
1638c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr2);
1648c2ecf20Sopenharmony_ci	hal2_write(0, &regs->idr3);
1658c2ecf20Sopenharmony_ci	hal2_write(H2_WRITE_ADDR(addr), &regs->iar);
1668c2ecf20Sopenharmony_ci	H2_INDIRECT_WAIT(regs);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic int hal2_gain_info(struct snd_kcontrol *kcontrol,
1708c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
1738c2ecf20Sopenharmony_ci	uinfo->count = 2;
1748c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
1758c2ecf20Sopenharmony_ci	switch ((int)kcontrol->private_value) {
1768c2ecf20Sopenharmony_ci	case H2_MIX_OUTPUT_ATT:
1778c2ecf20Sopenharmony_ci		uinfo->value.integer.max = 31;
1788c2ecf20Sopenharmony_ci		break;
1798c2ecf20Sopenharmony_ci	case H2_MIX_INPUT_GAIN:
1808c2ecf20Sopenharmony_ci		uinfo->value.integer.max = 15;
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int hal2_gain_get(struct snd_kcontrol *kcontrol,
1878c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
1908c2ecf20Sopenharmony_ci	u32 tmp;
1918c2ecf20Sopenharmony_ci	int l, r;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	switch ((int)kcontrol->private_value) {
1948c2ecf20Sopenharmony_ci	case H2_MIX_OUTPUT_ATT:
1958c2ecf20Sopenharmony_ci		tmp = hal2_i_read32(hal2, H2I_DAC_C2);
1968c2ecf20Sopenharmony_ci		if (tmp & H2I_C2_MUTE) {
1978c2ecf20Sopenharmony_ci			l = 0;
1988c2ecf20Sopenharmony_ci			r = 0;
1998c2ecf20Sopenharmony_ci		} else {
2008c2ecf20Sopenharmony_ci			l = 31 - ((tmp >> H2I_C2_L_ATT_SHIFT) & 31);
2018c2ecf20Sopenharmony_ci			r = 31 - ((tmp >> H2I_C2_R_ATT_SHIFT) & 31);
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci		break;
2048c2ecf20Sopenharmony_ci	case H2_MIX_INPUT_GAIN:
2058c2ecf20Sopenharmony_ci		tmp = hal2_i_read32(hal2, H2I_ADC_C2);
2068c2ecf20Sopenharmony_ci		l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15;
2078c2ecf20Sopenharmony_ci		r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15;
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci	default:
2108c2ecf20Sopenharmony_ci		return -EINVAL;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = l;
2138c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] = r;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic int hal2_gain_put(struct snd_kcontrol *kcontrol,
2198c2ecf20Sopenharmony_ci			 struct snd_ctl_elem_value *ucontrol)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_kcontrol_chip(kcontrol);
2228c2ecf20Sopenharmony_ci	u32 old, new;
2238c2ecf20Sopenharmony_ci	int l, r;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	l = ucontrol->value.integer.value[0];
2268c2ecf20Sopenharmony_ci	r = ucontrol->value.integer.value[1];
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	switch ((int)kcontrol->private_value) {
2298c2ecf20Sopenharmony_ci	case H2_MIX_OUTPUT_ATT:
2308c2ecf20Sopenharmony_ci		old = hal2_i_read32(hal2, H2I_DAC_C2);
2318c2ecf20Sopenharmony_ci		new = old & ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
2328c2ecf20Sopenharmony_ci		if (l | r) {
2338c2ecf20Sopenharmony_ci			l = 31 - l;
2348c2ecf20Sopenharmony_ci			r = 31 - r;
2358c2ecf20Sopenharmony_ci			new |= (l << H2I_C2_L_ATT_SHIFT);
2368c2ecf20Sopenharmony_ci			new |= (r << H2I_C2_R_ATT_SHIFT);
2378c2ecf20Sopenharmony_ci		} else
2388c2ecf20Sopenharmony_ci			new |= H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE;
2398c2ecf20Sopenharmony_ci		hal2_i_write32(hal2, H2I_DAC_C2, new);
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case H2_MIX_INPUT_GAIN:
2428c2ecf20Sopenharmony_ci		old = hal2_i_read32(hal2, H2I_ADC_C2);
2438c2ecf20Sopenharmony_ci		new = old & ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M);
2448c2ecf20Sopenharmony_ci		new |= (l << H2I_C2_L_GAIN_SHIFT);
2458c2ecf20Sopenharmony_ci		new |= (r << H2I_C2_R_GAIN_SHIFT);
2468c2ecf20Sopenharmony_ci		hal2_i_write32(hal2, H2I_ADC_C2, new);
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci	default:
2498c2ecf20Sopenharmony_ci		return -EINVAL;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci	return old != new;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new hal2_ctrl_headphone = {
2558c2ecf20Sopenharmony_ci	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
2568c2ecf20Sopenharmony_ci	.name           = "Headphone Playback Volume",
2578c2ecf20Sopenharmony_ci	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
2588c2ecf20Sopenharmony_ci	.private_value  = H2_MIX_OUTPUT_ATT,
2598c2ecf20Sopenharmony_ci	.info           = hal2_gain_info,
2608c2ecf20Sopenharmony_ci	.get            = hal2_gain_get,
2618c2ecf20Sopenharmony_ci	.put            = hal2_gain_put,
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new hal2_ctrl_mic = {
2658c2ecf20Sopenharmony_ci	.iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
2668c2ecf20Sopenharmony_ci	.name           = "Mic Capture Volume",
2678c2ecf20Sopenharmony_ci	.access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
2688c2ecf20Sopenharmony_ci	.private_value  = H2_MIX_INPUT_GAIN,
2698c2ecf20Sopenharmony_ci	.info           = hal2_gain_info,
2708c2ecf20Sopenharmony_ci	.get            = hal2_gain_get,
2718c2ecf20Sopenharmony_ci	.put            = hal2_gain_put,
2728c2ecf20Sopenharmony_ci};
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic int hal2_mixer_create(struct snd_hal2 *hal2)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	int err;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* mute DAC */
2798c2ecf20Sopenharmony_ci	hal2_i_write32(hal2, H2I_DAC_C2,
2808c2ecf20Sopenharmony_ci		       H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE);
2818c2ecf20Sopenharmony_ci	/* mute ADC */
2828c2ecf20Sopenharmony_ci	hal2_i_write32(hal2, H2I_ADC_C2, 0);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	err = snd_ctl_add(hal2->card,
2858c2ecf20Sopenharmony_ci			  snd_ctl_new1(&hal2_ctrl_headphone, hal2));
2868c2ecf20Sopenharmony_ci	if (err < 0)
2878c2ecf20Sopenharmony_ci		return err;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	err = snd_ctl_add(hal2->card,
2908c2ecf20Sopenharmony_ci			  snd_ctl_new1(&hal2_ctrl_mic, hal2));
2918c2ecf20Sopenharmony_ci	if (err < 0)
2928c2ecf20Sopenharmony_ci		return err;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return 0;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic irqreturn_t hal2_interrupt(int irq, void *dev_id)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = dev_id;
3008c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* decide what caused this interrupt */
3038c2ecf20Sopenharmony_ci	if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
3048c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(hal2->dac.substream);
3058c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci	if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) {
3088c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(hal2->adc.substream);
3098c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	return ret;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	unsigned short mod;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (44100 % rate < 48000 % rate) {
3198c2ecf20Sopenharmony_ci		mod = 4 * 44100 / rate;
3208c2ecf20Sopenharmony_ci		codec->master = 44100;
3218c2ecf20Sopenharmony_ci	} else {
3228c2ecf20Sopenharmony_ci		mod = 4 * 48000 / rate;
3238c2ecf20Sopenharmony_ci		codec->master = 48000;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	codec->inc = 4;
3278c2ecf20Sopenharmony_ci	codec->mod = mod;
3288c2ecf20Sopenharmony_ci	rate = 4 * codec->master / mod;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return rate;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic void hal2_set_dac_rate(struct snd_hal2 *hal2)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	unsigned int master = hal2->dac.master;
3368c2ecf20Sopenharmony_ci	int inc = hal2->dac.inc;
3378c2ecf20Sopenharmony_ci	int mod = hal2->dac.mod;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0);
3408c2ecf20Sopenharmony_ci	hal2_i_write32(hal2, H2I_BRES1_C2,
3418c2ecf20Sopenharmony_ci		       ((0xffff & (inc - mod - 1)) << 16) | inc);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic void hal2_set_adc_rate(struct snd_hal2 *hal2)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	unsigned int master = hal2->adc.master;
3478c2ecf20Sopenharmony_ci	int inc = hal2->adc.inc;
3488c2ecf20Sopenharmony_ci	int mod = hal2->adc.mod;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0);
3518c2ecf20Sopenharmony_ci	hal2_i_write32(hal2, H2I_BRES2_C2,
3528c2ecf20Sopenharmony_ci		       ((0xffff & (inc - mod - 1)) << 16) | inc);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic void hal2_setup_dac(struct snd_hal2 *hal2)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	unsigned int fifobeg, fifoend, highwater, sample_size;
3588c2ecf20Sopenharmony_ci	struct hal2_pbus *pbus = &hal2->dac.pbus;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* Now we set up some PBUS information. The PBUS needs information about
3618c2ecf20Sopenharmony_ci	 * what portion of the fifo it will use. If it's receiving or
3628c2ecf20Sopenharmony_ci	 * transmitting, and finally whether the stream is little endian or big
3638c2ecf20Sopenharmony_ci	 * endian. The information is written later, on the start call.
3648c2ecf20Sopenharmony_ci	 */
3658c2ecf20Sopenharmony_ci	sample_size = 2 * hal2->dac.voices;
3668c2ecf20Sopenharmony_ci	/* Fifo should be set to hold exactly four samples. Highwater mark
3678c2ecf20Sopenharmony_ci	 * should be set to two samples. */
3688c2ecf20Sopenharmony_ci	highwater = (sample_size * 2) >> 1;	/* halfwords */
3698c2ecf20Sopenharmony_ci	fifobeg = 0;				/* playback is first */
3708c2ecf20Sopenharmony_ci	fifoend = (sample_size * 4) >> 3;	/* doublewords */
3718c2ecf20Sopenharmony_ci	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD |
3728c2ecf20Sopenharmony_ci		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24);
3738c2ecf20Sopenharmony_ci	/* We disable everything before we do anything at all */
3748c2ecf20Sopenharmony_ci	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
3758c2ecf20Sopenharmony_ci	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
3768c2ecf20Sopenharmony_ci	/* Setup the HAL2 for playback */
3778c2ecf20Sopenharmony_ci	hal2_set_dac_rate(hal2);
3788c2ecf20Sopenharmony_ci	/* Set endianess */
3798c2ecf20Sopenharmony_ci	hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX);
3808c2ecf20Sopenharmony_ci	/* Set DMA bus */
3818c2ecf20Sopenharmony_ci	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
3828c2ecf20Sopenharmony_ci	/* We are using 1st Bresenham clock generator for playback */
3838c2ecf20Sopenharmony_ci	hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
3848c2ecf20Sopenharmony_ci			| (1 << H2I_C1_CLKID_SHIFT)
3858c2ecf20Sopenharmony_ci			| (hal2->dac.voices << H2I_C1_DATAT_SHIFT));
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void hal2_setup_adc(struct snd_hal2 *hal2)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	unsigned int fifobeg, fifoend, highwater, sample_size;
3918c2ecf20Sopenharmony_ci	struct hal2_pbus *pbus = &hal2->adc.pbus;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	sample_size = 2 * hal2->adc.voices;
3948c2ecf20Sopenharmony_ci	highwater = (sample_size * 2) >> 1;		/* halfwords */
3958c2ecf20Sopenharmony_ci	fifobeg = (4 * 4) >> 3;				/* record is second */
3968c2ecf20Sopenharmony_ci	fifoend = (4 * 4 + sample_size * 4) >> 3;	/* doublewords */
3978c2ecf20Sopenharmony_ci	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD |
3988c2ecf20Sopenharmony_ci		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24);
3998c2ecf20Sopenharmony_ci	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
4008c2ecf20Sopenharmony_ci	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
4018c2ecf20Sopenharmony_ci	/* Setup the HAL2 for record */
4028c2ecf20Sopenharmony_ci	hal2_set_adc_rate(hal2);
4038c2ecf20Sopenharmony_ci	/* Set endianess */
4048c2ecf20Sopenharmony_ci	hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR);
4058c2ecf20Sopenharmony_ci	/* Set DMA bus */
4068c2ecf20Sopenharmony_ci	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
4078c2ecf20Sopenharmony_ci	/* We are using 2nd Bresenham clock generator for record */
4088c2ecf20Sopenharmony_ci	hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT)
4098c2ecf20Sopenharmony_ci			| (2 << H2I_C1_CLKID_SHIFT)
4108c2ecf20Sopenharmony_ci			| (hal2->adc.voices << H2I_C1_DATAT_SHIFT));
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void hal2_start_dac(struct snd_hal2 *hal2)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct hal2_pbus *pbus = &hal2->dac.pbus;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	pbus->pbus->pbdma_dptr = hal2->dac.desc_dma;
4188c2ecf20Sopenharmony_ci	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
4198c2ecf20Sopenharmony_ci	/* enable DAC */
4208c2ecf20Sopenharmony_ci	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic void hal2_start_adc(struct snd_hal2 *hal2)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct hal2_pbus *pbus = &hal2->adc.pbus;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	pbus->pbus->pbdma_dptr = hal2->adc.desc_dma;
4288c2ecf20Sopenharmony_ci	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT;
4298c2ecf20Sopenharmony_ci	/* enable ADC */
4308c2ecf20Sopenharmony_ci	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic inline void hal2_stop_dac(struct snd_hal2 *hal2)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
4368c2ecf20Sopenharmony_ci	/* The HAL2 itself may remain enabled safely */
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic inline void hal2_stop_adc(struct snd_hal2 *hal2)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_cistatic int hal2_alloc_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec,
4458c2ecf20Sopenharmony_ci		enum dma_data_direction buffer_dir)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct device *dev = hal2->card->dev;
4488c2ecf20Sopenharmony_ci	struct hal2_desc *desc;
4498c2ecf20Sopenharmony_ci	dma_addr_t desc_dma, buffer_dma;
4508c2ecf20Sopenharmony_ci	int count = H2_BUF_SIZE / H2_BLOCK_SIZE;
4518c2ecf20Sopenharmony_ci	int i;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	codec->buffer = dma_alloc_noncoherent(dev, H2_BUF_SIZE, &buffer_dma,
4548c2ecf20Sopenharmony_ci					buffer_dir, GFP_KERNEL);
4558c2ecf20Sopenharmony_ci	if (!codec->buffer)
4568c2ecf20Sopenharmony_ci		return -ENOMEM;
4578c2ecf20Sopenharmony_ci	desc = dma_alloc_noncoherent(dev, count * sizeof(struct hal2_desc),
4588c2ecf20Sopenharmony_ci			&desc_dma, DMA_BIDIRECTIONAL, GFP_KERNEL);
4598c2ecf20Sopenharmony_ci	if (!desc) {
4608c2ecf20Sopenharmony_ci		dma_free_noncoherent(dev, H2_BUF_SIZE, codec->buffer, buffer_dma,
4618c2ecf20Sopenharmony_ci				buffer_dir);
4628c2ecf20Sopenharmony_ci		return -ENOMEM;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci	codec->buffer_dma = buffer_dma;
4658c2ecf20Sopenharmony_ci	codec->desc_dma = desc_dma;
4668c2ecf20Sopenharmony_ci	codec->desc = desc;
4678c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
4688c2ecf20Sopenharmony_ci		desc->desc.pbuf = buffer_dma + i * H2_BLOCK_SIZE;
4698c2ecf20Sopenharmony_ci		desc->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE;
4708c2ecf20Sopenharmony_ci		desc->desc.pnext = (i == count - 1) ?
4718c2ecf20Sopenharmony_ci		      desc_dma : desc_dma + (i + 1) * sizeof(struct hal2_desc);
4728c2ecf20Sopenharmony_ci		desc++;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	dma_sync_single_for_device(dev, codec->desc_dma,
4758c2ecf20Sopenharmony_ci				   count * sizeof(struct hal2_desc),
4768c2ecf20Sopenharmony_ci				   DMA_BIDIRECTIONAL);
4778c2ecf20Sopenharmony_ci	codec->desc_count = count;
4788c2ecf20Sopenharmony_ci	return 0;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void hal2_free_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec,
4828c2ecf20Sopenharmony_ci		enum dma_data_direction buffer_dir)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct device *dev = hal2->card->dev;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	dma_free_noncoherent(dev, codec->desc_count * sizeof(struct hal2_desc),
4878c2ecf20Sopenharmony_ci		       codec->desc, codec->desc_dma, DMA_BIDIRECTIONAL);
4888c2ecf20Sopenharmony_ci	dma_free_noncoherent(dev, H2_BUF_SIZE, codec->buffer, codec->buffer_dma,
4898c2ecf20Sopenharmony_ci			buffer_dir);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware hal2_pcm_hw = {
4938c2ecf20Sopenharmony_ci	.info = (SNDRV_PCM_INFO_MMAP |
4948c2ecf20Sopenharmony_ci		 SNDRV_PCM_INFO_MMAP_VALID |
4958c2ecf20Sopenharmony_ci		 SNDRV_PCM_INFO_INTERLEAVED |
4968c2ecf20Sopenharmony_ci		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
4978c2ecf20Sopenharmony_ci		 SNDRV_PCM_INFO_SYNC_APPLPTR),
4988c2ecf20Sopenharmony_ci	.formats =          SNDRV_PCM_FMTBIT_S16_BE,
4998c2ecf20Sopenharmony_ci	.rates =            SNDRV_PCM_RATE_8000_48000,
5008c2ecf20Sopenharmony_ci	.rate_min =         8000,
5018c2ecf20Sopenharmony_ci	.rate_max =         48000,
5028c2ecf20Sopenharmony_ci	.channels_min =     2,
5038c2ecf20Sopenharmony_ci	.channels_max =     2,
5048c2ecf20Sopenharmony_ci	.buffer_bytes_max = 65536,
5058c2ecf20Sopenharmony_ci	.period_bytes_min = 1024,
5068c2ecf20Sopenharmony_ci	.period_bytes_max = 65536,
5078c2ecf20Sopenharmony_ci	.periods_min =      2,
5088c2ecf20Sopenharmony_ci	.periods_max =      1024,
5098c2ecf20Sopenharmony_ci};
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int hal2_playback_open(struct snd_pcm_substream *substream)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5148c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	runtime->hw = hal2_pcm_hw;
5178c2ecf20Sopenharmony_ci	return hal2_alloc_dmabuf(hal2, &hal2->dac, DMA_TO_DEVICE);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic int hal2_playback_close(struct snd_pcm_substream *substream)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	hal2_free_dmabuf(hal2, &hal2->dac, DMA_TO_DEVICE);
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic int hal2_playback_prepare(struct snd_pcm_substream *substream)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
5318c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5328c2ecf20Sopenharmony_ci	struct hal2_codec *dac = &hal2->dac;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	dac->voices = runtime->channels;
5358c2ecf20Sopenharmony_ci	dac->sample_rate = hal2_compute_rate(dac, runtime->rate);
5368c2ecf20Sopenharmony_ci	memset(&dac->pcm_indirect, 0, sizeof(dac->pcm_indirect));
5378c2ecf20Sopenharmony_ci	dac->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
5388c2ecf20Sopenharmony_ci	dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
5398c2ecf20Sopenharmony_ci	dac->pcm_indirect.hw_io = dac->buffer_dma;
5408c2ecf20Sopenharmony_ci	dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
5418c2ecf20Sopenharmony_ci	dac->substream = substream;
5428c2ecf20Sopenharmony_ci	hal2_setup_dac(hal2);
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	switch (cmd) {
5518c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
5528c2ecf20Sopenharmony_ci		hal2_start_dac(hal2);
5538c2ecf20Sopenharmony_ci		break;
5548c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
5558c2ecf20Sopenharmony_ci		hal2_stop_dac(hal2);
5568c2ecf20Sopenharmony_ci		break;
5578c2ecf20Sopenharmony_ci	default:
5588c2ecf20Sopenharmony_ci		return -EINVAL;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci	return 0;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
5648c2ecf20Sopenharmony_cihal2_playback_pointer(struct snd_pcm_substream *substream)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
5678c2ecf20Sopenharmony_ci	struct hal2_codec *dac = &hal2->dac;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	return snd_pcm_indirect_playback_pointer(substream, &dac->pcm_indirect,
5708c2ecf20Sopenharmony_ci						 dac->pbus.pbus->pbdma_bptr);
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic void hal2_playback_transfer(struct snd_pcm_substream *substream,
5748c2ecf20Sopenharmony_ci				   struct snd_pcm_indirect *rec, size_t bytes)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
5778c2ecf20Sopenharmony_ci	unsigned char *buf = hal2->dac.buffer + rec->hw_data;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	memcpy(buf, substream->runtime->dma_area + rec->sw_data, bytes);
5808c2ecf20Sopenharmony_ci	dma_sync_single_for_device(hal2->card->dev,
5818c2ecf20Sopenharmony_ci			hal2->dac.buffer_dma + rec->hw_data, bytes,
5828c2ecf20Sopenharmony_ci			DMA_TO_DEVICE);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic int hal2_playback_ack(struct snd_pcm_substream *substream)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
5898c2ecf20Sopenharmony_ci	struct hal2_codec *dac = &hal2->dac;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return snd_pcm_indirect_playback_transfer(substream,
5928c2ecf20Sopenharmony_ci						  &dac->pcm_indirect,
5938c2ecf20Sopenharmony_ci						  hal2_playback_transfer);
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int hal2_capture_open(struct snd_pcm_substream *substream)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
5998c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	runtime->hw = hal2_pcm_hw;
6028c2ecf20Sopenharmony_ci	return hal2_alloc_dmabuf(hal2, &hal2->adc, DMA_FROM_DEVICE);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int hal2_capture_close(struct snd_pcm_substream *substream)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	hal2_free_dmabuf(hal2, &hal2->adc, DMA_FROM_DEVICE);
6108c2ecf20Sopenharmony_ci	return 0;
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic int hal2_capture_prepare(struct snd_pcm_substream *substream)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
6168c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6178c2ecf20Sopenharmony_ci	struct hal2_codec *adc = &hal2->adc;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	adc->voices = runtime->channels;
6208c2ecf20Sopenharmony_ci	adc->sample_rate = hal2_compute_rate(adc, runtime->rate);
6218c2ecf20Sopenharmony_ci	memset(&adc->pcm_indirect, 0, sizeof(adc->pcm_indirect));
6228c2ecf20Sopenharmony_ci	adc->pcm_indirect.hw_buffer_size = H2_BUF_SIZE;
6238c2ecf20Sopenharmony_ci	adc->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2;
6248c2ecf20Sopenharmony_ci	adc->pcm_indirect.hw_io = adc->buffer_dma;
6258c2ecf20Sopenharmony_ci	adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
6268c2ecf20Sopenharmony_ci	adc->substream = substream;
6278c2ecf20Sopenharmony_ci	hal2_setup_adc(hal2);
6288c2ecf20Sopenharmony_ci	return 0;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	switch (cmd) {
6368c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
6378c2ecf20Sopenharmony_ci		hal2_start_adc(hal2);
6388c2ecf20Sopenharmony_ci		break;
6398c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
6408c2ecf20Sopenharmony_ci		hal2_stop_adc(hal2);
6418c2ecf20Sopenharmony_ci		break;
6428c2ecf20Sopenharmony_ci	default:
6438c2ecf20Sopenharmony_ci		return -EINVAL;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	return 0;
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
6498c2ecf20Sopenharmony_cihal2_capture_pointer(struct snd_pcm_substream *substream)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
6528c2ecf20Sopenharmony_ci	struct hal2_codec *adc = &hal2->adc;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	return snd_pcm_indirect_capture_pointer(substream, &adc->pcm_indirect,
6558c2ecf20Sopenharmony_ci						adc->pbus.pbus->pbdma_bptr);
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic void hal2_capture_transfer(struct snd_pcm_substream *substream,
6598c2ecf20Sopenharmony_ci				  struct snd_pcm_indirect *rec, size_t bytes)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
6628c2ecf20Sopenharmony_ci	unsigned char *buf = hal2->adc.buffer + rec->hw_data;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	dma_sync_single_for_cpu(hal2->card->dev,
6658c2ecf20Sopenharmony_ci			hal2->adc.buffer_dma + rec->hw_data, bytes,
6668c2ecf20Sopenharmony_ci			DMA_FROM_DEVICE);
6678c2ecf20Sopenharmony_ci	memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes);
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic int hal2_capture_ack(struct snd_pcm_substream *substream)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream);
6738c2ecf20Sopenharmony_ci	struct hal2_codec *adc = &hal2->adc;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	return snd_pcm_indirect_capture_transfer(substream,
6768c2ecf20Sopenharmony_ci						 &adc->pcm_indirect,
6778c2ecf20Sopenharmony_ci						 hal2_capture_transfer);
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops hal2_playback_ops = {
6818c2ecf20Sopenharmony_ci	.open =        hal2_playback_open,
6828c2ecf20Sopenharmony_ci	.close =       hal2_playback_close,
6838c2ecf20Sopenharmony_ci	.prepare =     hal2_playback_prepare,
6848c2ecf20Sopenharmony_ci	.trigger =     hal2_playback_trigger,
6858c2ecf20Sopenharmony_ci	.pointer =     hal2_playback_pointer,
6868c2ecf20Sopenharmony_ci	.ack =         hal2_playback_ack,
6878c2ecf20Sopenharmony_ci};
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops hal2_capture_ops = {
6908c2ecf20Sopenharmony_ci	.open =        hal2_capture_open,
6918c2ecf20Sopenharmony_ci	.close =       hal2_capture_close,
6928c2ecf20Sopenharmony_ci	.prepare =     hal2_capture_prepare,
6938c2ecf20Sopenharmony_ci	.trigger =     hal2_capture_trigger,
6948c2ecf20Sopenharmony_ci	.pointer =     hal2_capture_pointer,
6958c2ecf20Sopenharmony_ci	.ack =         hal2_capture_ack,
6968c2ecf20Sopenharmony_ci};
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_cistatic int hal2_pcm_create(struct snd_hal2 *hal2)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
7018c2ecf20Sopenharmony_ci	int err;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/* create first pcm device with one outputs and one input */
7048c2ecf20Sopenharmony_ci	err = snd_pcm_new(hal2->card, "SGI HAL2 Audio", 0, 1, 1, &pcm);
7058c2ecf20Sopenharmony_ci	if (err < 0)
7068c2ecf20Sopenharmony_ci		return err;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	pcm->private_data = hal2;
7098c2ecf20Sopenharmony_ci	strcpy(pcm->name, "SGI HAL2");
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* set operators */
7128c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
7138c2ecf20Sopenharmony_ci			&hal2_playback_ops);
7148c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
7158c2ecf20Sopenharmony_ci			&hal2_capture_ops);
7168c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
7178c2ecf20Sopenharmony_ci				       NULL, 0, 1024 * 1024);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	return 0;
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic int hal2_dev_free(struct snd_device *device)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2 = device->device_data;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	free_irq(SGI_HPCDMA_IRQ, hal2);
7278c2ecf20Sopenharmony_ci	kfree(hal2);
7288c2ecf20Sopenharmony_ci	return 0;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic const struct snd_device_ops hal2_ops = {
7328c2ecf20Sopenharmony_ci	.dev_free = hal2_dev_free,
7338c2ecf20Sopenharmony_ci};
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_cistatic void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3,
7368c2ecf20Sopenharmony_ci			    int index)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	codec->pbus.pbusnr = index;
7398c2ecf20Sopenharmony_ci	codec->pbus.pbus = &hpc3->pbdma[index];
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int hal2_detect(struct snd_hal2 *hal2)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	unsigned short board, major, minor;
7458c2ecf20Sopenharmony_ci	unsigned short rev;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	/* reset HAL2 */
7488c2ecf20Sopenharmony_ci	hal2_write(0, &hal2->ctl_regs->isr);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	/* release reset */
7518c2ecf20Sopenharmony_ci	hal2_write(H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N,
7528c2ecf20Sopenharmony_ci		   &hal2->ctl_regs->isr);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE);
7568c2ecf20Sopenharmony_ci	rev = hal2_read(&hal2->ctl_regs->rev);
7578c2ecf20Sopenharmony_ci	if (rev & H2_REV_AUDIO_PRESENT)
7588c2ecf20Sopenharmony_ci		return -ENODEV;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	board = (rev & H2_REV_BOARD_M) >> 12;
7618c2ecf20Sopenharmony_ci	major = (rev & H2_REV_MAJOR_CHIP_M) >> 4;
7628c2ecf20Sopenharmony_ci	minor = (rev & H2_REV_MINOR_CHIP_M);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n",
7658c2ecf20Sopenharmony_ci	       board, major, minor);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	return 0;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int hal2_create(struct snd_card *card, struct snd_hal2 **rchip)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct snd_hal2 *hal2;
7738c2ecf20Sopenharmony_ci	struct hpc3_regs *hpc3 = hpc3c0;
7748c2ecf20Sopenharmony_ci	int err;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	hal2 = kzalloc(sizeof(*hal2), GFP_KERNEL);
7778c2ecf20Sopenharmony_ci	if (!hal2)
7788c2ecf20Sopenharmony_ci		return -ENOMEM;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	hal2->card = card;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED,
7838c2ecf20Sopenharmony_ci			"SGI HAL2", hal2)) {
7848c2ecf20Sopenharmony_ci		printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ);
7858c2ecf20Sopenharmony_ci		kfree(hal2);
7868c2ecf20Sopenharmony_ci		return -EAGAIN;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0];
7908c2ecf20Sopenharmony_ci	hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1];
7918c2ecf20Sopenharmony_ci	hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2];
7928c2ecf20Sopenharmony_ci	hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3];
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	if (hal2_detect(hal2) < 0) {
7958c2ecf20Sopenharmony_ci		kfree(hal2);
7968c2ecf20Sopenharmony_ci		return -ENODEV;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	hal2_init_codec(&hal2->dac, hpc3, 0);
8008c2ecf20Sopenharmony_ci	hal2_init_codec(&hal2->adc, hpc3, 1);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	/*
8038c2ecf20Sopenharmony_ci	 * All DMA channel interfaces in HAL2 are designed to operate with
8048c2ecf20Sopenharmony_ci	 * PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles
8058c2ecf20Sopenharmony_ci	 * in D5. HAL2 is a 16-bit device which can accept both big and little
8068c2ecf20Sopenharmony_ci	 * endian format. It assumes that even address bytes are on high
8078c2ecf20Sopenharmony_ci	 * portion of PBUS (15:8) and assumes that HPC3 is programmed to
8088c2ecf20Sopenharmony_ci	 * accept a live (unsynchronized) version of P_DREQ_N from HAL2.
8098c2ecf20Sopenharmony_ci	 */
8108c2ecf20Sopenharmony_ci#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \
8118c2ecf20Sopenharmony_ci			  (2 << HPC3_DMACFG_D4R_SHIFT) | \
8128c2ecf20Sopenharmony_ci			  (2 << HPC3_DMACFG_D5R_SHIFT) | \
8138c2ecf20Sopenharmony_ci			  (0 << HPC3_DMACFG_D3W_SHIFT) | \
8148c2ecf20Sopenharmony_ci			  (2 << HPC3_DMACFG_D4W_SHIFT) | \
8158c2ecf20Sopenharmony_ci			  (2 << HPC3_DMACFG_D5W_SHIFT) | \
8168c2ecf20Sopenharmony_ci				HPC3_DMACFG_DS16 | \
8178c2ecf20Sopenharmony_ci				HPC3_DMACFG_EVENHI | \
8188c2ecf20Sopenharmony_ci				HPC3_DMACFG_RTIME | \
8198c2ecf20Sopenharmony_ci			  (8 << HPC3_DMACFG_BURST_SHIFT) | \
8208c2ecf20Sopenharmony_ci				HPC3_DMACFG_DRQLIVE)
8218c2ecf20Sopenharmony_ci	/*
8228c2ecf20Sopenharmony_ci	 * Ignore what's mentioned in the specification and write value which
8238c2ecf20Sopenharmony_ci	 * works in The Real World (TM)
8248c2ecf20Sopenharmony_ci	 */
8258c2ecf20Sopenharmony_ci	hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844;
8268c2ecf20Sopenharmony_ci	hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, hal2, &hal2_ops);
8298c2ecf20Sopenharmony_ci	if (err < 0) {
8308c2ecf20Sopenharmony_ci		free_irq(SGI_HPCDMA_IRQ, hal2);
8318c2ecf20Sopenharmony_ci		kfree(hal2);
8328c2ecf20Sopenharmony_ci		return err;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci	*rchip = hal2;
8358c2ecf20Sopenharmony_ci	return 0;
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_cistatic int hal2_probe(struct platform_device *pdev)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	struct snd_card *card;
8418c2ecf20Sopenharmony_ci	struct snd_hal2 *chip;
8428c2ecf20Sopenharmony_ci	int err;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	err = snd_card_new(&pdev->dev, index, id, THIS_MODULE, 0, &card);
8458c2ecf20Sopenharmony_ci	if (err < 0)
8468c2ecf20Sopenharmony_ci		return err;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	err = hal2_create(card, &chip);
8498c2ecf20Sopenharmony_ci	if (err < 0) {
8508c2ecf20Sopenharmony_ci		snd_card_free(card);
8518c2ecf20Sopenharmony_ci		return err;
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	err = hal2_pcm_create(chip);
8558c2ecf20Sopenharmony_ci	if (err < 0) {
8568c2ecf20Sopenharmony_ci		snd_card_free(card);
8578c2ecf20Sopenharmony_ci		return err;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci	err = hal2_mixer_create(chip);
8608c2ecf20Sopenharmony_ci	if (err < 0) {
8618c2ecf20Sopenharmony_ci		snd_card_free(card);
8628c2ecf20Sopenharmony_ci		return err;
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	strcpy(card->driver, "SGI HAL2 Audio");
8668c2ecf20Sopenharmony_ci	strcpy(card->shortname, "SGI HAL2 Audio");
8678c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s irq %i",
8688c2ecf20Sopenharmony_ci		card->shortname,
8698c2ecf20Sopenharmony_ci		SGI_HPCDMA_IRQ);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	err = snd_card_register(card);
8728c2ecf20Sopenharmony_ci	if (err < 0) {
8738c2ecf20Sopenharmony_ci		snd_card_free(card);
8748c2ecf20Sopenharmony_ci		return err;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, card);
8778c2ecf20Sopenharmony_ci	return 0;
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistatic int hal2_remove(struct platform_device *pdev)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	struct snd_card *card = platform_get_drvdata(pdev);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	snd_card_free(card);
8858c2ecf20Sopenharmony_ci	return 0;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic struct platform_driver hal2_driver = {
8898c2ecf20Sopenharmony_ci	.probe	= hal2_probe,
8908c2ecf20Sopenharmony_ci	.remove	= hal2_remove,
8918c2ecf20Sopenharmony_ci	.driver = {
8928c2ecf20Sopenharmony_ci		.name	= "sgihal2",
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci};
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cimodule_platform_driver(hal2_driver);
897