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(®s->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), ®s->iar); 1098c2ecf20Sopenharmony_ci H2_INDIRECT_WAIT(regs); 1108c2ecf20Sopenharmony_ci ret = hal2_read(®s->idr0) & 0xffff; 1118c2ecf20Sopenharmony_ci hal2_write(H2_READ_ADDR(addr) | 0x1, ®s->iar); 1128c2ecf20Sopenharmony_ci H2_INDIRECT_WAIT(regs); 1138c2ecf20Sopenharmony_ci ret |= (hal2_read(®s->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, ®s->idr0); 1228c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr1); 1238c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr2); 1248c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr3); 1258c2ecf20Sopenharmony_ci hal2_write(H2_WRITE_ADDR(addr), ®s->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, ®s->idr0); 1348c2ecf20Sopenharmony_ci hal2_write(val >> 16, ®s->idr1); 1358c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr2); 1368c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr3); 1378c2ecf20Sopenharmony_ci hal2_write(H2_WRITE_ADDR(addr), ®s->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), ®s->iar); 1468c2ecf20Sopenharmony_ci H2_INDIRECT_WAIT(regs); 1478c2ecf20Sopenharmony_ci hal2_write((hal2_read(®s->idr0) & 0xffff) | bit, ®s->idr0); 1488c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr1); 1498c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr2); 1508c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr3); 1518c2ecf20Sopenharmony_ci hal2_write(H2_WRITE_ADDR(addr), ®s->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), ®s->iar); 1608c2ecf20Sopenharmony_ci H2_INDIRECT_WAIT(regs); 1618c2ecf20Sopenharmony_ci hal2_write((hal2_read(®s->idr0) & 0xffff) & ~bit, ®s->idr0); 1628c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr1); 1638c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr2); 1648c2ecf20Sopenharmony_ci hal2_write(0, ®s->idr3); 1658c2ecf20Sopenharmony_ci hal2_write(H2_WRITE_ADDR(addr), ®s->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