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