// SPDX-License-Identifier: GPL-2.0+
//
// Empiatech em28x1 audio extension
//
// Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
//
// Copyright (C) 2007-2016 Mauro Carvalho Chehab
// - Port to work with the in-kernel driver
// - Cleanups, fixes, alsa-controls, etc.
//
// This driver is based on my previous au600 usb pstn audio driver
// and inherits all the copyrights
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "em28xx.h"
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/init.h>
#include <linux/sound.h>
#include <linux/spinlock.h>
#include <linux/soundcard.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/ac97_codec.h>
#include <media/v4l2-common.h>
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
#define EM28XX_MAX_AUDIO_BUFS 5
#define EM28XX_MIN_AUDIO_PACKETS 64
#define dprintk(fmt, arg...) do { \
if (debug) \
dev_printk(KERN_DEBUG, &dev->intf->dev, \
"video: %s: " fmt, __func__, ## arg); \
} while (0)
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static int em28xx_deinit_isoc_audio(struct em28xx *dev)
{
int i;
dprintk("Stopping isoc\n");
for (i = 0; i < dev->adev.num_urb; i++) {
struct urb *urb = dev->adev.urb[i];
if (!irqs_disabled())
usb_kill_urb(urb);
else
usb_unlink_urb(urb);
}
return 0;
}
static void em28xx_audio_isocirq(struct urb *urb)
{
struct em28xx *dev = urb->context;
int i;
unsigned int oldptr;
int period_elapsed = 0;
int status;
unsigned char *cp;
unsigned int stride;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
if (dev->disconnected) {
dprintk("device disconnected while streaming. URB status=%d.\n",
urb->status);
atomic_set(&dev->adev.stream_started, 0);
return;
}
switch (urb->status) {
case 0: /* success */
case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
case -ESHUTDOWN:
return;
default: /* error */
dprintk("urb completion error %d.\n", urb->status);
break;
}
if (atomic_read(&dev->adev.stream_started) == 0)
return;
if (dev->adev.capture_pcm_substream) {
substream = dev->adev.capture_pcm_substream;
runtime = substream->runtime;
stride = runtime->frame_bits >> 3;
for (i = 0; i < urb->number_of_packets; i++) {
unsigned long flags;
int length =
urb->iso_frame_desc[i].actual_length / stride;
cp = (unsigned char *)urb->transfer_buffer +
urb->iso_frame_desc[i].offset;
if (!length)
continue;
oldptr = dev->adev.hwptr_done_capture;
if (oldptr + length >= runtime->buffer_size) {
unsigned int cnt =
runtime->buffer_size - oldptr;
memcpy(runtime->dma_area + oldptr * stride, cp,
cnt * stride);
memcpy(runtime->dma_area, cp + cnt * stride,
length * stride - cnt * stride);
} else {
memcpy(runtime->dma_area