cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tm6000-alsa.c (10356B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Support for audio capture for tm5600/6000/6010
      3// Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org>
      4//
      5// Based on cx88-alsa.c
      6
      7#include <linux/module.h>
      8#include <linux/init.h>
      9#include <linux/device.h>
     10#include <linux/interrupt.h>
     11#include <linux/usb.h>
     12#include <linux/slab.h>
     13
     14#include <linux/delay.h>
     15#include <sound/core.h>
     16#include <sound/pcm.h>
     17#include <sound/pcm_params.h>
     18#include <sound/control.h>
     19#include <sound/initval.h>
     20
     21
     22#include "tm6000.h"
     23#include "tm6000-regs.h"
     24
     25#undef dprintk
     26
     27#define dprintk(level, fmt, arg...) do {				   \
     28	if (debug >= level)						   \
     29		printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \
     30	} while (0)
     31
     32/****************************************************************************
     33			Module global static vars
     34 ****************************************************************************/
     35
     36static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
     37
     38static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
     39
     40module_param_array(enable, bool, NULL, 0444);
     41MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
     42
     43module_param_array(index, int, NULL, 0444);
     44MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
     45
     46
     47/****************************************************************************
     48				Module macros
     49 ****************************************************************************/
     50
     51MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
     52MODULE_AUTHOR("Mauro Carvalho Chehab");
     53MODULE_LICENSE("GPL v2");
     54static unsigned int debug;
     55module_param(debug, int, 0644);
     56MODULE_PARM_DESC(debug, "enable debug messages");
     57
     58/****************************************************************************
     59			Module specific functions
     60 ****************************************************************************/
     61
     62/*
     63 * BOARD Specific: Sets audio DMA
     64 */
     65
     66static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
     67{
     68	struct tm6000_core *core = chip->core;
     69
     70	dprintk(1, "Starting audio DMA\n");
     71
     72	/* Enables audio */
     73	tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40);
     74
     75	tm6000_set_audio_bitrate(core, 48000);
     76
     77	return 0;
     78}
     79
     80/*
     81 * BOARD Specific: Resets audio DMA
     82 */
     83static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
     84{
     85	struct tm6000_core *core = chip->core;
     86
     87	dprintk(1, "Stopping audio DMA\n");
     88
     89	/* Disables audio */
     90	tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40);
     91
     92	return 0;
     93}
     94
     95/****************************************************************************
     96				ALSA PCM Interface
     97 ****************************************************************************/
     98
     99/*
    100 * Digital hardware definition
    101 */
    102#define DEFAULT_FIFO_SIZE	4096
    103
    104static const struct snd_pcm_hardware snd_tm6000_digital_hw = {
    105	.info = SNDRV_PCM_INFO_BATCH |
    106		SNDRV_PCM_INFO_MMAP |
    107		SNDRV_PCM_INFO_INTERLEAVED |
    108		SNDRV_PCM_INFO_BLOCK_TRANSFER |
    109		SNDRV_PCM_INFO_MMAP_VALID,
    110	.formats = SNDRV_PCM_FMTBIT_S16_LE,
    111
    112	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
    113	.rate_min = 48000,
    114	.rate_max = 48000,
    115	.channels_min = 2,
    116	.channels_max = 2,
    117	.period_bytes_min = 64,
    118	.period_bytes_max = 12544,
    119	.periods_min = 2,
    120	.periods_max = 98,
    121	.buffer_bytes_max = 62720 * 8,
    122};
    123
    124/*
    125 * audio pcm capture open callback
    126 */
    127static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
    128{
    129	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
    130	struct snd_pcm_runtime *runtime = substream->runtime;
    131	int err;
    132
    133	err = snd_pcm_hw_constraint_pow2(runtime, 0,
    134					 SNDRV_PCM_HW_PARAM_PERIODS);
    135	if (err < 0)
    136		goto _error;
    137
    138	chip->substream = substream;
    139
    140	runtime->hw = snd_tm6000_digital_hw;
    141	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
    142
    143	return 0;
    144_error:
    145	dprintk(1, "Error opening PCM!\n");
    146	return err;
    147}
    148
    149/*
    150 * audio close callback
    151 */
    152static int snd_tm6000_close(struct snd_pcm_substream *substream)
    153{
    154	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
    155	struct tm6000_core *core = chip->core;
    156
    157	if (atomic_read(&core->stream_started) > 0) {
    158		atomic_set(&core->stream_started, 0);
    159		schedule_work(&core->wq_trigger);
    160	}
    161
    162	return 0;
    163}
    164
    165static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
    166{
    167	struct snd_tm6000_card *chip = core->adev;
    168	struct snd_pcm_substream *substream = chip->substream;
    169	struct snd_pcm_runtime *runtime;
    170	int period_elapsed = 0;
    171	unsigned int stride, buf_pos;
    172	int length;
    173
    174	if (atomic_read(&core->stream_started) == 0)
    175		return 0;
    176
    177	if (!size || !substream) {
    178		dprintk(1, "substream was NULL\n");
    179		return -EINVAL;
    180	}
    181
    182	runtime = substream->runtime;
    183	if (!runtime || !runtime->dma_area) {
    184		dprintk(1, "runtime was NULL\n");
    185		return -EINVAL;
    186	}
    187
    188	buf_pos = chip->buf_pos;
    189	stride = runtime->frame_bits >> 3;
    190
    191	if (stride == 0) {
    192		dprintk(1, "stride is zero\n");
    193		return -EINVAL;
    194	}
    195
    196	length = size / stride;
    197	if (length == 0) {
    198		dprintk(1, "%s: length was zero\n", __func__);
    199		return -EINVAL;
    200	}
    201
    202	dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size,
    203		runtime->dma_area, buf_pos,
    204		(unsigned int)runtime->buffer_size, stride);
    205
    206	if (buf_pos + length >= runtime->buffer_size) {
    207		unsigned int cnt = runtime->buffer_size - buf_pos;
    208		memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride);
    209		memcpy(runtime->dma_area, buf + cnt * stride,
    210			length * stride - cnt * stride);
    211	} else
    212		memcpy(runtime->dma_area + buf_pos * stride, buf,
    213			length * stride);
    214
    215	snd_pcm_stream_lock(substream);
    216
    217	chip->buf_pos += length;
    218	if (chip->buf_pos >= runtime->buffer_size)
    219		chip->buf_pos -= runtime->buffer_size;
    220
    221	chip->period_pos += length;
    222	if (chip->period_pos >= runtime->period_size) {
    223		chip->period_pos -= runtime->period_size;
    224		period_elapsed = 1;
    225	}
    226
    227	snd_pcm_stream_unlock(substream);
    228
    229	if (period_elapsed)
    230		snd_pcm_period_elapsed(substream);
    231
    232	return 0;
    233}
    234
    235/*
    236 * prepare callback
    237 */
    238static int snd_tm6000_prepare(struct snd_pcm_substream *substream)
    239{
    240	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
    241
    242	chip->buf_pos = 0;
    243	chip->period_pos = 0;
    244
    245	return 0;
    246}
    247
    248
    249/*
    250 * trigger callback
    251 */
    252static void audio_trigger(struct work_struct *work)
    253{
    254	struct tm6000_core *core = container_of(work, struct tm6000_core,
    255						wq_trigger);
    256	struct snd_tm6000_card *chip = core->adev;
    257
    258	if (atomic_read(&core->stream_started)) {
    259		dprintk(1, "starting capture");
    260		_tm6000_start_audio_dma(chip);
    261	} else {
    262		dprintk(1, "stopping capture");
    263		_tm6000_stop_audio_dma(chip);
    264	}
    265}
    266
    267static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
    268{
    269	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
    270	struct tm6000_core *core = chip->core;
    271	int err = 0;
    272
    273	switch (cmd) {
    274	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    275	case SNDRV_PCM_TRIGGER_RESUME:
    276	case SNDRV_PCM_TRIGGER_START:
    277		atomic_set(&core->stream_started, 1);
    278		break;
    279	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    280	case SNDRV_PCM_TRIGGER_SUSPEND:
    281	case SNDRV_PCM_TRIGGER_STOP:
    282		atomic_set(&core->stream_started, 0);
    283		break;
    284	default:
    285		err = -EINVAL;
    286		break;
    287	}
    288	schedule_work(&core->wq_trigger);
    289
    290	return err;
    291}
    292/*
    293 * pointer callback
    294 */
    295static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
    296{
    297	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
    298
    299	return chip->buf_pos;
    300}
    301
    302/*
    303 * operators
    304 */
    305static const struct snd_pcm_ops snd_tm6000_pcm_ops = {
    306	.open = snd_tm6000_pcm_open,
    307	.close = snd_tm6000_close,
    308	.prepare = snd_tm6000_prepare,
    309	.trigger = snd_tm6000_card_trigger,
    310	.pointer = snd_tm6000_pointer,
    311};
    312
    313/*
    314 * create a PCM device
    315 */
    316
    317/* FIXME: Control interface - How to control volume/mute? */
    318
    319/****************************************************************************
    320			Basic Flow for Sound Devices
    321 ****************************************************************************/
    322
    323/*
    324 * Alsa Constructor - Component probe
    325 */
    326static int tm6000_audio_init(struct tm6000_core *dev)
    327{
    328	struct snd_card		*card;
    329	struct snd_tm6000_card	*chip;
    330	int			rc;
    331	static int		devnr;
    332	char			component[14];
    333	struct snd_pcm		*pcm;
    334
    335	if (!dev)
    336		return 0;
    337
    338	if (devnr >= SNDRV_CARDS)
    339		return -ENODEV;
    340
    341	if (!enable[devnr])
    342		return -ENOENT;
    343
    344	rc = snd_card_new(&dev->udev->dev, index[devnr], "tm6000",
    345			  THIS_MODULE, 0, &card);
    346	if (rc < 0) {
    347		snd_printk(KERN_ERR "cannot create card instance %d\n", devnr);
    348		return rc;
    349	}
    350	strscpy(card->driver, "tm6000-alsa", sizeof(card->driver));
    351	strscpy(card->shortname, "TM5600/60x0", sizeof(card->shortname));
    352	sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d",
    353		dev->udev->bus->busnum, dev->udev->devnum);
    354
    355	sprintf(component, "USB%04x:%04x",
    356		le16_to_cpu(dev->udev->descriptor.idVendor),
    357		le16_to_cpu(dev->udev->descriptor.idProduct));
    358	snd_component_add(card, component);
    359
    360	chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL);
    361	if (!chip) {
    362		rc = -ENOMEM;
    363		goto error;
    364	}
    365
    366	chip->core = dev;
    367	chip->card = card;
    368	dev->adev = chip;
    369	spin_lock_init(&chip->reg_lock);
    370
    371	rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm);
    372	if (rc < 0)
    373		goto error_chip;
    374
    375	pcm->info_flags = 0;
    376	pcm->private_data = chip;
    377	strscpy(pcm->name, "Trident TM5600/60x0", sizeof(pcm->name));
    378
    379	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
    380	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
    381
    382	INIT_WORK(&dev->wq_trigger, audio_trigger);
    383	rc = snd_card_register(card);
    384	if (rc < 0)
    385		goto error_chip;
    386
    387	dprintk(1, "Registered audio driver for %s\n", card->longname);
    388
    389	return 0;
    390
    391error_chip:
    392	kfree(chip);
    393	dev->adev = NULL;
    394error:
    395	snd_card_free(card);
    396	return rc;
    397}
    398
    399static int tm6000_audio_fini(struct tm6000_core *dev)
    400{
    401	struct snd_tm6000_card *chip;
    402
    403	if (!dev)
    404		return 0;
    405	chip = dev->adev;
    406
    407	if (!chip)
    408		return 0;
    409
    410	if (!chip->card)
    411		return 0;
    412
    413	snd_card_free(chip->card);
    414	chip->card = NULL;
    415	kfree(chip);
    416	dev->adev = NULL;
    417
    418	return 0;
    419}
    420
    421static struct tm6000_ops audio_ops = {
    422	.type	= TM6000_AUDIO,
    423	.name	= "TM6000 Audio Extension",
    424	.init	= tm6000_audio_init,
    425	.fini	= tm6000_audio_fini,
    426	.fillbuf = tm6000_fillbuf,
    427};
    428
    429static int __init tm6000_alsa_register(void)
    430{
    431	return tm6000_register_extension(&audio_ops);
    432}
    433
    434static void __exit tm6000_alsa_unregister(void)
    435{
    436	tm6000_unregister_extension(&audio_ops);
    437}
    438
    439module_init(tm6000_alsa_register);
    440module_exit(tm6000_alsa_unregister);