18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Support for audio capture for tm5600/6000/6010
38c2ecf20Sopenharmony_ci// Copyright (c) 2007-2008 Mauro Carvalho Chehab <mchehab@kernel.org>
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Based on cx88-alsa.c
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/usb.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <sound/core.h>
168c2ecf20Sopenharmony_ci#include <sound/pcm.h>
178c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
188c2ecf20Sopenharmony_ci#include <sound/control.h>
198c2ecf20Sopenharmony_ci#include <sound/initval.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "tm6000.h"
238c2ecf20Sopenharmony_ci#include "tm6000-regs.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#undef dprintk
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define dprintk(level, fmt, arg...) do {				   \
288c2ecf20Sopenharmony_ci	if (debug >= level)						   \
298c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \
308c2ecf20Sopenharmony_ci	} while (0)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/****************************************************************************
338c2ecf20Sopenharmony_ci			Module global static vars
348c2ecf20Sopenharmony_ci ****************************************************************************/
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/****************************************************************************
488c2ecf20Sopenharmony_ci				Module macros
498c2ecf20Sopenharmony_ci ****************************************************************************/
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab");
538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
548c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Trident,tm5600},{{Trident,tm6000},{{Trident,tm6010}");
558c2ecf20Sopenharmony_cistatic unsigned int debug;
568c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "enable debug messages");
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/****************************************************************************
608c2ecf20Sopenharmony_ci			Module specific functions
618c2ecf20Sopenharmony_ci ****************************************************************************/
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * BOARD Specific: Sets audio DMA
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct tm6000_core *core = chip->core;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	dprintk(1, "Starting audio DMA\n");
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Enables audio */
748c2ecf20Sopenharmony_ci	tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	tm6000_set_audio_bitrate(core, 48000);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * BOARD Specific: Resets audio DMA
838c2ecf20Sopenharmony_ci */
848c2ecf20Sopenharmony_cistatic int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct tm6000_core *core = chip->core;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	dprintk(1, "Stopping audio DMA\n");
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Disables audio */
918c2ecf20Sopenharmony_ci	tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/****************************************************************************
978c2ecf20Sopenharmony_ci				ALSA PCM Interface
988c2ecf20Sopenharmony_ci ****************************************************************************/
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/*
1018c2ecf20Sopenharmony_ci * Digital hardware definition
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ci#define DEFAULT_FIFO_SIZE	4096
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_tm6000_digital_hw = {
1068c2ecf20Sopenharmony_ci	.info = SNDRV_PCM_INFO_BATCH |
1078c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP |
1088c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED |
1098c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_BLOCK_TRANSFER |
1108c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID,
1118c2ecf20Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
1148c2ecf20Sopenharmony_ci	.rate_min = 48000,
1158c2ecf20Sopenharmony_ci	.rate_max = 48000,
1168c2ecf20Sopenharmony_ci	.channels_min = 2,
1178c2ecf20Sopenharmony_ci	.channels_max = 2,
1188c2ecf20Sopenharmony_ci	.period_bytes_min = 64,
1198c2ecf20Sopenharmony_ci	.period_bytes_max = 12544,
1208c2ecf20Sopenharmony_ci	.periods_min = 2,
1218c2ecf20Sopenharmony_ci	.periods_max = 98,
1228c2ecf20Sopenharmony_ci	.buffer_bytes_max = 62720 * 8,
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * audio pcm capture open callback
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
1318c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
1328c2ecf20Sopenharmony_ci	int err;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_pow2(runtime, 0,
1358c2ecf20Sopenharmony_ci					 SNDRV_PCM_HW_PARAM_PERIODS);
1368c2ecf20Sopenharmony_ci	if (err < 0)
1378c2ecf20Sopenharmony_ci		goto _error;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	chip->substream = substream;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	runtime->hw = snd_tm6000_digital_hw;
1428c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci_error:
1468c2ecf20Sopenharmony_ci	dprintk(1, "Error opening PCM!\n");
1478c2ecf20Sopenharmony_ci	return err;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/*
1518c2ecf20Sopenharmony_ci * audio close callback
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_cistatic int snd_tm6000_close(struct snd_pcm_substream *substream)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
1568c2ecf20Sopenharmony_ci	struct tm6000_core *core = chip->core;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (atomic_read(&core->stream_started) > 0) {
1598c2ecf20Sopenharmony_ci		atomic_set(&core->stream_started, 0);
1608c2ecf20Sopenharmony_ci		schedule_work(&core->wq_trigger);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip = core->adev;
1698c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = chip->substream;
1708c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime;
1718c2ecf20Sopenharmony_ci	int period_elapsed = 0;
1728c2ecf20Sopenharmony_ci	unsigned int stride, buf_pos;
1738c2ecf20Sopenharmony_ci	int length;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (atomic_read(&core->stream_started) == 0)
1768c2ecf20Sopenharmony_ci		return 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (!size || !substream) {
1798c2ecf20Sopenharmony_ci		dprintk(1, "substream was NULL\n");
1808c2ecf20Sopenharmony_ci		return -EINVAL;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	runtime = substream->runtime;
1848c2ecf20Sopenharmony_ci	if (!runtime || !runtime->dma_area) {
1858c2ecf20Sopenharmony_ci		dprintk(1, "runtime was NULL\n");
1868c2ecf20Sopenharmony_ci		return -EINVAL;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	buf_pos = chip->buf_pos;
1908c2ecf20Sopenharmony_ci	stride = runtime->frame_bits >> 3;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (stride == 0) {
1938c2ecf20Sopenharmony_ci		dprintk(1, "stride is zero\n");
1948c2ecf20Sopenharmony_ci		return -EINVAL;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	length = size / stride;
1988c2ecf20Sopenharmony_ci	if (length == 0) {
1998c2ecf20Sopenharmony_ci		dprintk(1, "%s: length was zero\n", __func__);
2008c2ecf20Sopenharmony_ci		return -EINVAL;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size,
2048c2ecf20Sopenharmony_ci		runtime->dma_area, buf_pos,
2058c2ecf20Sopenharmony_ci		(unsigned int)runtime->buffer_size, stride);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (buf_pos + length >= runtime->buffer_size) {
2088c2ecf20Sopenharmony_ci		unsigned int cnt = runtime->buffer_size - buf_pos;
2098c2ecf20Sopenharmony_ci		memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride);
2108c2ecf20Sopenharmony_ci		memcpy(runtime->dma_area, buf + cnt * stride,
2118c2ecf20Sopenharmony_ci			length * stride - cnt * stride);
2128c2ecf20Sopenharmony_ci	} else
2138c2ecf20Sopenharmony_ci		memcpy(runtime->dma_area + buf_pos * stride, buf,
2148c2ecf20Sopenharmony_ci			length * stride);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	snd_pcm_stream_lock(substream);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	chip->buf_pos += length;
2198c2ecf20Sopenharmony_ci	if (chip->buf_pos >= runtime->buffer_size)
2208c2ecf20Sopenharmony_ci		chip->buf_pos -= runtime->buffer_size;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	chip->period_pos += length;
2238c2ecf20Sopenharmony_ci	if (chip->period_pos >= runtime->period_size) {
2248c2ecf20Sopenharmony_ci		chip->period_pos -= runtime->period_size;
2258c2ecf20Sopenharmony_ci		period_elapsed = 1;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	snd_pcm_stream_unlock(substream);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (period_elapsed)
2318c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/*
2378c2ecf20Sopenharmony_ci * prepare callback
2388c2ecf20Sopenharmony_ci */
2398c2ecf20Sopenharmony_cistatic int snd_tm6000_prepare(struct snd_pcm_substream *substream)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	chip->buf_pos = 0;
2448c2ecf20Sopenharmony_ci	chip->period_pos = 0;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/*
2518c2ecf20Sopenharmony_ci * trigger callback
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_cistatic void audio_trigger(struct work_struct *work)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct tm6000_core *core = container_of(work, struct tm6000_core,
2568c2ecf20Sopenharmony_ci						wq_trigger);
2578c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip = core->adev;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (atomic_read(&core->stream_started)) {
2608c2ecf20Sopenharmony_ci		dprintk(1, "starting capture");
2618c2ecf20Sopenharmony_ci		_tm6000_start_audio_dma(chip);
2628c2ecf20Sopenharmony_ci	} else {
2638c2ecf20Sopenharmony_ci		dprintk(1, "stopping capture");
2648c2ecf20Sopenharmony_ci		_tm6000_stop_audio_dma(chip);
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
2718c2ecf20Sopenharmony_ci	struct tm6000_core *core = chip->core;
2728c2ecf20Sopenharmony_ci	int err = 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	switch (cmd) {
2758c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2768c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
2778c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2788c2ecf20Sopenharmony_ci		atomic_set(&core->stream_started, 1);
2798c2ecf20Sopenharmony_ci		break;
2808c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2818c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
2828c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2838c2ecf20Sopenharmony_ci		atomic_set(&core->stream_started, 0);
2848c2ecf20Sopenharmony_ci		break;
2858c2ecf20Sopenharmony_ci	default:
2868c2ecf20Sopenharmony_ci		err = -EINVAL;
2878c2ecf20Sopenharmony_ci		break;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci	schedule_work(&core->wq_trigger);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return err;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci/*
2948c2ecf20Sopenharmony_ci * pointer callback
2958c2ecf20Sopenharmony_ci */
2968c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return chip->buf_pos;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * operators
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_tm6000_pcm_ops = {
3078c2ecf20Sopenharmony_ci	.open = snd_tm6000_pcm_open,
3088c2ecf20Sopenharmony_ci	.close = snd_tm6000_close,
3098c2ecf20Sopenharmony_ci	.prepare = snd_tm6000_prepare,
3108c2ecf20Sopenharmony_ci	.trigger = snd_tm6000_card_trigger,
3118c2ecf20Sopenharmony_ci	.pointer = snd_tm6000_pointer,
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/*
3158c2ecf20Sopenharmony_ci * create a PCM device
3168c2ecf20Sopenharmony_ci */
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/* FIXME: Control interface - How to control volume/mute? */
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/****************************************************************************
3218c2ecf20Sopenharmony_ci			Basic Flow for Sound Devices
3228c2ecf20Sopenharmony_ci ****************************************************************************/
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/*
3258c2ecf20Sopenharmony_ci * Alsa Constructor - Component probe
3268c2ecf20Sopenharmony_ci */
3278c2ecf20Sopenharmony_cistatic int tm6000_audio_init(struct tm6000_core *dev)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct snd_card		*card;
3308c2ecf20Sopenharmony_ci	struct snd_tm6000_card	*chip;
3318c2ecf20Sopenharmony_ci	int			rc;
3328c2ecf20Sopenharmony_ci	static int		devnr;
3338c2ecf20Sopenharmony_ci	char			component[14];
3348c2ecf20Sopenharmony_ci	struct snd_pcm		*pcm;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (!dev)
3378c2ecf20Sopenharmony_ci		return 0;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (devnr >= SNDRV_CARDS)
3408c2ecf20Sopenharmony_ci		return -ENODEV;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (!enable[devnr])
3438c2ecf20Sopenharmony_ci		return -ENOENT;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	rc = snd_card_new(&dev->udev->dev, index[devnr], "tm6000",
3468c2ecf20Sopenharmony_ci			  THIS_MODULE, 0, &card);
3478c2ecf20Sopenharmony_ci	if (rc < 0) {
3488c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "cannot create card instance %d\n", devnr);
3498c2ecf20Sopenharmony_ci		return rc;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci	strscpy(card->driver, "tm6000-alsa", sizeof(card->driver));
3528c2ecf20Sopenharmony_ci	strscpy(card->shortname, "TM5600/60x0", sizeof(card->shortname));
3538c2ecf20Sopenharmony_ci	sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d",
3548c2ecf20Sopenharmony_ci		dev->udev->bus->busnum, dev->udev->devnum);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	sprintf(component, "USB%04x:%04x",
3578c2ecf20Sopenharmony_ci		le16_to_cpu(dev->udev->descriptor.idVendor),
3588c2ecf20Sopenharmony_ci		le16_to_cpu(dev->udev->descriptor.idProduct));
3598c2ecf20Sopenharmony_ci	snd_component_add(card, component);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL);
3628c2ecf20Sopenharmony_ci	if (!chip) {
3638c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3648c2ecf20Sopenharmony_ci		goto error;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	chip->core = dev;
3688c2ecf20Sopenharmony_ci	chip->card = card;
3698c2ecf20Sopenharmony_ci	dev->adev = chip;
3708c2ecf20Sopenharmony_ci	spin_lock_init(&chip->reg_lock);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm);
3738c2ecf20Sopenharmony_ci	if (rc < 0)
3748c2ecf20Sopenharmony_ci		goto error_chip;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
3778c2ecf20Sopenharmony_ci	pcm->private_data = chip;
3788c2ecf20Sopenharmony_ci	strscpy(pcm->name, "Trident TM5600/60x0", sizeof(pcm->name));
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
3818c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC, NULL, 0, 0);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	INIT_WORK(&dev->wq_trigger, audio_trigger);
3848c2ecf20Sopenharmony_ci	rc = snd_card_register(card);
3858c2ecf20Sopenharmony_ci	if (rc < 0)
3868c2ecf20Sopenharmony_ci		goto error_chip;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	dprintk(1, "Registered audio driver for %s\n", card->longname);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cierror_chip:
3938c2ecf20Sopenharmony_ci	kfree(chip);
3948c2ecf20Sopenharmony_ci	dev->adev = NULL;
3958c2ecf20Sopenharmony_cierror:
3968c2ecf20Sopenharmony_ci	snd_card_free(card);
3978c2ecf20Sopenharmony_ci	return rc;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int tm6000_audio_fini(struct tm6000_core *dev)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct snd_tm6000_card *chip;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (!dev)
4058c2ecf20Sopenharmony_ci		return 0;
4068c2ecf20Sopenharmony_ci	chip = dev->adev;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (!chip)
4098c2ecf20Sopenharmony_ci		return 0;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (!chip->card)
4128c2ecf20Sopenharmony_ci		return 0;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	snd_card_free(chip->card);
4158c2ecf20Sopenharmony_ci	chip->card = NULL;
4168c2ecf20Sopenharmony_ci	kfree(chip);
4178c2ecf20Sopenharmony_ci	dev->adev = NULL;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return 0;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic struct tm6000_ops audio_ops = {
4238c2ecf20Sopenharmony_ci	.type	= TM6000_AUDIO,
4248c2ecf20Sopenharmony_ci	.name	= "TM6000 Audio Extension",
4258c2ecf20Sopenharmony_ci	.init	= tm6000_audio_init,
4268c2ecf20Sopenharmony_ci	.fini	= tm6000_audio_fini,
4278c2ecf20Sopenharmony_ci	.fillbuf = tm6000_fillbuf,
4288c2ecf20Sopenharmony_ci};
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int __init tm6000_alsa_register(void)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	return tm6000_register_extension(&audio_ops);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void __exit tm6000_alsa_unregister(void)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	tm6000_unregister_extension(&audio_ops);
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cimodule_init(tm6000_alsa_register);
4418c2ecf20Sopenharmony_cimodule_exit(tm6000_alsa_unregister);
442