// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2006-2008 Daniel Mack, Karsten Wiese
*/
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include "device.h"
#include "audio.h"
#define N_URBS 32
#define CLOCK_DRIFT_TOLERANCE 5
#define FRAMES_PER_URB 8
#define BYTES_PER_FRAME 512
#define CHANNELS_PER_STREAM 2
#define BYTES_PER_SAMPLE 3
#define BYTES_PER_SAMPLE_USB 4
#define MAX_BUFFER_SIZE (128*1024)
#define MAX_ENDPOINT_SIZE 512
#define ENDPOINT_CAPTURE 2
#define ENDPOINT_PLAYBACK 6
#define MAKE_CHECKBYTE(cdev,stream,i) \
(stream << 1) | (~(i / (cdev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)
static const struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
.formats = SNDRV_PCM_FMTBIT_S24_3BE,
.rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000),
.rate_min = 44100,
.rate_max = 0, /* will overwrite later */
.channels_min = CHANNELS_PER_STREAM,
.channels_max = CHANNELS_PER_STREAM,
.buffer_bytes_max = MAX_BUFFER_SIZE,
.period_bytes_min = 128,
.period_bytes_max = MAX_BUFFER_SIZE,
.periods_min = 1,
.periods_max = 1024,
};
static void
activate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
guard(spinlock)(&cdev->spinlock);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
cdev->sub_playback[sub->number] = sub;
else
cdev->sub_capture[sub->number] = sub;
}
static void
deactivate_substream(struct snd_usb_caiaqdev *cdev,
struct snd_pcm_substream *sub)
{
guard(spinlock_irqsave)(&cdev->spinlock);
if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
cdev->sub_playback[sub->number] = NULL;
else
cdev->sub_capture[sub->number] = NULL;
}
static int
all_substreams_zero(struct snd_pcm_substream **subs)
{
int i;
for (i = 0; i < MAX_STREAMS; i++)
if (subs[i] != NULL)
return 0;
return 1;
}
static int stream_start(struct snd_usb_caiaqdev *cdev)
{
int i, ret;
struct device *dev = caiaqdev_to_dev(cdev);
dev_dbg(dev, "%s(%p)\n", __func__, cdev);
if (cdev->streaming)
return -EINVAL;
memset(cdev->sub_playback, 0, sizeof(cdev->sub_playback));
memset(cdev->sub_capture, 0, sizeof(cdev->sub_capture));
cdev->input_panic = 0;
cdev->output_panic = 0;
cdev->first_packet = 4;
cdev->streaming = 1;
cdev->warned = 0;
for (i = 0; i < N_URBS; i++) {
ret = usb_submit_urb(cdev->data_urbs_in[i], GFP_ATOMIC);
if (ret) {
dev_err(dev, "unable to trigger read #%d! (ret %d)\n",
i, ret);
cdev->streaming = 0;
return -EPIPE;
}
}
return 0;
}
static void stream_stop(struct snd_usb_caiaqdev *cdev)
{
int i;
struct device *dev = caiaqdev_to_dev(cdev);
dev_dbg(dev, "%s(%p)\n", __func__, cdev);
if (!cdev->streaming)
return;
cdev->streaming = 0;
for (i = 0; i < N_URBS; i++) {
usb_kill_urb(cdev->data_urbs_in[i]);
if (test_bit(i, &cdev->outurb_active_mask))
usb_kill_urb(cdev->data_urbs_out[i]);
}
cdev->outurb_active_mask = 0;
}
static int snd_usb_caiaq_substream_open(struct snd_pcm_substream *substream)
{
struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
struct device *dev = caiaqdev_to_dev(cdev);
dev_dbg(dev, "%s(%p)\n", __func__, substream);
substream->runtime->hw = cdev->pcm_info;
snd_pcm_limit_hw_rates(substream->runtime);
return 0;
}
static int snd_usb_caiaq_substream_close(struct snd_pcm_substream *substream)
{
struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(substream);
struct device *dev = caiaqdev_to_dev(cdev);
dev_dbg(dev, "%s(%p)\n", __func__, substream);
if (all_substreams_zero(cdev->sub_playback) &&
all_substreams_zero(cdev->sub_capture)) {
/* when the last client has stopped streaming,
* all sample rates are allowed again */
stream_stop(cdev);
cdev->pcm_info.rates = cdev->samplerates;
}
return 0;
}
static int snd_usb_caiaq_pcm_hw_free(struct snd_pcm_substream *sub)
{
struct snd_usb_caiaqdev *cdev = snd_pcm_substream_chip(sub);
deactivate_substream(