162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * bt87x.c - Brooktree Bt878/Bt879 driver for ALSA 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * based on btaudio.c by Gerd Knorr <kraxel@bytesex.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/bitops.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/pcm.h> 1962306a36Sopenharmony_ci#include <sound/pcm_params.h> 2062306a36Sopenharmony_ci#include <sound/control.h> 2162306a36Sopenharmony_ci#include <sound/initval.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 2462306a36Sopenharmony_ciMODULE_DESCRIPTION("Brooktree Bt87x audio driver"); 2562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */ 2862306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 2962306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 3062306a36Sopenharmony_cistatic int digital_rate[SNDRV_CARDS]; /* digital input rate */ 3162306a36Sopenharmony_cistatic bool load_all; /* allow to load cards not the allowlist */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 3462306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Bt87x soundcard"); 3562306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Bt87x soundcard"); 3762306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 3862306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Bt87x soundcard"); 3962306a36Sopenharmony_cimodule_param_array(digital_rate, int, NULL, 0444); 4062306a36Sopenharmony_ciMODULE_PARM_DESC(digital_rate, "Digital input rate for Bt87x soundcard"); 4162306a36Sopenharmony_cimodule_param(load_all, bool, 0444); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(load_all, "Allow to load cards not on the allowlist"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* register offsets */ 4662306a36Sopenharmony_ci#define REG_INT_STAT 0x100 /* interrupt status */ 4762306a36Sopenharmony_ci#define REG_INT_MASK 0x104 /* interrupt mask */ 4862306a36Sopenharmony_ci#define REG_GPIO_DMA_CTL 0x10c /* audio control */ 4962306a36Sopenharmony_ci#define REG_PACKET_LEN 0x110 /* audio packet lengths */ 5062306a36Sopenharmony_ci#define REG_RISC_STRT_ADD 0x114 /* RISC program start address */ 5162306a36Sopenharmony_ci#define REG_RISC_COUNT 0x120 /* RISC program counter */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* interrupt bits */ 5462306a36Sopenharmony_ci#define INT_OFLOW (1 << 3) /* audio A/D overflow */ 5562306a36Sopenharmony_ci#define INT_RISCI (1 << 11) /* RISC instruction IRQ bit set */ 5662306a36Sopenharmony_ci#define INT_FBUS (1 << 12) /* FIFO overrun due to bus access latency */ 5762306a36Sopenharmony_ci#define INT_FTRGT (1 << 13) /* FIFO overrun due to target latency */ 5862306a36Sopenharmony_ci#define INT_FDSR (1 << 14) /* FIFO data stream resynchronization */ 5962306a36Sopenharmony_ci#define INT_PPERR (1 << 15) /* PCI parity error */ 6062306a36Sopenharmony_ci#define INT_RIPERR (1 << 16) /* RISC instruction parity error */ 6162306a36Sopenharmony_ci#define INT_PABORT (1 << 17) /* PCI master or target abort */ 6262306a36Sopenharmony_ci#define INT_OCERR (1 << 18) /* invalid opcode */ 6362306a36Sopenharmony_ci#define INT_SCERR (1 << 19) /* sync counter overflow */ 6462306a36Sopenharmony_ci#define INT_RISC_EN (1 << 27) /* DMA controller running */ 6562306a36Sopenharmony_ci#define INT_RISCS_SHIFT 28 /* RISC status bits */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* audio control bits */ 6862306a36Sopenharmony_ci#define CTL_FIFO_ENABLE (1 << 0) /* enable audio data FIFO */ 6962306a36Sopenharmony_ci#define CTL_RISC_ENABLE (1 << 1) /* enable audio DMA controller */ 7062306a36Sopenharmony_ci#define CTL_PKTP_4 (0 << 2) /* packet mode FIFO trigger point - 4 DWORDs */ 7162306a36Sopenharmony_ci#define CTL_PKTP_8 (1 << 2) /* 8 DWORDs */ 7262306a36Sopenharmony_ci#define CTL_PKTP_16 (2 << 2) /* 16 DWORDs */ 7362306a36Sopenharmony_ci#define CTL_ACAP_EN (1 << 4) /* enable audio capture */ 7462306a36Sopenharmony_ci#define CTL_DA_APP (1 << 5) /* GPIO input */ 7562306a36Sopenharmony_ci#define CTL_DA_IOM_AFE (0 << 6) /* audio A/D input */ 7662306a36Sopenharmony_ci#define CTL_DA_IOM_DA (1 << 6) /* digital audio input */ 7762306a36Sopenharmony_ci#define CTL_DA_SDR_SHIFT 8 /* DDF first stage decimation rate */ 7862306a36Sopenharmony_ci#define CTL_DA_SDR_MASK (0xf<< 8) 7962306a36Sopenharmony_ci#define CTL_DA_LMT (1 << 12) /* limit audio data values */ 8062306a36Sopenharmony_ci#define CTL_DA_ES2 (1 << 13) /* enable DDF stage 2 */ 8162306a36Sopenharmony_ci#define CTL_DA_SBR (1 << 14) /* samples rounded to 8 bits */ 8262306a36Sopenharmony_ci#define CTL_DA_DPM (1 << 15) /* data packet mode */ 8362306a36Sopenharmony_ci#define CTL_DA_LRD_SHIFT 16 /* ALRCK delay */ 8462306a36Sopenharmony_ci#define CTL_DA_MLB (1 << 21) /* MSB/LSB format */ 8562306a36Sopenharmony_ci#define CTL_DA_LRI (1 << 22) /* left/right indication */ 8662306a36Sopenharmony_ci#define CTL_DA_SCE (1 << 23) /* sample clock edge */ 8762306a36Sopenharmony_ci#define CTL_A_SEL_STV (0 << 24) /* TV tuner audio input */ 8862306a36Sopenharmony_ci#define CTL_A_SEL_SFM (1 << 24) /* FM audio input */ 8962306a36Sopenharmony_ci#define CTL_A_SEL_SML (2 << 24) /* mic/line audio input */ 9062306a36Sopenharmony_ci#define CTL_A_SEL_SMXC (3 << 24) /* MUX bypass */ 9162306a36Sopenharmony_ci#define CTL_A_SEL_SHIFT 24 9262306a36Sopenharmony_ci#define CTL_A_SEL_MASK (3 << 24) 9362306a36Sopenharmony_ci#define CTL_A_PWRDN (1 << 26) /* analog audio power-down */ 9462306a36Sopenharmony_ci#define CTL_A_G2X (1 << 27) /* audio gain boost */ 9562306a36Sopenharmony_ci#define CTL_A_GAIN_SHIFT 28 /* audio input gain */ 9662306a36Sopenharmony_ci#define CTL_A_GAIN_MASK (0xf<<28) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* RISC instruction opcodes */ 9962306a36Sopenharmony_ci#define RISC_WRITE (0x1 << 28) /* write FIFO data to memory at address */ 10062306a36Sopenharmony_ci#define RISC_WRITEC (0x5 << 28) /* write FIFO data to memory at current address */ 10162306a36Sopenharmony_ci#define RISC_SKIP (0x2 << 28) /* skip FIFO data */ 10262306a36Sopenharmony_ci#define RISC_JUMP (0x7 << 28) /* jump to address */ 10362306a36Sopenharmony_ci#define RISC_SYNC (0x8 << 28) /* synchronize with FIFO */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* RISC instruction bits */ 10662306a36Sopenharmony_ci#define RISC_BYTES_ENABLE (0xf << 12) /* byte enable bits */ 10762306a36Sopenharmony_ci#define RISC_RESYNC ( 1 << 15) /* disable FDSR errors */ 10862306a36Sopenharmony_ci#define RISC_SET_STATUS_SHIFT 16 /* set status bits */ 10962306a36Sopenharmony_ci#define RISC_RESET_STATUS_SHIFT 20 /* clear status bits */ 11062306a36Sopenharmony_ci#define RISC_IRQ ( 1 << 24) /* interrupt */ 11162306a36Sopenharmony_ci#define RISC_EOL ( 1 << 26) /* end of line */ 11262306a36Sopenharmony_ci#define RISC_SOL ( 1 << 27) /* start of line */ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* SYNC status bits values */ 11562306a36Sopenharmony_ci#define RISC_SYNC_FM1 0x6 11662306a36Sopenharmony_ci#define RISC_SYNC_VRO 0xc 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define ANALOG_CLOCK 1792000 11962306a36Sopenharmony_ci#ifdef CONFIG_SND_BT87X_OVERCLOCK 12062306a36Sopenharmony_ci#define CLOCK_DIV_MIN 1 12162306a36Sopenharmony_ci#else 12262306a36Sopenharmony_ci#define CLOCK_DIV_MIN 4 12362306a36Sopenharmony_ci#endif 12462306a36Sopenharmony_ci#define CLOCK_DIV_MAX 15 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define ERROR_INTERRUPTS (INT_FBUS | INT_FTRGT | INT_PPERR | \ 12762306a36Sopenharmony_ci INT_RIPERR | INT_PABORT | INT_OCERR) 12862306a36Sopenharmony_ci#define MY_INTERRUPTS (INT_RISCI | ERROR_INTERRUPTS) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* SYNC, one WRITE per line, one extra WRITE per page boundary, SYNC, JUMP */ 13162306a36Sopenharmony_ci#define MAX_RISC_SIZE ((1 + 255 + (PAGE_ALIGN(255 * 4092) / PAGE_SIZE - 1) + 1 + 1) * 8) 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* Cards with configuration information */ 13462306a36Sopenharmony_cienum snd_bt87x_boardid { 13562306a36Sopenharmony_ci SND_BT87X_BOARD_UNKNOWN, 13662306a36Sopenharmony_ci SND_BT87X_BOARD_GENERIC, /* both an & dig interfaces, 32kHz */ 13762306a36Sopenharmony_ci SND_BT87X_BOARD_ANALOG, /* board with no external A/D */ 13862306a36Sopenharmony_ci SND_BT87X_BOARD_OSPREY2x0, 13962306a36Sopenharmony_ci SND_BT87X_BOARD_OSPREY440, 14062306a36Sopenharmony_ci SND_BT87X_BOARD_AVPHONE98, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Card configuration */ 14462306a36Sopenharmony_cistruct snd_bt87x_board { 14562306a36Sopenharmony_ci int dig_rate; /* Digital input sampling rate */ 14662306a36Sopenharmony_ci u32 digital_fmt; /* Register settings for digital input */ 14762306a36Sopenharmony_ci unsigned no_analog:1; /* No analog input */ 14862306a36Sopenharmony_ci unsigned no_digital:1; /* No digital input */ 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic const struct snd_bt87x_board snd_bt87x_boards[] = { 15262306a36Sopenharmony_ci [SND_BT87X_BOARD_UNKNOWN] = { 15362306a36Sopenharmony_ci .dig_rate = 32000, /* just a guess */ 15462306a36Sopenharmony_ci }, 15562306a36Sopenharmony_ci [SND_BT87X_BOARD_GENERIC] = { 15662306a36Sopenharmony_ci .dig_rate = 32000, 15762306a36Sopenharmony_ci }, 15862306a36Sopenharmony_ci [SND_BT87X_BOARD_ANALOG] = { 15962306a36Sopenharmony_ci .no_digital = 1, 16062306a36Sopenharmony_ci }, 16162306a36Sopenharmony_ci [SND_BT87X_BOARD_OSPREY2x0] = { 16262306a36Sopenharmony_ci .dig_rate = 44100, 16362306a36Sopenharmony_ci .digital_fmt = CTL_DA_LRI | (1 << CTL_DA_LRD_SHIFT), 16462306a36Sopenharmony_ci }, 16562306a36Sopenharmony_ci [SND_BT87X_BOARD_OSPREY440] = { 16662306a36Sopenharmony_ci .dig_rate = 32000, 16762306a36Sopenharmony_ci .digital_fmt = CTL_DA_LRI | (1 << CTL_DA_LRD_SHIFT), 16862306a36Sopenharmony_ci .no_analog = 1, 16962306a36Sopenharmony_ci }, 17062306a36Sopenharmony_ci [SND_BT87X_BOARD_AVPHONE98] = { 17162306a36Sopenharmony_ci .dig_rate = 48000, 17262306a36Sopenharmony_ci }, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistruct snd_bt87x { 17662306a36Sopenharmony_ci struct snd_card *card; 17762306a36Sopenharmony_ci struct pci_dev *pci; 17862306a36Sopenharmony_ci struct snd_bt87x_board board; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci void __iomem *mmio; 18162306a36Sopenharmony_ci int irq; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci spinlock_t reg_lock; 18462306a36Sopenharmony_ci unsigned long opened; 18562306a36Sopenharmony_ci struct snd_pcm_substream *substream; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci struct snd_dma_buffer dma_risc; 18862306a36Sopenharmony_ci unsigned int line_bytes; 18962306a36Sopenharmony_ci unsigned int lines; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci u32 reg_control; 19262306a36Sopenharmony_ci u32 interrupt_mask; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci int current_line; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci int pci_parity_errors; 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cienum { DEVICE_DIGITAL, DEVICE_ANALOG }; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic inline u32 snd_bt87x_readl(struct snd_bt87x *chip, u32 reg) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci return readl(chip->mmio + reg); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic inline void snd_bt87x_writel(struct snd_bt87x *chip, u32 reg, u32 value) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci writel(value, chip->mmio + reg); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substream *substream, 21262306a36Sopenharmony_ci unsigned int periods, unsigned int period_bytes) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci unsigned int i, offset; 21562306a36Sopenharmony_ci __le32 *risc; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (chip->dma_risc.area == NULL) { 21862306a36Sopenharmony_ci if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev, 21962306a36Sopenharmony_ci PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0) 22062306a36Sopenharmony_ci return -ENOMEM; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci risc = (__le32 *)chip->dma_risc.area; 22362306a36Sopenharmony_ci offset = 0; 22462306a36Sopenharmony_ci *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_FM1); 22562306a36Sopenharmony_ci *risc++ = cpu_to_le32(0); 22662306a36Sopenharmony_ci for (i = 0; i < periods; ++i) { 22762306a36Sopenharmony_ci u32 rest; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci rest = period_bytes; 23062306a36Sopenharmony_ci do { 23162306a36Sopenharmony_ci u32 cmd, len; 23262306a36Sopenharmony_ci unsigned int addr; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci len = PAGE_SIZE - (offset % PAGE_SIZE); 23562306a36Sopenharmony_ci if (len > rest) 23662306a36Sopenharmony_ci len = rest; 23762306a36Sopenharmony_ci cmd = RISC_WRITE | len; 23862306a36Sopenharmony_ci if (rest == period_bytes) { 23962306a36Sopenharmony_ci u32 block = i * 16 / periods; 24062306a36Sopenharmony_ci cmd |= RISC_SOL; 24162306a36Sopenharmony_ci cmd |= block << RISC_SET_STATUS_SHIFT; 24262306a36Sopenharmony_ci cmd |= (~block & 0xf) << RISC_RESET_STATUS_SHIFT; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci if (len == rest) 24562306a36Sopenharmony_ci cmd |= RISC_EOL | RISC_IRQ; 24662306a36Sopenharmony_ci *risc++ = cpu_to_le32(cmd); 24762306a36Sopenharmony_ci addr = snd_pcm_sgbuf_get_addr(substream, offset); 24862306a36Sopenharmony_ci *risc++ = cpu_to_le32(addr); 24962306a36Sopenharmony_ci offset += len; 25062306a36Sopenharmony_ci rest -= len; 25162306a36Sopenharmony_ci } while (rest > 0); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_VRO); 25462306a36Sopenharmony_ci *risc++ = cpu_to_le32(0); 25562306a36Sopenharmony_ci *risc++ = cpu_to_le32(RISC_JUMP); 25662306a36Sopenharmony_ci *risc++ = cpu_to_le32(chip->dma_risc.addr); 25762306a36Sopenharmony_ci chip->line_bytes = period_bytes; 25862306a36Sopenharmony_ci chip->lines = periods; 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void snd_bt87x_free_risc(struct snd_bt87x *chip) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci if (chip->dma_risc.area) { 26562306a36Sopenharmony_ci snd_dma_free_pages(&chip->dma_risc); 26662306a36Sopenharmony_ci chip->dma_risc.area = NULL; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic void snd_bt87x_pci_error(struct snd_bt87x *chip, unsigned int status) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci int pci_status = pci_status_get_and_clear_errors(chip->pci); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (pci_status != PCI_STATUS_DETECTED_PARITY) 27562306a36Sopenharmony_ci dev_err(chip->card->dev, 27662306a36Sopenharmony_ci "Aieee - PCI error! status %#08x, PCI status %#04x\n", 27762306a36Sopenharmony_ci status & ERROR_INTERRUPTS, pci_status); 27862306a36Sopenharmony_ci else { 27962306a36Sopenharmony_ci dev_err(chip->card->dev, 28062306a36Sopenharmony_ci "Aieee - PCI parity error detected!\n"); 28162306a36Sopenharmony_ci /* error 'handling' similar to aic7xxx_pci.c: */ 28262306a36Sopenharmony_ci chip->pci_parity_errors++; 28362306a36Sopenharmony_ci if (chip->pci_parity_errors > 20) { 28462306a36Sopenharmony_ci dev_err(chip->card->dev, 28562306a36Sopenharmony_ci "Too many PCI parity errors observed.\n"); 28662306a36Sopenharmony_ci dev_err(chip->card->dev, 28762306a36Sopenharmony_ci "Some device on this bus is generating bad parity.\n"); 28862306a36Sopenharmony_ci dev_err(chip->card->dev, 28962306a36Sopenharmony_ci "This is an error *observed by*, not *generated by*, this card.\n"); 29062306a36Sopenharmony_ci dev_err(chip->card->dev, 29162306a36Sopenharmony_ci "PCI parity error checking has been disabled.\n"); 29262306a36Sopenharmony_ci chip->interrupt_mask &= ~(INT_PPERR | INT_RIPERR); 29362306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct snd_bt87x *chip = dev_id; 30162306a36Sopenharmony_ci unsigned int status, irq_status; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci status = snd_bt87x_readl(chip, REG_INT_STAT); 30462306a36Sopenharmony_ci irq_status = status & chip->interrupt_mask; 30562306a36Sopenharmony_ci if (!irq_status) 30662306a36Sopenharmony_ci return IRQ_NONE; 30762306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_INT_STAT, irq_status); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (irq_status & ERROR_INTERRUPTS) { 31062306a36Sopenharmony_ci if (irq_status & (INT_FBUS | INT_FTRGT)) 31162306a36Sopenharmony_ci dev_warn(chip->card->dev, 31262306a36Sopenharmony_ci "FIFO overrun, status %#08x\n", status); 31362306a36Sopenharmony_ci if (irq_status & INT_OCERR) 31462306a36Sopenharmony_ci dev_err(chip->card->dev, 31562306a36Sopenharmony_ci "internal RISC error, status %#08x\n", status); 31662306a36Sopenharmony_ci if (irq_status & (INT_PPERR | INT_RIPERR | INT_PABORT)) 31762306a36Sopenharmony_ci snd_bt87x_pci_error(chip, irq_status); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci if ((irq_status & INT_RISCI) && (chip->reg_control & CTL_ACAP_EN)) { 32062306a36Sopenharmony_ci int current_block, irq_block; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* assume that exactly one line has been recorded */ 32362306a36Sopenharmony_ci chip->current_line = (chip->current_line + 1) % chip->lines; 32462306a36Sopenharmony_ci /* but check if some interrupts have been skipped */ 32562306a36Sopenharmony_ci current_block = chip->current_line * 16 / chip->lines; 32662306a36Sopenharmony_ci irq_block = status >> INT_RISCS_SHIFT; 32762306a36Sopenharmony_ci if (current_block != irq_block) 32862306a36Sopenharmony_ci chip->current_line = DIV_ROUND_UP(irq_block * chip->lines, 32962306a36Sopenharmony_ci 16); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->substream); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci return IRQ_HANDLED; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_bt87x_digital_hw = { 33762306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 33862306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 33962306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 34062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 34162306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH, 34262306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 34362306a36Sopenharmony_ci .rates = 0, /* set at runtime */ 34462306a36Sopenharmony_ci .channels_min = 2, 34562306a36Sopenharmony_ci .channels_max = 2, 34662306a36Sopenharmony_ci .buffer_bytes_max = 255 * 4092, 34762306a36Sopenharmony_ci .period_bytes_min = 32, 34862306a36Sopenharmony_ci .period_bytes_max = 4092, 34962306a36Sopenharmony_ci .periods_min = 2, 35062306a36Sopenharmony_ci .periods_max = 255, 35162306a36Sopenharmony_ci}; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_bt87x_analog_hw = { 35462306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 35562306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 35662306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 35762306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 35862306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH, 35962306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, 36062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT, 36162306a36Sopenharmony_ci .rate_min = ANALOG_CLOCK / CLOCK_DIV_MAX, 36262306a36Sopenharmony_ci .rate_max = ANALOG_CLOCK / CLOCK_DIV_MIN, 36362306a36Sopenharmony_ci .channels_min = 1, 36462306a36Sopenharmony_ci .channels_max = 1, 36562306a36Sopenharmony_ci .buffer_bytes_max = 255 * 4092, 36662306a36Sopenharmony_ci .period_bytes_min = 32, 36762306a36Sopenharmony_ci .period_bytes_max = 4092, 36862306a36Sopenharmony_ci .periods_min = 2, 36962306a36Sopenharmony_ci .periods_max = 255, 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int snd_bt87x_set_digital_hw(struct snd_bt87x *chip, struct snd_pcm_runtime *runtime) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci chip->reg_control |= CTL_DA_IOM_DA | CTL_A_PWRDN; 37562306a36Sopenharmony_ci runtime->hw = snd_bt87x_digital_hw; 37662306a36Sopenharmony_ci runtime->hw.rates = snd_pcm_rate_to_rate_bit(chip->board.dig_rate); 37762306a36Sopenharmony_ci runtime->hw.rate_min = chip->board.dig_rate; 37862306a36Sopenharmony_ci runtime->hw.rate_max = chip->board.dig_rate; 37962306a36Sopenharmony_ci return 0; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int snd_bt87x_set_analog_hw(struct snd_bt87x *chip, struct snd_pcm_runtime *runtime) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci static const struct snd_ratnum analog_clock = { 38562306a36Sopenharmony_ci .num = ANALOG_CLOCK, 38662306a36Sopenharmony_ci .den_min = CLOCK_DIV_MIN, 38762306a36Sopenharmony_ci .den_max = CLOCK_DIV_MAX, 38862306a36Sopenharmony_ci .den_step = 1 38962306a36Sopenharmony_ci }; 39062306a36Sopenharmony_ci static const struct snd_pcm_hw_constraint_ratnums constraint_rates = { 39162306a36Sopenharmony_ci .nrats = 1, 39262306a36Sopenharmony_ci .rats = &analog_clock 39362306a36Sopenharmony_ci }; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci chip->reg_control &= ~(CTL_DA_IOM_DA | CTL_A_PWRDN); 39662306a36Sopenharmony_ci runtime->hw = snd_bt87x_analog_hw; 39762306a36Sopenharmony_ci return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 39862306a36Sopenharmony_ci &constraint_rates); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int snd_bt87x_pcm_open(struct snd_pcm_substream *substream) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct snd_bt87x *chip = snd_pcm_substream_chip(substream); 40462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 40562306a36Sopenharmony_ci int err; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (test_and_set_bit(0, &chip->opened)) 40862306a36Sopenharmony_ci return -EBUSY; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (substream->pcm->device == DEVICE_DIGITAL) 41162306a36Sopenharmony_ci err = snd_bt87x_set_digital_hw(chip, runtime); 41262306a36Sopenharmony_ci else 41362306a36Sopenharmony_ci err = snd_bt87x_set_analog_hw(chip, runtime); 41462306a36Sopenharmony_ci if (err < 0) 41562306a36Sopenharmony_ci goto _error; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 41862306a36Sopenharmony_ci if (err < 0) 41962306a36Sopenharmony_ci goto _error; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci chip->substream = substream; 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci_error: 42562306a36Sopenharmony_ci clear_bit(0, &chip->opened); 42662306a36Sopenharmony_ci smp_mb__after_atomic(); 42762306a36Sopenharmony_ci return err; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int snd_bt87x_close(struct snd_pcm_substream *substream) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct snd_bt87x *chip = snd_pcm_substream_chip(substream); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 43562306a36Sopenharmony_ci chip->reg_control |= CTL_A_PWRDN; 43662306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 43762306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci chip->substream = NULL; 44062306a36Sopenharmony_ci clear_bit(0, &chip->opened); 44162306a36Sopenharmony_ci smp_mb__after_atomic(); 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic int snd_bt87x_hw_params(struct snd_pcm_substream *substream, 44662306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct snd_bt87x *chip = snd_pcm_substream_chip(substream); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return snd_bt87x_create_risc(chip, substream, 45162306a36Sopenharmony_ci params_periods(hw_params), 45262306a36Sopenharmony_ci params_period_bytes(hw_params)); 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int snd_bt87x_hw_free(struct snd_pcm_substream *substream) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct snd_bt87x *chip = snd_pcm_substream_chip(substream); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci snd_bt87x_free_risc(chip); 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic int snd_bt87x_prepare(struct snd_pcm_substream *substream) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct snd_bt87x *chip = snd_pcm_substream_chip(substream); 46662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 46762306a36Sopenharmony_ci int decimation; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 47062306a36Sopenharmony_ci chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR); 47162306a36Sopenharmony_ci decimation = (ANALOG_CLOCK + runtime->rate / 4) / runtime->rate; 47262306a36Sopenharmony_ci chip->reg_control |= decimation << CTL_DA_SDR_SHIFT; 47362306a36Sopenharmony_ci if (runtime->format == SNDRV_PCM_FORMAT_S8) 47462306a36Sopenharmony_ci chip->reg_control |= CTL_DA_SBR; 47562306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 47662306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int snd_bt87x_start(struct snd_bt87x *chip) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 48362306a36Sopenharmony_ci chip->current_line = 0; 48462306a36Sopenharmony_ci chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN; 48562306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr); 48662306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_PACKET_LEN, 48762306a36Sopenharmony_ci chip->line_bytes | (chip->lines << 16)); 48862306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_INT_MASK, chip->interrupt_mask); 48962306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 49062306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int snd_bt87x_stop(struct snd_bt87x *chip) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 49762306a36Sopenharmony_ci chip->reg_control &= ~(CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN); 49862306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 49962306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_INT_MASK, 0); 50062306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS); 50162306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int snd_bt87x_trigger(struct snd_pcm_substream *substream, int cmd) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct snd_bt87x *chip = snd_pcm_substream_chip(substream); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci switch (cmd) { 51062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 51162306a36Sopenharmony_ci return snd_bt87x_start(chip); 51262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 51362306a36Sopenharmony_ci return snd_bt87x_stop(chip); 51462306a36Sopenharmony_ci default: 51562306a36Sopenharmony_ci return -EINVAL; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_bt87x_pointer(struct snd_pcm_substream *substream) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct snd_bt87x *chip = snd_pcm_substream_chip(substream); 52262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return (snd_pcm_uframes_t)bytes_to_frames(runtime, chip->current_line * chip->line_bytes); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_bt87x_pcm_ops = { 52862306a36Sopenharmony_ci .open = snd_bt87x_pcm_open, 52962306a36Sopenharmony_ci .close = snd_bt87x_close, 53062306a36Sopenharmony_ci .hw_params = snd_bt87x_hw_params, 53162306a36Sopenharmony_ci .hw_free = snd_bt87x_hw_free, 53262306a36Sopenharmony_ci .prepare = snd_bt87x_prepare, 53362306a36Sopenharmony_ci .trigger = snd_bt87x_trigger, 53462306a36Sopenharmony_ci .pointer = snd_bt87x_pointer, 53562306a36Sopenharmony_ci}; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int snd_bt87x_capture_volume_info(struct snd_kcontrol *kcontrol, 53862306a36Sopenharmony_ci struct snd_ctl_elem_info *info) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 54162306a36Sopenharmony_ci info->count = 1; 54262306a36Sopenharmony_ci info->value.integer.min = 0; 54362306a36Sopenharmony_ci info->value.integer.max = 15; 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int snd_bt87x_capture_volume_get(struct snd_kcontrol *kcontrol, 54862306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci value->value.integer.value[0] = (chip->reg_control & CTL_A_GAIN_MASK) >> CTL_A_GAIN_SHIFT; 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic int snd_bt87x_capture_volume_put(struct snd_kcontrol *kcontrol, 55762306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); 56062306a36Sopenharmony_ci u32 old_control; 56162306a36Sopenharmony_ci int changed; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 56462306a36Sopenharmony_ci old_control = chip->reg_control; 56562306a36Sopenharmony_ci chip->reg_control = (chip->reg_control & ~CTL_A_GAIN_MASK) 56662306a36Sopenharmony_ci | (value->value.integer.value[0] << CTL_A_GAIN_SHIFT); 56762306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 56862306a36Sopenharmony_ci changed = old_control != chip->reg_control; 56962306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 57062306a36Sopenharmony_ci return changed; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_bt87x_capture_volume = { 57462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 57562306a36Sopenharmony_ci .name = "Capture Volume", 57662306a36Sopenharmony_ci .info = snd_bt87x_capture_volume_info, 57762306a36Sopenharmony_ci .get = snd_bt87x_capture_volume_get, 57862306a36Sopenharmony_ci .put = snd_bt87x_capture_volume_put, 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci#define snd_bt87x_capture_boost_info snd_ctl_boolean_mono_info 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic int snd_bt87x_capture_boost_get(struct snd_kcontrol *kcontrol, 58462306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci value->value.integer.value[0] = !! (chip->reg_control & CTL_A_G2X); 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int snd_bt87x_capture_boost_put(struct snd_kcontrol *kcontrol, 59362306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); 59662306a36Sopenharmony_ci u32 old_control; 59762306a36Sopenharmony_ci int changed; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 60062306a36Sopenharmony_ci old_control = chip->reg_control; 60162306a36Sopenharmony_ci chip->reg_control = (chip->reg_control & ~CTL_A_G2X) 60262306a36Sopenharmony_ci | (value->value.integer.value[0] ? CTL_A_G2X : 0); 60362306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 60462306a36Sopenharmony_ci changed = chip->reg_control != old_control; 60562306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 60662306a36Sopenharmony_ci return changed; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_bt87x_capture_boost = { 61062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 61162306a36Sopenharmony_ci .name = "Capture Boost", 61262306a36Sopenharmony_ci .info = snd_bt87x_capture_boost_info, 61362306a36Sopenharmony_ci .get = snd_bt87x_capture_boost_get, 61462306a36Sopenharmony_ci .put = snd_bt87x_capture_boost_put, 61562306a36Sopenharmony_ci}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int snd_bt87x_capture_source_info(struct snd_kcontrol *kcontrol, 61862306a36Sopenharmony_ci struct snd_ctl_elem_info *info) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci static const char *const texts[3] = {"TV Tuner", "FM", "Mic/Line"}; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return snd_ctl_enum_info(info, 1, 3, texts); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int snd_bt87x_capture_source_get(struct snd_kcontrol *kcontrol, 62662306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci value->value.enumerated.item[0] = (chip->reg_control & CTL_A_SEL_MASK) >> CTL_A_SEL_SHIFT; 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int snd_bt87x_capture_source_put(struct snd_kcontrol *kcontrol, 63562306a36Sopenharmony_ci struct snd_ctl_elem_value *value) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct snd_bt87x *chip = snd_kcontrol_chip(kcontrol); 63862306a36Sopenharmony_ci u32 old_control; 63962306a36Sopenharmony_ci int changed; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 64262306a36Sopenharmony_ci old_control = chip->reg_control; 64362306a36Sopenharmony_ci chip->reg_control = (chip->reg_control & ~CTL_A_SEL_MASK) 64462306a36Sopenharmony_ci | (value->value.enumerated.item[0] << CTL_A_SEL_SHIFT); 64562306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 64662306a36Sopenharmony_ci changed = chip->reg_control != old_control; 64762306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 64862306a36Sopenharmony_ci return changed; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_bt87x_capture_source = { 65262306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 65362306a36Sopenharmony_ci .name = "Capture Source", 65462306a36Sopenharmony_ci .info = snd_bt87x_capture_source_info, 65562306a36Sopenharmony_ci .get = snd_bt87x_capture_source_get, 65662306a36Sopenharmony_ci .put = snd_bt87x_capture_source_put, 65762306a36Sopenharmony_ci}; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void snd_bt87x_free(struct snd_card *card) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct snd_bt87x *chip = card->private_data; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci snd_bt87x_stop(chip); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci int err; 66962306a36Sopenharmony_ci struct snd_pcm *pcm; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); 67262306a36Sopenharmony_ci if (err < 0) 67362306a36Sopenharmony_ci return err; 67462306a36Sopenharmony_ci pcm->private_data = chip; 67562306a36Sopenharmony_ci strcpy(pcm->name, name); 67662306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops); 67762306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_SG, 67862306a36Sopenharmony_ci &chip->pci->dev, 67962306a36Sopenharmony_ci 128 * 1024, 68062306a36Sopenharmony_ci ALIGN(255 * 4092, 1024)); 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int snd_bt87x_create(struct snd_card *card, 68562306a36Sopenharmony_ci struct pci_dev *pci) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct snd_bt87x *chip = card->private_data; 68862306a36Sopenharmony_ci int err; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci err = pcim_enable_device(pci); 69162306a36Sopenharmony_ci if (err < 0) 69262306a36Sopenharmony_ci return err; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci chip->card = card; 69562306a36Sopenharmony_ci chip->pci = pci; 69662306a36Sopenharmony_ci chip->irq = -1; 69762306a36Sopenharmony_ci spin_lock_init(&chip->reg_lock); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci err = pcim_iomap_regions(pci, 1 << 0, "Bt87x audio"); 70062306a36Sopenharmony_ci if (err < 0) 70162306a36Sopenharmony_ci return err; 70262306a36Sopenharmony_ci chip->mmio = pcim_iomap_table(pci)[0]; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci chip->reg_control = CTL_A_PWRDN | CTL_DA_ES2 | 70562306a36Sopenharmony_ci CTL_PKTP_16 | (15 << CTL_DA_SDR_SHIFT); 70662306a36Sopenharmony_ci chip->interrupt_mask = MY_INTERRUPTS; 70762306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); 70862306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_INT_MASK, 0); 70962306a36Sopenharmony_ci snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci err = devm_request_irq(&pci->dev, pci->irq, snd_bt87x_interrupt, 71262306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip); 71362306a36Sopenharmony_ci if (err < 0) { 71462306a36Sopenharmony_ci dev_err(card->dev, "cannot grab irq %d\n", pci->irq); 71562306a36Sopenharmony_ci return err; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci chip->irq = pci->irq; 71862306a36Sopenharmony_ci card->sync_irq = chip->irq; 71962306a36Sopenharmony_ci card->private_free = snd_bt87x_free; 72062306a36Sopenharmony_ci pci_set_master(pci); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return 0; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci#define BT_DEVICE(chip, subvend, subdev, id) \ 72662306a36Sopenharmony_ci { .vendor = PCI_VENDOR_ID_BROOKTREE, \ 72762306a36Sopenharmony_ci .device = chip, \ 72862306a36Sopenharmony_ci .subvendor = subvend, .subdevice = subdev, \ 72962306a36Sopenharmony_ci .driver_data = SND_BT87X_BOARD_ ## id } 73062306a36Sopenharmony_ci/* driver_data is the card id for that device */ 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic const struct pci_device_id snd_bt87x_ids[] = { 73362306a36Sopenharmony_ci /* Hauppauge WinTV series */ 73462306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0x13eb, GENERIC), 73562306a36Sopenharmony_ci /* Hauppauge WinTV series */ 73662306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, 0x0070, 0x13eb, GENERIC), 73762306a36Sopenharmony_ci /* Viewcast Osprey 200 */ 73862306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, OSPREY2x0), 73962306a36Sopenharmony_ci /* Viewcast Osprey 440 (rate is configurable via gpio) */ 74062306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff07, OSPREY440), 74162306a36Sopenharmony_ci /* ATI TV-Wonder */ 74262306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1002, 0x0001, GENERIC), 74362306a36Sopenharmony_ci /* Leadtek Winfast tv 2000xp delux */ 74462306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, GENERIC), 74562306a36Sopenharmony_ci /* Pinnacle PCTV */ 74662306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x11bd, 0x0012, GENERIC), 74762306a36Sopenharmony_ci /* Voodoo TV 200 */ 74862306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, GENERIC), 74962306a36Sopenharmony_ci /* Askey Computer Corp. MagicTView'99 */ 75062306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x144f, 0x3000, GENERIC), 75162306a36Sopenharmony_ci /* AVerMedia Studio No. 103, 203, ...? */ 75262306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, AVPHONE98), 75362306a36Sopenharmony_ci /* Prolink PixelView PV-M4900 */ 75462306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1554, 0x4011, GENERIC), 75562306a36Sopenharmony_ci /* Pinnacle Studio PCTV rave */ 75662306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0xbd11, 0x1200, GENERIC), 75762306a36Sopenharmony_ci { } 75862306a36Sopenharmony_ci}; 75962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_bt87x_ids); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci/* cards known not to have audio 76262306a36Sopenharmony_ci * (DVB cards use the audio function to transfer MPEG data) */ 76362306a36Sopenharmony_cistatic struct { 76462306a36Sopenharmony_ci unsigned short subvendor, subdevice; 76562306a36Sopenharmony_ci} denylist[] = { 76662306a36Sopenharmony_ci {0x0071, 0x0101}, /* Nebula Electronics DigiTV */ 76762306a36Sopenharmony_ci {0x11bd, 0x001c}, /* Pinnacle PCTV Sat */ 76862306a36Sopenharmony_ci {0x11bd, 0x0026}, /* Pinnacle PCTV SAT CI */ 76962306a36Sopenharmony_ci {0x1461, 0x0761}, /* AVermedia AverTV DVB-T */ 77062306a36Sopenharmony_ci {0x1461, 0x0771}, /* AVermedia DVB-T 771 */ 77162306a36Sopenharmony_ci {0x1822, 0x0001}, /* Twinhan VisionPlus DVB-T */ 77262306a36Sopenharmony_ci {0x18ac, 0xd500}, /* DVICO FusionHDTV 5 Lite */ 77362306a36Sopenharmony_ci {0x18ac, 0xdb10}, /* DVICO FusionHDTV DVB-T Lite */ 77462306a36Sopenharmony_ci {0x18ac, 0xdb11}, /* Ultraview DVB-T Lite */ 77562306a36Sopenharmony_ci {0x270f, 0xfc00}, /* Chaintech Digitop DST-1000 DVB-S */ 77662306a36Sopenharmony_ci {0x7063, 0x2000}, /* pcHDTV HD-2000 TV */ 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic struct pci_driver driver; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci/* return the id of the card, or a negative value if it's on the denylist */ 78262306a36Sopenharmony_cistatic int snd_bt87x_detect_card(struct pci_dev *pci) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci int i; 78562306a36Sopenharmony_ci const struct pci_device_id *supported; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci supported = pci_match_id(snd_bt87x_ids, pci); 78862306a36Sopenharmony_ci if (supported && supported->driver_data > 0) 78962306a36Sopenharmony_ci return supported->driver_data; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(denylist); ++i) 79262306a36Sopenharmony_ci if (denylist[i].subvendor == pci->subsystem_vendor && 79362306a36Sopenharmony_ci denylist[i].subdevice == pci->subsystem_device) { 79462306a36Sopenharmony_ci dev_dbg(&pci->dev, 79562306a36Sopenharmony_ci "card %#04x-%#04x:%#04x has no audio\n", 79662306a36Sopenharmony_ci pci->device, pci->subsystem_vendor, pci->subsystem_device); 79762306a36Sopenharmony_ci return -EBUSY; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci dev_info(&pci->dev, "unknown card %#04x-%#04x:%#04x\n", 80162306a36Sopenharmony_ci pci->device, pci->subsystem_vendor, pci->subsystem_device); 80262306a36Sopenharmony_ci dev_info(&pci->dev, "please mail id, board name, and, " 80362306a36Sopenharmony_ci "if it works, the correct digital_rate option to " 80462306a36Sopenharmony_ci "<alsa-devel@alsa-project.org>\n"); 80562306a36Sopenharmony_ci return SND_BT87X_BOARD_UNKNOWN; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic int __snd_bt87x_probe(struct pci_dev *pci, 80962306a36Sopenharmony_ci const struct pci_device_id *pci_id) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci static int dev; 81262306a36Sopenharmony_ci struct snd_card *card; 81362306a36Sopenharmony_ci struct snd_bt87x *chip; 81462306a36Sopenharmony_ci int err; 81562306a36Sopenharmony_ci enum snd_bt87x_boardid boardid; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (!pci_id->driver_data) { 81862306a36Sopenharmony_ci err = snd_bt87x_detect_card(pci); 81962306a36Sopenharmony_ci if (err < 0) 82062306a36Sopenharmony_ci return -ENODEV; 82162306a36Sopenharmony_ci boardid = err; 82262306a36Sopenharmony_ci } else 82362306a36Sopenharmony_ci boardid = pci_id->driver_data; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 82662306a36Sopenharmony_ci return -ENODEV; 82762306a36Sopenharmony_ci if (!enable[dev]) { 82862306a36Sopenharmony_ci ++dev; 82962306a36Sopenharmony_ci return -ENOENT; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 83362306a36Sopenharmony_ci sizeof(*chip), &card); 83462306a36Sopenharmony_ci if (err < 0) 83562306a36Sopenharmony_ci return err; 83662306a36Sopenharmony_ci chip = card->private_data; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci err = snd_bt87x_create(card, pci); 83962306a36Sopenharmony_ci if (err < 0) 84062306a36Sopenharmony_ci return err; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci memcpy(&chip->board, &snd_bt87x_boards[boardid], sizeof(chip->board)); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (!chip->board.no_digital) { 84562306a36Sopenharmony_ci if (digital_rate[dev] > 0) 84662306a36Sopenharmony_ci chip->board.dig_rate = digital_rate[dev]; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci chip->reg_control |= chip->board.digital_fmt; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci err = snd_bt87x_pcm(chip, DEVICE_DIGITAL, "Bt87x Digital"); 85162306a36Sopenharmony_ci if (err < 0) 85262306a36Sopenharmony_ci return err; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci if (!chip->board.no_analog) { 85562306a36Sopenharmony_ci err = snd_bt87x_pcm(chip, DEVICE_ANALOG, "Bt87x Analog"); 85662306a36Sopenharmony_ci if (err < 0) 85762306a36Sopenharmony_ci return err; 85862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1( 85962306a36Sopenharmony_ci &snd_bt87x_capture_volume, chip)); 86062306a36Sopenharmony_ci if (err < 0) 86162306a36Sopenharmony_ci return err; 86262306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1( 86362306a36Sopenharmony_ci &snd_bt87x_capture_boost, chip)); 86462306a36Sopenharmony_ci if (err < 0) 86562306a36Sopenharmony_ci return err; 86662306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1( 86762306a36Sopenharmony_ci &snd_bt87x_capture_source, chip)); 86862306a36Sopenharmony_ci if (err < 0) 86962306a36Sopenharmony_ci return err; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci dev_info(card->dev, "bt87x%d: Using board %d, %sanalog, %sdigital " 87262306a36Sopenharmony_ci "(rate %d Hz)\n", dev, boardid, 87362306a36Sopenharmony_ci chip->board.no_analog ? "no " : "", 87462306a36Sopenharmony_ci chip->board.no_digital ? "no " : "", chip->board.dig_rate); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci strcpy(card->driver, "Bt87x"); 87762306a36Sopenharmony_ci sprintf(card->shortname, "Brooktree Bt%x", pci->device); 87862306a36Sopenharmony_ci sprintf(card->longname, "%s at %#llx, irq %i", 87962306a36Sopenharmony_ci card->shortname, (unsigned long long)pci_resource_start(pci, 0), 88062306a36Sopenharmony_ci chip->irq); 88162306a36Sopenharmony_ci strcpy(card->mixername, "Bt87x"); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci err = snd_card_register(card); 88462306a36Sopenharmony_ci if (err < 0) 88562306a36Sopenharmony_ci return err; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci pci_set_drvdata(pci, card); 88862306a36Sopenharmony_ci ++dev; 88962306a36Sopenharmony_ci return 0; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic int snd_bt87x_probe(struct pci_dev *pci, 89362306a36Sopenharmony_ci const struct pci_device_id *pci_id) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci return snd_card_free_on_error(&pci->dev, __snd_bt87x_probe(pci, pci_id)); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci/* default entries for all Bt87x cards - it's not exported */ 89962306a36Sopenharmony_ci/* driver_data is set to 0 to call detection */ 90062306a36Sopenharmony_cistatic const struct pci_device_id snd_bt87x_default_ids[] = { 90162306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN), 90262306a36Sopenharmony_ci BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN), 90362306a36Sopenharmony_ci { } 90462306a36Sopenharmony_ci}; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic struct pci_driver driver = { 90762306a36Sopenharmony_ci .name = KBUILD_MODNAME, 90862306a36Sopenharmony_ci .id_table = snd_bt87x_ids, 90962306a36Sopenharmony_ci .probe = snd_bt87x_probe, 91062306a36Sopenharmony_ci}; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic int __init alsa_card_bt87x_init(void) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci if (load_all) 91562306a36Sopenharmony_ci driver.id_table = snd_bt87x_default_ids; 91662306a36Sopenharmony_ci return pci_register_driver(&driver); 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic void __exit alsa_card_bt87x_exit(void) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci pci_unregister_driver(&driver); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cimodule_init(alsa_card_bt87x_init) 92562306a36Sopenharmony_cimodule_exit(alsa_card_bt87x_exit) 926