162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Support for CX23885 analog audio capture
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *    (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com>
762306a36Sopenharmony_ci *    Adapted from cx88-alsa.c
862306a36Sopenharmony_ci *    (c) 2009 Steven Toth <stoth@kernellabs.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "cx23885.h"
1262306a36Sopenharmony_ci#include "cx23885-reg.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/device.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/vmalloc.h>
1962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2062306a36Sopenharmony_ci#include <linux/pci.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <asm/delay.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <sound/core.h>
2562306a36Sopenharmony_ci#include <sound/pcm.h>
2662306a36Sopenharmony_ci#include <sound/pcm_params.h>
2762306a36Sopenharmony_ci#include <sound/control.h>
2862306a36Sopenharmony_ci#include <sound/initval.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <sound/tlv.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define AUDIO_SRAM_CHANNEL	SRAM_CH07
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define dprintk(level, fmt, arg...) do {				\
3562306a36Sopenharmony_ci	if (audio_debug + 1 > level)					\
3662306a36Sopenharmony_ci		printk(KERN_DEBUG pr_fmt("%s: alsa: " fmt), \
3762306a36Sopenharmony_ci			chip->dev->name, ##arg); \
3862306a36Sopenharmony_ci} while(0)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/****************************************************************************
4162306a36Sopenharmony_ci			Module global static vars
4262306a36Sopenharmony_ci ****************************************************************************/
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic unsigned int disable_analog_audio;
4562306a36Sopenharmony_cimodule_param(disable_analog_audio, int, 0644);
4662306a36Sopenharmony_ciMODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver");
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic unsigned int audio_debug;
4962306a36Sopenharmony_cimodule_param(audio_debug, int, 0644);
5062306a36Sopenharmony_ciMODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/****************************************************************************
5362306a36Sopenharmony_ci			Board specific functions
5462306a36Sopenharmony_ci ****************************************************************************/
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* Constants taken from cx88-reg.h */
5762306a36Sopenharmony_ci#define AUD_INT_DN_RISCI1       (1 <<  0)
5862306a36Sopenharmony_ci#define AUD_INT_UP_RISCI1       (1 <<  1)
5962306a36Sopenharmony_ci#define AUD_INT_RDS_DN_RISCI1   (1 <<  2)
6062306a36Sopenharmony_ci#define AUD_INT_DN_RISCI2       (1 <<  4) /* yes, 3 is skipped */
6162306a36Sopenharmony_ci#define AUD_INT_UP_RISCI2       (1 <<  5)
6262306a36Sopenharmony_ci#define AUD_INT_RDS_DN_RISCI2   (1 <<  6)
6362306a36Sopenharmony_ci#define AUD_INT_DN_SYNC         (1 << 12)
6462306a36Sopenharmony_ci#define AUD_INT_UP_SYNC         (1 << 13)
6562306a36Sopenharmony_ci#define AUD_INT_RDS_DN_SYNC     (1 << 14)
6662306a36Sopenharmony_ci#define AUD_INT_OPC_ERR         (1 << 16)
6762306a36Sopenharmony_ci#define AUD_INT_BER_IRQ         (1 << 20)
6862306a36Sopenharmony_ci#define AUD_INT_MCHG_IRQ        (1 << 21)
6962306a36Sopenharmony_ci#define GP_COUNT_CONTROL_RESET	0x3
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip,
7262306a36Sopenharmony_ci				 unsigned long nr_pages)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct cx23885_audio_buffer *buf = chip->buf;
7562306a36Sopenharmony_ci	struct page *pg;
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);
7962306a36Sopenharmony_ci	if (NULL == buf->vaddr) {
8062306a36Sopenharmony_ci		dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages);
8162306a36Sopenharmony_ci		return -ENOMEM;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	dprintk(1, "vmalloc is at addr %p, size=%lu\n",
8562306a36Sopenharmony_ci		buf->vaddr, nr_pages << PAGE_SHIFT);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);
8862306a36Sopenharmony_ci	buf->nr_pages = nr_pages;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	buf->sglist = vzalloc(array_size(sizeof(*buf->sglist), buf->nr_pages));
9162306a36Sopenharmony_ci	if (NULL == buf->sglist)
9262306a36Sopenharmony_ci		goto vzalloc_err;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	sg_init_table(buf->sglist, buf->nr_pages);
9562306a36Sopenharmony_ci	for (i = 0; i < buf->nr_pages; i++) {
9662306a36Sopenharmony_ci		pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE);
9762306a36Sopenharmony_ci		if (NULL == pg)
9862306a36Sopenharmony_ci			goto vmalloc_to_page_err;
9962306a36Sopenharmony_ci		sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0);
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_civmalloc_to_page_err:
10462306a36Sopenharmony_ci	vfree(buf->sglist);
10562306a36Sopenharmony_ci	buf->sglist = NULL;
10662306a36Sopenharmony_civzalloc_err:
10762306a36Sopenharmony_ci	vfree(buf->vaddr);
10862306a36Sopenharmony_ci	buf->vaddr = NULL;
10962306a36Sopenharmony_ci	return -ENOMEM;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int cx23885_alsa_dma_map(struct cx23885_audio_dev *dev)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct cx23885_audio_buffer *buf = dev->buf;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist,
11762306a36Sopenharmony_ci			buf->nr_pages, DMA_FROM_DEVICE);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (0 == buf->sglen) {
12062306a36Sopenharmony_ci		pr_warn("%s: cx23885_alsa_map_sg failed\n", __func__);
12162306a36Sopenharmony_ci		return -ENOMEM;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int cx23885_alsa_dma_unmap(struct cx23885_audio_dev *dev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct cx23885_audio_buffer *buf = dev->buf;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!buf->sglen)
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->nr_pages, DMA_FROM_DEVICE);
13462306a36Sopenharmony_ci	buf->sglen = 0;
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int cx23885_alsa_dma_free(struct cx23885_audio_buffer *buf)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	vfree(buf->sglist);
14162306a36Sopenharmony_ci	buf->sglist = NULL;
14262306a36Sopenharmony_ci	vfree(buf->vaddr);
14362306a36Sopenharmony_ci	buf->vaddr = NULL;
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * BOARD Specific: Sets audio DMA
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int cx23885_start_audio_dma(struct cx23885_audio_dev *chip)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct cx23885_audio_buffer *buf = chip->buf;
15462306a36Sopenharmony_ci	struct cx23885_dev *dev  = chip->dev;
15562306a36Sopenharmony_ci	struct sram_channel *audio_ch =
15662306a36Sopenharmony_ci		&dev->sram_channels[AUDIO_SRAM_CHANNEL];
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	dprintk(1, "%s()\n", __func__);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
16162306a36Sopenharmony_ci	cx_clear(AUD_INT_DMA_CTL, 0x11);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* setup fifo + format - out channel */
16462306a36Sopenharmony_ci	cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl,
16562306a36Sopenharmony_ci		buf->risc.dma);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* sets bpl size */
16862306a36Sopenharmony_ci	cx_write(AUD_INT_A_LNGTH, buf->bpl);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* This is required to get good audio (1 seems to be ok) */
17162306a36Sopenharmony_ci	cx_write(AUD_INT_A_MODE, 1);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* reset counter */
17462306a36Sopenharmony_ci	cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
17562306a36Sopenharmony_ci	atomic_set(&chip->count, 0);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d byte buffer\n",
17862306a36Sopenharmony_ci		buf->bpl, cx_read(audio_ch->cmds_start+12)>>1,
17962306a36Sopenharmony_ci		chip->num_periods, buf->bpl * chip->num_periods);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* Enables corresponding bits at AUD_INT_STAT */
18262306a36Sopenharmony_ci	cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
18362306a36Sopenharmony_ci				    AUD_INT_DN_RISCI1);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* Clean any pending interrupt bits already set */
18662306a36Sopenharmony_ci	cx_write(AUDIO_INT_INT_STAT, ~0);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* enable audio irqs */
18962306a36Sopenharmony_ci	cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* start dma */
19262306a36Sopenharmony_ci	cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
19362306a36Sopenharmony_ci	cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and
19462306a36Sopenharmony_ci					  RISC enable */
19562306a36Sopenharmony_ci	if (audio_debug)
19662306a36Sopenharmony_ci		cx23885_sram_channel_dump(chip->dev, audio_ch);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/*
20262306a36Sopenharmony_ci * BOARD Specific: Resets audio DMA
20362306a36Sopenharmony_ci */
20462306a36Sopenharmony_cistatic int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct cx23885_dev *dev = chip->dev;
20762306a36Sopenharmony_ci	dprintk(1, "Stopping audio DMA\n");
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* stop dma */
21062306a36Sopenharmony_ci	cx_clear(AUD_INT_DMA_CTL, 0x11);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* disable irqs */
21362306a36Sopenharmony_ci	cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);
21462306a36Sopenharmony_ci	cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
21562306a36Sopenharmony_ci				    AUD_INT_DN_RISCI1);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (audio_debug)
21862306a36Sopenharmony_ci		cx23885_sram_channel_dump(chip->dev,
21962306a36Sopenharmony_ci			&dev->sram_channels[AUDIO_SRAM_CHANNEL]);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/*
22562306a36Sopenharmony_ci * BOARD Specific: Handles audio IRQ
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_ciint cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct cx23885_audio_dev *chip = dev->audio_dev;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (0 == (status & mask))
23262306a36Sopenharmony_ci		return 0;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	cx_write(AUDIO_INT_INT_STAT, status);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* risc op code error */
23762306a36Sopenharmony_ci	if (status & AUD_INT_OPC_ERR) {
23862306a36Sopenharmony_ci		pr_warn("%s/1: Audio risc op code error\n",
23962306a36Sopenharmony_ci			dev->name);
24062306a36Sopenharmony_ci		cx_clear(AUD_INT_DMA_CTL, 0x11);
24162306a36Sopenharmony_ci		cx23885_sram_channel_dump(dev,
24262306a36Sopenharmony_ci			&dev->sram_channels[AUDIO_SRAM_CHANNEL]);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	if (status & AUD_INT_DN_SYNC) {
24562306a36Sopenharmony_ci		dprintk(1, "Downstream sync error\n");
24662306a36Sopenharmony_ci		cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);
24762306a36Sopenharmony_ci		return 1;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	/* risc1 downstream */
25062306a36Sopenharmony_ci	if (status & AUD_INT_DN_RISCI1) {
25162306a36Sopenharmony_ci		atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT));
25262306a36Sopenharmony_ci		snd_pcm_period_elapsed(chip->substream);
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci	/* FIXME: Any other status should deserve a special handling? */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return 1;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int dsp_buffer_free(struct cx23885_audio_dev *chip)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct cx23885_riscmem *risc;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	BUG_ON(!chip->dma_size);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	dprintk(2, "Freeing buffer\n");
26662306a36Sopenharmony_ci	cx23885_alsa_dma_unmap(chip);
26762306a36Sopenharmony_ci	cx23885_alsa_dma_free(chip->buf);
26862306a36Sopenharmony_ci	risc = &chip->buf->risc;
26962306a36Sopenharmony_ci	dma_free_coherent(&chip->pci->dev, risc->size, risc->cpu, risc->dma);
27062306a36Sopenharmony_ci	kfree(chip->buf);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	chip->buf = NULL;
27362306a36Sopenharmony_ci	chip->dma_size = 0;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/****************************************************************************
27962306a36Sopenharmony_ci				ALSA PCM Interface
28062306a36Sopenharmony_ci ****************************************************************************/
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/*
28362306a36Sopenharmony_ci * Digital hardware definition
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_ci#define DEFAULT_FIFO_SIZE	4096
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cx23885_digital_hw = {
28862306a36Sopenharmony_ci	.info = SNDRV_PCM_INFO_MMAP |
28962306a36Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED |
29062306a36Sopenharmony_ci		SNDRV_PCM_INFO_BLOCK_TRANSFER |
29162306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID,
29262306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE,
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_48000,
29562306a36Sopenharmony_ci	.rate_min =		48000,
29662306a36Sopenharmony_ci	.rate_max =		48000,
29762306a36Sopenharmony_ci	.channels_min = 2,
29862306a36Sopenharmony_ci	.channels_max = 2,
29962306a36Sopenharmony_ci	/* Analog audio output will be full of clicks and pops if there
30062306a36Sopenharmony_ci	   are not exactly four lines in the SRAM FIFO buffer.  */
30162306a36Sopenharmony_ci	.period_bytes_min = DEFAULT_FIFO_SIZE/4,
30262306a36Sopenharmony_ci	.period_bytes_max = DEFAULT_FIFO_SIZE/4,
30362306a36Sopenharmony_ci	.periods_min = 1,
30462306a36Sopenharmony_ci	.periods_max = 1024,
30562306a36Sopenharmony_ci	.buffer_bytes_max = (1024*1024),
30662306a36Sopenharmony_ci};
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci/*
30962306a36Sopenharmony_ci * audio pcm capture open callback
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_cistatic int snd_cx23885_pcm_open(struct snd_pcm_substream *substream)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
31462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
31562306a36Sopenharmony_ci	int err;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (!chip) {
31862306a36Sopenharmony_ci		pr_err("BUG: cx23885 can't find device struct. Can't proceed with open\n");
31962306a36Sopenharmony_ci		return -ENODEV;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	err = snd_pcm_hw_constraint_pow2(runtime, 0,
32362306a36Sopenharmony_ci		SNDRV_PCM_HW_PARAM_PERIODS);
32462306a36Sopenharmony_ci	if (err < 0)
32562306a36Sopenharmony_ci		goto _error;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	chip->substream = substream;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	runtime->hw = snd_cx23885_digital_hw;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=
33262306a36Sopenharmony_ci		DEFAULT_FIFO_SIZE) {
33362306a36Sopenharmony_ci		unsigned int bpl = chip->dev->
33462306a36Sopenharmony_ci			sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4;
33562306a36Sopenharmony_ci		bpl &= ~7; /* must be multiple of 8 */
33662306a36Sopenharmony_ci		runtime->hw.period_bytes_min = bpl;
33762306a36Sopenharmony_ci		runtime->hw.period_bytes_max = bpl;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return 0;
34162306a36Sopenharmony_ci_error:
34262306a36Sopenharmony_ci	dprintk(1, "Error opening PCM!\n");
34362306a36Sopenharmony_ci	return err;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/*
34762306a36Sopenharmony_ci * audio close callback
34862306a36Sopenharmony_ci */
34962306a36Sopenharmony_cistatic int snd_cx23885_close(struct snd_pcm_substream *substream)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/*
35662306a36Sopenharmony_ci * hw_params callback
35762306a36Sopenharmony_ci */
35862306a36Sopenharmony_cistatic int snd_cx23885_hw_params(struct snd_pcm_substream *substream,
35962306a36Sopenharmony_ci			      struct snd_pcm_hw_params *hw_params)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
36262306a36Sopenharmony_ci	struct cx23885_audio_buffer *buf;
36362306a36Sopenharmony_ci	int ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (substream->runtime->dma_area) {
36662306a36Sopenharmony_ci		dsp_buffer_free(chip);
36762306a36Sopenharmony_ci		substream->runtime->dma_area = NULL;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	chip->period_size = params_period_bytes(hw_params);
37162306a36Sopenharmony_ci	chip->num_periods = params_periods(hw_params);
37262306a36Sopenharmony_ci	chip->dma_size = chip->period_size * params_periods(hw_params);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	BUG_ON(!chip->dma_size);
37562306a36Sopenharmony_ci	BUG_ON(chip->num_periods & (chip->num_periods-1));
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
37862306a36Sopenharmony_ci	if (NULL == buf)
37962306a36Sopenharmony_ci		return -ENOMEM;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	buf->bpl = chip->period_size;
38262306a36Sopenharmony_ci	chip->buf = buf;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ret = cx23885_alsa_dma_init(chip,
38562306a36Sopenharmony_ci			(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
38662306a36Sopenharmony_ci	if (ret < 0)
38762306a36Sopenharmony_ci		goto error;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	ret = cx23885_alsa_dma_map(chip);
39062306a36Sopenharmony_ci	if (ret < 0)
39162306a36Sopenharmony_ci		goto error;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	ret = cx23885_risc_databuffer(chip->pci, &buf->risc, buf->sglist,
39462306a36Sopenharmony_ci				   chip->period_size, chip->num_periods, 1);
39562306a36Sopenharmony_ci	if (ret < 0)
39662306a36Sopenharmony_ci		goto error;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Loop back to start of program */
39962306a36Sopenharmony_ci	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
40062306a36Sopenharmony_ci	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
40162306a36Sopenharmony_ci	buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	substream->runtime->dma_area = chip->buf->vaddr;
40462306a36Sopenharmony_ci	substream->runtime->dma_bytes = chip->dma_size;
40562306a36Sopenharmony_ci	substream->runtime->dma_addr = 0;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cierror:
41062306a36Sopenharmony_ci	kfree(buf);
41162306a36Sopenharmony_ci	chip->buf = NULL;
41262306a36Sopenharmony_ci	return ret;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/*
41662306a36Sopenharmony_ci * hw free callback
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_cistatic int snd_cx23885_hw_free(struct snd_pcm_substream *substream)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (substream->runtime->dma_area) {
42462306a36Sopenharmony_ci		dsp_buffer_free(chip);
42562306a36Sopenharmony_ci		substream->runtime->dma_area = NULL;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return 0;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci/*
43262306a36Sopenharmony_ci * prepare callback
43362306a36Sopenharmony_ci */
43462306a36Sopenharmony_cistatic int snd_cx23885_prepare(struct snd_pcm_substream *substream)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	return 0;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/*
44062306a36Sopenharmony_ci * trigger callback
44162306a36Sopenharmony_ci */
44262306a36Sopenharmony_cistatic int snd_cx23885_card_trigger(struct snd_pcm_substream *substream,
44362306a36Sopenharmony_ci	int cmd)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
44662306a36Sopenharmony_ci	int err;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Local interrupts are already disabled by ALSA */
44962306a36Sopenharmony_ci	spin_lock(&chip->lock);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	switch (cmd) {
45262306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
45362306a36Sopenharmony_ci		err = cx23885_start_audio_dma(chip);
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
45662306a36Sopenharmony_ci		err = cx23885_stop_audio_dma(chip);
45762306a36Sopenharmony_ci		break;
45862306a36Sopenharmony_ci	default:
45962306a36Sopenharmony_ci		err = -EINVAL;
46062306a36Sopenharmony_ci		break;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	spin_unlock(&chip->lock);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return err;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/*
46962306a36Sopenharmony_ci * pointer callback
47062306a36Sopenharmony_ci */
47162306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cx23885_pointer(
47262306a36Sopenharmony_ci	struct snd_pcm_substream *substream)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);
47562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
47662306a36Sopenharmony_ci	u16 count;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	count = atomic_read(&chip->count);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return runtime->period_size * (count & (runtime->periods-1));
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci/*
48462306a36Sopenharmony_ci * page callback (needed for mmap)
48562306a36Sopenharmony_ci */
48662306a36Sopenharmony_cistatic struct page *snd_cx23885_page(struct snd_pcm_substream *substream,
48762306a36Sopenharmony_ci				unsigned long offset)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	void *pageptr = substream->runtime->dma_area + offset;
49062306a36Sopenharmony_ci	return vmalloc_to_page(pageptr);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/*
49462306a36Sopenharmony_ci * operators
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cx23885_pcm_ops = {
49762306a36Sopenharmony_ci	.open = snd_cx23885_pcm_open,
49862306a36Sopenharmony_ci	.close = snd_cx23885_close,
49962306a36Sopenharmony_ci	.hw_params = snd_cx23885_hw_params,
50062306a36Sopenharmony_ci	.hw_free = snd_cx23885_hw_free,
50162306a36Sopenharmony_ci	.prepare = snd_cx23885_prepare,
50262306a36Sopenharmony_ci	.trigger = snd_cx23885_card_trigger,
50362306a36Sopenharmony_ci	.pointer = snd_cx23885_pointer,
50462306a36Sopenharmony_ci	.page = snd_cx23885_page,
50562306a36Sopenharmony_ci};
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci/*
50862306a36Sopenharmony_ci * create a PCM device
50962306a36Sopenharmony_ci */
51062306a36Sopenharmony_cistatic int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device,
51162306a36Sopenharmony_ci	char *name)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	int err;
51462306a36Sopenharmony_ci	struct snd_pcm *pcm;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
51762306a36Sopenharmony_ci	if (err < 0)
51862306a36Sopenharmony_ci		return err;
51962306a36Sopenharmony_ci	pcm->private_data = chip;
52062306a36Sopenharmony_ci	strscpy(pcm->name, name, sizeof(pcm->name));
52162306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci/****************************************************************************
52762306a36Sopenharmony_ci			Basic Flow for Sound Devices
52862306a36Sopenharmony_ci ****************************************************************************/
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci/*
53162306a36Sopenharmony_ci * Alsa Constructor - Component probe
53262306a36Sopenharmony_ci */
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistruct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct snd_card *card;
53762306a36Sopenharmony_ci	struct cx23885_audio_dev *chip;
53862306a36Sopenharmony_ci	int err;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (disable_analog_audio)
54162306a36Sopenharmony_ci		return NULL;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) {
54462306a36Sopenharmony_ci		pr_warn("%s(): Missing SRAM channel configuration for analog TV Audio\n",
54562306a36Sopenharmony_ci		       __func__);
54662306a36Sopenharmony_ci		return NULL;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	err = snd_card_new(&dev->pci->dev,
55062306a36Sopenharmony_ci			   SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
55162306a36Sopenharmony_ci			THIS_MODULE, sizeof(struct cx23885_audio_dev), &card);
55262306a36Sopenharmony_ci	if (err < 0)
55362306a36Sopenharmony_ci		goto error_msg;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	chip = (struct cx23885_audio_dev *) card->private_data;
55662306a36Sopenharmony_ci	chip->dev = dev;
55762306a36Sopenharmony_ci	chip->pci = dev->pci;
55862306a36Sopenharmony_ci	chip->card = card;
55962306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	err = snd_cx23885_pcm(chip, 0, "CX23885 Digital");
56262306a36Sopenharmony_ci	if (err < 0)
56362306a36Sopenharmony_ci		goto error;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	strscpy(card->driver, "CX23885", sizeof(card->driver));
56662306a36Sopenharmony_ci	sprintf(card->shortname, "Conexant CX23885");
56762306a36Sopenharmony_ci	sprintf(card->longname, "%s at %s", card->shortname, dev->name);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	err = snd_card_register(card);
57062306a36Sopenharmony_ci	if (err < 0)
57162306a36Sopenharmony_ci		goto error;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	dprintk(0, "registered ALSA audio device\n");
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return chip;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cierror:
57862306a36Sopenharmony_ci	snd_card_free(card);
57962306a36Sopenharmony_cierror_msg:
58062306a36Sopenharmony_ci	pr_err("%s(): Failed to register analog audio adapter\n",
58162306a36Sopenharmony_ci	       __func__);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return NULL;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/*
58762306a36Sopenharmony_ci * ALSA destructor
58862306a36Sopenharmony_ci */
58962306a36Sopenharmony_civoid cx23885_audio_unregister(struct cx23885_dev *dev)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct cx23885_audio_dev *chip = dev->audio_dev;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	snd_card_free(chip->card);
59462306a36Sopenharmony_ci}
595