brintos

brintos / linux-shallow public Read only

0
0
Text · 14.1 KiB · 25dc8d4 Raw
595 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *4 *  Support for CX23885 analog audio capture5 *6 *    (c) 2008 Mijhail Moreyra <mijhail.moreyra@gmail.com>7 *    Adapted from cx88-alsa.c8 *    (c) 2009 Steven Toth <stoth@kernellabs.com>9 */10 11#include "cx23885.h"12#include "cx23885-reg.h"13 14#include <linux/module.h>15#include <linux/init.h>16#include <linux/device.h>17#include <linux/interrupt.h>18#include <linux/vmalloc.h>19#include <linux/dma-mapping.h>20#include <linux/pci.h>21 22#include <asm/delay.h>23 24#include <sound/core.h>25#include <sound/pcm.h>26#include <sound/pcm_params.h>27#include <sound/control.h>28#include <sound/initval.h>29 30#include <sound/tlv.h>31 32#define AUDIO_SRAM_CHANNEL	SRAM_CH0733 34#define dprintk(level, fmt, arg...) do {				\35	if (audio_debug + 1 > level)					\36		printk(KERN_DEBUG pr_fmt("%s: alsa: " fmt), \37			chip->dev->name, ##arg); \38} while(0)39 40/****************************************************************************41			Module global static vars42 ****************************************************************************/43 44static unsigned int disable_analog_audio;45module_param(disable_analog_audio, int, 0644);46MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver");47 48static unsigned int audio_debug;49module_param(audio_debug, int, 0644);50MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]");51 52/****************************************************************************53			Board specific functions54 ****************************************************************************/55 56/* Constants taken from cx88-reg.h */57#define AUD_INT_DN_RISCI1       (1 <<  0)58#define AUD_INT_UP_RISCI1       (1 <<  1)59#define AUD_INT_RDS_DN_RISCI1   (1 <<  2)60#define AUD_INT_DN_RISCI2       (1 <<  4) /* yes, 3 is skipped */61#define AUD_INT_UP_RISCI2       (1 <<  5)62#define AUD_INT_RDS_DN_RISCI2   (1 <<  6)63#define AUD_INT_DN_SYNC         (1 << 12)64#define AUD_INT_UP_SYNC         (1 << 13)65#define AUD_INT_RDS_DN_SYNC     (1 << 14)66#define AUD_INT_OPC_ERR         (1 << 16)67#define AUD_INT_BER_IRQ         (1 << 20)68#define AUD_INT_MCHG_IRQ        (1 << 21)69#define GP_COUNT_CONTROL_RESET	0x370 71static int cx23885_alsa_dma_init(struct cx23885_audio_dev *chip,72				 unsigned long nr_pages)73{74	struct cx23885_audio_buffer *buf = chip->buf;75	struct page *pg;76	int i;77 78	buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT);79	if (NULL == buf->vaddr) {80		dprintk(1, "vmalloc_32(%lu pages) failed\n", nr_pages);81		return -ENOMEM;82	}83 84	dprintk(1, "vmalloc is at addr %p, size=%lu\n",85		buf->vaddr, nr_pages << PAGE_SHIFT);86 87	memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT);88	buf->nr_pages = nr_pages;89 90	buf->sglist = vzalloc(array_size(sizeof(*buf->sglist), buf->nr_pages));91	if (NULL == buf->sglist)92		goto vzalloc_err;93 94	sg_init_table(buf->sglist, buf->nr_pages);95	for (i = 0; i < buf->nr_pages; i++) {96		pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE);97		if (NULL == pg)98			goto vmalloc_to_page_err;99		sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0);100	}101	return 0;102 103vmalloc_to_page_err:104	vfree(buf->sglist);105	buf->sglist = NULL;106vzalloc_err:107	vfree(buf->vaddr);108	buf->vaddr = NULL;109	return -ENOMEM;110}111 112static int cx23885_alsa_dma_map(struct cx23885_audio_dev *dev)113{114	struct cx23885_audio_buffer *buf = dev->buf;115 116	buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist,117			buf->nr_pages, DMA_FROM_DEVICE);118 119	if (0 == buf->sglen) {120		pr_warn("%s: cx23885_alsa_map_sg failed\n", __func__);121		return -ENOMEM;122	}123	return 0;124}125 126static int cx23885_alsa_dma_unmap(struct cx23885_audio_dev *dev)127{128	struct cx23885_audio_buffer *buf = dev->buf;129 130	if (!buf->sglen)131		return 0;132 133	dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->nr_pages, DMA_FROM_DEVICE);134	buf->sglen = 0;135	return 0;136}137 138static int cx23885_alsa_dma_free(struct cx23885_audio_buffer *buf)139{140	vfree(buf->sglist);141	buf->sglist = NULL;142	vfree(buf->vaddr);143	buf->vaddr = NULL;144	return 0;145}146 147/*148 * BOARD Specific: Sets audio DMA149 */150 151static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip)152{153	struct cx23885_audio_buffer *buf = chip->buf;154	struct cx23885_dev *dev  = chip->dev;155	struct sram_channel *audio_ch =156		&dev->sram_channels[AUDIO_SRAM_CHANNEL];157 158	dprintk(1, "%s()\n", __func__);159 160	/* Make sure RISC/FIFO are off before changing FIFO/RISC settings */161	cx_clear(AUD_INT_DMA_CTL, 0x11);162 163	/* setup fifo + format - out channel */164	cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl,165		buf->risc.dma);166 167	/* sets bpl size */168	cx_write(AUD_INT_A_LNGTH, buf->bpl);169 170	/* This is required to get good audio (1 seems to be ok) */171	cx_write(AUD_INT_A_MODE, 1);172 173	/* reset counter */174	cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);175	atomic_set(&chip->count, 0);176 177	dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d byte buffer\n",178		buf->bpl, cx_read(audio_ch->cmds_start+12)>>1,179		chip->num_periods, buf->bpl * chip->num_periods);180 181	/* Enables corresponding bits at AUD_INT_STAT */182	cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |183				    AUD_INT_DN_RISCI1);184 185	/* Clean any pending interrupt bits already set */186	cx_write(AUDIO_INT_INT_STAT, ~0);187 188	/* enable audio irqs */189	cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT);190 191	/* start dma */192	cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */193	cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and194					  RISC enable */195	if (audio_debug)196		cx23885_sram_channel_dump(chip->dev, audio_ch);197 198	return 0;199}200 201/*202 * BOARD Specific: Resets audio DMA203 */204static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip)205{206	struct cx23885_dev *dev = chip->dev;207	dprintk(1, "Stopping audio DMA\n");208 209	/* stop dma */210	cx_clear(AUD_INT_DMA_CTL, 0x11);211 212	/* disable irqs */213	cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT);214	cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |215				    AUD_INT_DN_RISCI1);216 217	if (audio_debug)218		cx23885_sram_channel_dump(chip->dev,219			&dev->sram_channels[AUDIO_SRAM_CHANNEL]);220 221	return 0;222}223 224/*225 * BOARD Specific: Handles audio IRQ226 */227int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask)228{229	struct cx23885_audio_dev *chip = dev->audio_dev;230 231	if (0 == (status & mask))232		return 0;233 234	cx_write(AUDIO_INT_INT_STAT, status);235 236	/* risc op code error */237	if (status & AUD_INT_OPC_ERR) {238		pr_warn("%s/1: Audio risc op code error\n",239			dev->name);240		cx_clear(AUD_INT_DMA_CTL, 0x11);241		cx23885_sram_channel_dump(dev,242			&dev->sram_channels[AUDIO_SRAM_CHANNEL]);243	}244	if (status & AUD_INT_DN_SYNC) {245		dprintk(1, "Downstream sync error\n");246		cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET);247		return 1;248	}249	/* risc1 downstream */250	if (status & AUD_INT_DN_RISCI1) {251		atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT));252		snd_pcm_period_elapsed(chip->substream);253	}254	/* FIXME: Any other status should deserve a special handling? */255 256	return 1;257}258 259static int dsp_buffer_free(struct cx23885_audio_dev *chip)260{261	struct cx23885_riscmem *risc;262 263	BUG_ON(!chip->dma_size);264 265	dprintk(2, "Freeing buffer\n");266	cx23885_alsa_dma_unmap(chip);267	cx23885_alsa_dma_free(chip->buf);268	risc = &chip->buf->risc;269	dma_free_coherent(&chip->pci->dev, risc->size, risc->cpu, risc->dma);270	kfree(chip->buf);271 272	chip->buf = NULL;273	chip->dma_size = 0;274 275	return 0;276}277 278/****************************************************************************279				ALSA PCM Interface280 ****************************************************************************/281 282/*283 * Digital hardware definition284 */285#define DEFAULT_FIFO_SIZE	4096286 287static const struct snd_pcm_hardware snd_cx23885_digital_hw = {288	.info = SNDRV_PCM_INFO_MMAP |289		SNDRV_PCM_INFO_INTERLEAVED |290		SNDRV_PCM_INFO_BLOCK_TRANSFER |291		SNDRV_PCM_INFO_MMAP_VALID,292	.formats = SNDRV_PCM_FMTBIT_S16_LE,293 294	.rates =		SNDRV_PCM_RATE_48000,295	.rate_min =		48000,296	.rate_max =		48000,297	.channels_min = 2,298	.channels_max = 2,299	/* Analog audio output will be full of clicks and pops if there300	   are not exactly four lines in the SRAM FIFO buffer.  */301	.period_bytes_min = DEFAULT_FIFO_SIZE/4,302	.period_bytes_max = DEFAULT_FIFO_SIZE/4,303	.periods_min = 1,304	.periods_max = 1024,305	.buffer_bytes_max = (1024*1024),306};307 308/*309 * audio pcm capture open callback310 */311static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream)312{313	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);314	struct snd_pcm_runtime *runtime = substream->runtime;315	int err;316 317	if (!chip) {318		pr_err("BUG: cx23885 can't find device struct. Can't proceed with open\n");319		return -ENODEV;320	}321 322	err = snd_pcm_hw_constraint_pow2(runtime, 0,323		SNDRV_PCM_HW_PARAM_PERIODS);324	if (err < 0)325		goto _error;326 327	chip->substream = substream;328 329	runtime->hw = snd_cx23885_digital_hw;330 331	if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size !=332		DEFAULT_FIFO_SIZE) {333		unsigned int bpl = chip->dev->334			sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4;335		bpl &= ~7; /* must be multiple of 8 */336		runtime->hw.period_bytes_min = bpl;337		runtime->hw.period_bytes_max = bpl;338	}339 340	return 0;341_error:342	dprintk(1, "Error opening PCM!\n");343	return err;344}345 346/*347 * audio close callback348 */349static int snd_cx23885_close(struct snd_pcm_substream *substream)350{351	return 0;352}353 354 355/*356 * hw_params callback357 */358static int snd_cx23885_hw_params(struct snd_pcm_substream *substream,359			      struct snd_pcm_hw_params *hw_params)360{361	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);362	struct cx23885_audio_buffer *buf;363	int ret;364 365	if (substream->runtime->dma_area) {366		dsp_buffer_free(chip);367		substream->runtime->dma_area = NULL;368	}369 370	chip->period_size = params_period_bytes(hw_params);371	chip->num_periods = params_periods(hw_params);372	chip->dma_size = chip->period_size * params_periods(hw_params);373 374	BUG_ON(!chip->dma_size);375	BUG_ON(chip->num_periods & (chip->num_periods-1));376 377	buf = kzalloc(sizeof(*buf), GFP_KERNEL);378	if (NULL == buf)379		return -ENOMEM;380 381	buf->bpl = chip->period_size;382	chip->buf = buf;383 384	ret = cx23885_alsa_dma_init(chip,385			(PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));386	if (ret < 0)387		goto error;388 389	ret = cx23885_alsa_dma_map(chip);390	if (ret < 0)391		goto error;392 393	ret = cx23885_risc_databuffer(chip->pci, &buf->risc, buf->sglist,394				   chip->period_size, chip->num_periods, 1);395	if (ret < 0)396		goto error;397 398	/* Loop back to start of program */399	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);400	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);401	buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */402 403	substream->runtime->dma_area = chip->buf->vaddr;404	substream->runtime->dma_bytes = chip->dma_size;405	substream->runtime->dma_addr = 0;406 407	return 0;408 409error:410	kfree(buf);411	chip->buf = NULL;412	return ret;413}414 415/*416 * hw free callback417 */418static int snd_cx23885_hw_free(struct snd_pcm_substream *substream)419{420 421	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);422 423	if (substream->runtime->dma_area) {424		dsp_buffer_free(chip);425		substream->runtime->dma_area = NULL;426	}427 428	return 0;429}430 431/*432 * prepare callback433 */434static int snd_cx23885_prepare(struct snd_pcm_substream *substream)435{436	return 0;437}438 439/*440 * trigger callback441 */442static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream,443	int cmd)444{445	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);446	int err;447 448	/* Local interrupts are already disabled by ALSA */449	spin_lock(&chip->lock);450 451	switch (cmd) {452	case SNDRV_PCM_TRIGGER_START:453		err = cx23885_start_audio_dma(chip);454		break;455	case SNDRV_PCM_TRIGGER_STOP:456		err = cx23885_stop_audio_dma(chip);457		break;458	default:459		err = -EINVAL;460		break;461	}462 463	spin_unlock(&chip->lock);464 465	return err;466}467 468/*469 * pointer callback470 */471static snd_pcm_uframes_t snd_cx23885_pointer(472	struct snd_pcm_substream *substream)473{474	struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream);475	struct snd_pcm_runtime *runtime = substream->runtime;476	u16 count;477 478	count = atomic_read(&chip->count);479 480	return runtime->period_size * (count & (runtime->periods-1));481}482 483/*484 * page callback (needed for mmap)485 */486static struct page *snd_cx23885_page(struct snd_pcm_substream *substream,487				unsigned long offset)488{489	void *pageptr = substream->runtime->dma_area + offset;490	return vmalloc_to_page(pageptr);491}492 493/*494 * operators495 */496static const struct snd_pcm_ops snd_cx23885_pcm_ops = {497	.open = snd_cx23885_pcm_open,498	.close = snd_cx23885_close,499	.hw_params = snd_cx23885_hw_params,500	.hw_free = snd_cx23885_hw_free,501	.prepare = snd_cx23885_prepare,502	.trigger = snd_cx23885_card_trigger,503	.pointer = snd_cx23885_pointer,504	.page = snd_cx23885_page,505};506 507/*508 * create a PCM device509 */510static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device,511	char *name)512{513	int err;514	struct snd_pcm *pcm;515 516	err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);517	if (err < 0)518		return err;519	pcm->private_data = chip;520	strscpy(pcm->name, name, sizeof(pcm->name));521	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops);522 523	return 0;524}525 526/****************************************************************************527			Basic Flow for Sound Devices528 ****************************************************************************/529 530/*531 * Alsa Constructor - Component probe532 */533 534struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev)535{536	struct snd_card *card;537	struct cx23885_audio_dev *chip;538	int err;539 540	if (disable_analog_audio)541		return NULL;542 543	if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) {544		pr_warn("%s(): Missing SRAM channel configuration for analog TV Audio\n",545		       __func__);546		return NULL;547	}548 549	err = snd_card_new(&dev->pci->dev,550			   SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,551			THIS_MODULE, sizeof(struct cx23885_audio_dev), &card);552	if (err < 0)553		goto error_msg;554 555	chip = (struct cx23885_audio_dev *) card->private_data;556	chip->dev = dev;557	chip->pci = dev->pci;558	chip->card = card;559	spin_lock_init(&chip->lock);560 561	err = snd_cx23885_pcm(chip, 0, "CX23885 Digital");562	if (err < 0)563		goto error;564 565	strscpy(card->driver, "CX23885", sizeof(card->driver));566	sprintf(card->shortname, "Conexant CX23885");567	sprintf(card->longname, "%s at %s", card->shortname, dev->name);568 569	err = snd_card_register(card);570	if (err < 0)571		goto error;572 573	dprintk(0, "registered ALSA audio device\n");574 575	return chip;576 577error:578	snd_card_free(card);579error_msg:580	pr_err("%s(): Failed to register analog audio adapter\n",581	       __func__);582 583	return NULL;584}585 586/*587 * ALSA destructor588 */589void cx23885_audio_unregister(struct cx23885_dev *dev)590{591	struct cx23885_audio_dev *chip = dev->audio_dev;592 593	snd_card_free(chip->card);594}595