xref: /kernel/linux/linux-5.10/sound/usb/hiface/pcm.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Linux driver for M2Tech hiFace compatible devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors:  Michael Trimarchi <michael@amarulasolutions.com>
88c2ecf20Sopenharmony_ci *           Antonio Ospite <ao2@amarulasolutions.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * The driver is based on the work done in TerraTec DMX 6Fire USB
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "pcm.h"
178c2ecf20Sopenharmony_ci#include "chip.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define OUT_EP          0x2
208c2ecf20Sopenharmony_ci#define PCM_N_URBS      8
218c2ecf20Sopenharmony_ci#define PCM_PACKET_SIZE 4096
228c2ecf20Sopenharmony_ci#define PCM_BUFFER_SIZE (2 * PCM_N_URBS * PCM_PACKET_SIZE)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct pcm_urb {
258c2ecf20Sopenharmony_ci	struct hiface_chip *chip;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	struct urb instance;
288c2ecf20Sopenharmony_ci	struct usb_anchor submitted;
298c2ecf20Sopenharmony_ci	u8 *buffer;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct pcm_substream {
338c2ecf20Sopenharmony_ci	spinlock_t lock;
348c2ecf20Sopenharmony_ci	struct snd_pcm_substream *instance;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	bool active;
378c2ecf20Sopenharmony_ci	snd_pcm_uframes_t dma_off;    /* current position in alsa dma_area */
388c2ecf20Sopenharmony_ci	snd_pcm_uframes_t period_off; /* current position in current period */
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cienum { /* pcm streaming states */
428c2ecf20Sopenharmony_ci	STREAM_DISABLED, /* no pcm streaming */
438c2ecf20Sopenharmony_ci	STREAM_STARTING, /* pcm streaming requested, waiting to become ready */
448c2ecf20Sopenharmony_ci	STREAM_RUNNING,  /* pcm streaming running */
458c2ecf20Sopenharmony_ci	STREAM_STOPPING
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct pcm_runtime {
498c2ecf20Sopenharmony_ci	struct hiface_chip *chip;
508c2ecf20Sopenharmony_ci	struct snd_pcm *instance;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	struct pcm_substream playback;
538c2ecf20Sopenharmony_ci	bool panic; /* if set driver won't do anymore pcm on device */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	struct pcm_urb out_urbs[PCM_N_URBS];
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	struct mutex stream_mutex;
588c2ecf20Sopenharmony_ci	u8 stream_state; /* one of STREAM_XXX */
598c2ecf20Sopenharmony_ci	u8 extra_freq;
608c2ecf20Sopenharmony_ci	wait_queue_head_t stream_wait_queue;
618c2ecf20Sopenharmony_ci	bool stream_wait_cond;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic const unsigned int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000,
658c2ecf20Sopenharmony_ci				      352800, 384000 };
668c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list constraints_extra_rates = {
678c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(rates),
688c2ecf20Sopenharmony_ci	.list = rates,
698c2ecf20Sopenharmony_ci	.mask = 0,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware pcm_hw = {
738c2ecf20Sopenharmony_ci	.info = SNDRV_PCM_INFO_MMAP |
748c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED |
758c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_BLOCK_TRANSFER |
768c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_PAUSE |
778c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID |
788c2ecf20Sopenharmony_ci		SNDRV_PCM_INFO_BATCH,
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S32_LE,
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	.rates = SNDRV_PCM_RATE_44100 |
838c2ecf20Sopenharmony_ci		SNDRV_PCM_RATE_48000 |
848c2ecf20Sopenharmony_ci		SNDRV_PCM_RATE_88200 |
858c2ecf20Sopenharmony_ci		SNDRV_PCM_RATE_96000 |
868c2ecf20Sopenharmony_ci		SNDRV_PCM_RATE_176400 |
878c2ecf20Sopenharmony_ci		SNDRV_PCM_RATE_192000,
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	.rate_min = 44100,
908c2ecf20Sopenharmony_ci	.rate_max = 192000, /* changes in hiface_pcm_open to support extra rates */
918c2ecf20Sopenharmony_ci	.channels_min = 2,
928c2ecf20Sopenharmony_ci	.channels_max = 2,
938c2ecf20Sopenharmony_ci	.buffer_bytes_max = PCM_BUFFER_SIZE,
948c2ecf20Sopenharmony_ci	.period_bytes_min = PCM_PACKET_SIZE,
958c2ecf20Sopenharmony_ci	.period_bytes_max = PCM_BUFFER_SIZE,
968c2ecf20Sopenharmony_ci	.periods_min = 2,
978c2ecf20Sopenharmony_ci	.periods_max = 1024
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* message values used to change the sample rate */
1018c2ecf20Sopenharmony_ci#define HIFACE_SET_RATE_REQUEST 0xb0
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define HIFACE_RATE_44100  0x43
1048c2ecf20Sopenharmony_ci#define HIFACE_RATE_48000  0x4b
1058c2ecf20Sopenharmony_ci#define HIFACE_RATE_88200  0x42
1068c2ecf20Sopenharmony_ci#define HIFACE_RATE_96000  0x4a
1078c2ecf20Sopenharmony_ci#define HIFACE_RATE_176400 0x40
1088c2ecf20Sopenharmony_ci#define HIFACE_RATE_192000 0x48
1098c2ecf20Sopenharmony_ci#define HIFACE_RATE_352800 0x58
1108c2ecf20Sopenharmony_ci#define HIFACE_RATE_384000 0x68
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int hiface_pcm_set_rate(struct pcm_runtime *rt, unsigned int rate)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct usb_device *device = rt->chip->dev;
1158c2ecf20Sopenharmony_ci	u16 rate_value;
1168c2ecf20Sopenharmony_ci	int ret;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* We are already sure that the rate is supported here thanks to
1198c2ecf20Sopenharmony_ci	 * ALSA constraints
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	switch (rate) {
1228c2ecf20Sopenharmony_ci	case 44100:
1238c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_44100;
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	case 48000:
1268c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_48000;
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	case 88200:
1298c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_88200;
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci	case 96000:
1328c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_96000;
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci	case 176400:
1358c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_176400;
1368c2ecf20Sopenharmony_ci		break;
1378c2ecf20Sopenharmony_ci	case 192000:
1388c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_192000;
1398c2ecf20Sopenharmony_ci		break;
1408c2ecf20Sopenharmony_ci	case 352800:
1418c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_352800;
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci	case 384000:
1448c2ecf20Sopenharmony_ci		rate_value = HIFACE_RATE_384000;
1458c2ecf20Sopenharmony_ci		break;
1468c2ecf20Sopenharmony_ci	default:
1478c2ecf20Sopenharmony_ci		dev_err(&device->dev, "Unsupported rate %d\n", rate);
1488c2ecf20Sopenharmony_ci		return -EINVAL;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/*
1528c2ecf20Sopenharmony_ci	 * USBIO: Vendor 0xb0(wValue=0x0043, wIndex=0x0000)
1538c2ecf20Sopenharmony_ci	 * 43 b0 43 00 00 00 00 00
1548c2ecf20Sopenharmony_ci	 * USBIO: Vendor 0xb0(wValue=0x004b, wIndex=0x0000)
1558c2ecf20Sopenharmony_ci	 * 43 b0 4b 00 00 00 00 00
1568c2ecf20Sopenharmony_ci	 * This control message doesn't have any ack from the
1578c2ecf20Sopenharmony_ci	 * other side
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	ret = usb_control_msg_send(device, 0,
1608c2ecf20Sopenharmony_ci				   HIFACE_SET_RATE_REQUEST,
1618c2ecf20Sopenharmony_ci				   USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
1628c2ecf20Sopenharmony_ci				   rate_value, 0, NULL, 0, 100, GFP_KERNEL);
1638c2ecf20Sopenharmony_ci	if (ret)
1648c2ecf20Sopenharmony_ci		dev_err(&device->dev, "Error setting samplerate %d.\n", rate);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return ret;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct pcm_substream *hiface_pcm_get_substream(struct snd_pcm_substream
1708c2ecf20Sopenharmony_ci						      *alsa_sub)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
1738c2ecf20Sopenharmony_ci	struct device *device = &rt->chip->dev->dev;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
1768c2ecf20Sopenharmony_ci		return &rt->playback;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	dev_err(device, "Error getting pcm substream slot.\n");
1798c2ecf20Sopenharmony_ci	return NULL;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* call with stream_mutex locked */
1838c2ecf20Sopenharmony_cistatic void hiface_pcm_stream_stop(struct pcm_runtime *rt)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int i, time;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (rt->stream_state != STREAM_DISABLED) {
1888c2ecf20Sopenharmony_ci		rt->stream_state = STREAM_STOPPING;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		for (i = 0; i < PCM_N_URBS; i++) {
1918c2ecf20Sopenharmony_ci			time = usb_wait_anchor_empty_timeout(
1928c2ecf20Sopenharmony_ci					&rt->out_urbs[i].submitted, 100);
1938c2ecf20Sopenharmony_ci			if (!time)
1948c2ecf20Sopenharmony_ci				usb_kill_anchored_urbs(
1958c2ecf20Sopenharmony_ci					&rt->out_urbs[i].submitted);
1968c2ecf20Sopenharmony_ci			usb_kill_urb(&rt->out_urbs[i].instance);
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		rt->stream_state = STREAM_DISABLED;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/* call with stream_mutex locked */
2048c2ecf20Sopenharmony_cistatic int hiface_pcm_stream_start(struct pcm_runtime *rt)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	int ret = 0;
2078c2ecf20Sopenharmony_ci	int i;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (rt->stream_state == STREAM_DISABLED) {
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		/* reset panic state when starting a new stream */
2128c2ecf20Sopenharmony_ci		rt->panic = false;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		/* submit our out urbs zero init */
2158c2ecf20Sopenharmony_ci		rt->stream_state = STREAM_STARTING;
2168c2ecf20Sopenharmony_ci		for (i = 0; i < PCM_N_URBS; i++) {
2178c2ecf20Sopenharmony_ci			memset(rt->out_urbs[i].buffer, 0, PCM_PACKET_SIZE);
2188c2ecf20Sopenharmony_ci			usb_anchor_urb(&rt->out_urbs[i].instance,
2198c2ecf20Sopenharmony_ci				       &rt->out_urbs[i].submitted);
2208c2ecf20Sopenharmony_ci			ret = usb_submit_urb(&rt->out_urbs[i].instance,
2218c2ecf20Sopenharmony_ci					     GFP_ATOMIC);
2228c2ecf20Sopenharmony_ci			if (ret) {
2238c2ecf20Sopenharmony_ci				hiface_pcm_stream_stop(rt);
2248c2ecf20Sopenharmony_ci				return ret;
2258c2ecf20Sopenharmony_ci			}
2268c2ecf20Sopenharmony_ci		}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		/* wait for first out urb to return (sent in in urb handler) */
2298c2ecf20Sopenharmony_ci		wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond,
2308c2ecf20Sopenharmony_ci				   HZ);
2318c2ecf20Sopenharmony_ci		if (rt->stream_wait_cond) {
2328c2ecf20Sopenharmony_ci			struct device *device = &rt->chip->dev->dev;
2338c2ecf20Sopenharmony_ci			dev_dbg(device, "%s: Stream is running wakeup event\n",
2348c2ecf20Sopenharmony_ci				 __func__);
2358c2ecf20Sopenharmony_ci			rt->stream_state = STREAM_RUNNING;
2368c2ecf20Sopenharmony_ci		} else {
2378c2ecf20Sopenharmony_ci			hiface_pcm_stream_stop(rt);
2388c2ecf20Sopenharmony_ci			return -EIO;
2398c2ecf20Sopenharmony_ci		}
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci	return ret;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci/* The hardware wants word-swapped 32-bit values */
2458c2ecf20Sopenharmony_cistatic void memcpy_swahw32(u8 *dest, u8 *src, unsigned int n)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	unsigned int i;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	for (i = 0; i < n / 4; i++)
2508c2ecf20Sopenharmony_ci		((u32 *)dest)[i] = swahw32(((u32 *)src)[i]);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/* call with substream locked */
2548c2ecf20Sopenharmony_ci/* returns true if a period elapsed */
2558c2ecf20Sopenharmony_cistatic bool hiface_pcm_playback(struct pcm_substream *sub, struct pcm_urb *urb)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
2588c2ecf20Sopenharmony_ci	struct device *device = &urb->chip->dev->dev;
2598c2ecf20Sopenharmony_ci	u8 *source;
2608c2ecf20Sopenharmony_ci	unsigned int pcm_buffer_size;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	WARN_ON(alsa_rt->format != SNDRV_PCM_FORMAT_S32_LE);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	pcm_buffer_size = snd_pcm_lib_buffer_bytes(sub->instance);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (sub->dma_off + PCM_PACKET_SIZE <= pcm_buffer_size) {
2678c2ecf20Sopenharmony_ci		dev_dbg(device, "%s: (1) buffer_size %#x dma_offset %#x\n", __func__,
2688c2ecf20Sopenharmony_ci			 (unsigned int) pcm_buffer_size,
2698c2ecf20Sopenharmony_ci			 (unsigned int) sub->dma_off);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		source = alsa_rt->dma_area + sub->dma_off;
2728c2ecf20Sopenharmony_ci		memcpy_swahw32(urb->buffer, source, PCM_PACKET_SIZE);
2738c2ecf20Sopenharmony_ci	} else {
2748c2ecf20Sopenharmony_ci		/* wrap around at end of ring buffer */
2758c2ecf20Sopenharmony_ci		unsigned int len;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		dev_dbg(device, "%s: (2) buffer_size %#x dma_offset %#x\n", __func__,
2788c2ecf20Sopenharmony_ci			 (unsigned int) pcm_buffer_size,
2798c2ecf20Sopenharmony_ci			 (unsigned int) sub->dma_off);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci		len = pcm_buffer_size - sub->dma_off;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		source = alsa_rt->dma_area + sub->dma_off;
2848c2ecf20Sopenharmony_ci		memcpy_swahw32(urb->buffer, source, len);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		source = alsa_rt->dma_area;
2878c2ecf20Sopenharmony_ci		memcpy_swahw32(urb->buffer + len, source,
2888c2ecf20Sopenharmony_ci			       PCM_PACKET_SIZE - len);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	sub->dma_off += PCM_PACKET_SIZE;
2918c2ecf20Sopenharmony_ci	if (sub->dma_off >= pcm_buffer_size)
2928c2ecf20Sopenharmony_ci		sub->dma_off -= pcm_buffer_size;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	sub->period_off += PCM_PACKET_SIZE;
2958c2ecf20Sopenharmony_ci	if (sub->period_off >= alsa_rt->period_size) {
2968c2ecf20Sopenharmony_ci		sub->period_off %= alsa_rt->period_size;
2978c2ecf20Sopenharmony_ci		return true;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	return false;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void hiface_pcm_out_urb_handler(struct urb *usb_urb)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct pcm_urb *out_urb = usb_urb->context;
3058c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = out_urb->chip->pcm;
3068c2ecf20Sopenharmony_ci	struct pcm_substream *sub;
3078c2ecf20Sopenharmony_ci	bool do_period_elapsed = false;
3088c2ecf20Sopenharmony_ci	unsigned long flags;
3098c2ecf20Sopenharmony_ci	int ret;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (rt->panic || rt->stream_state == STREAM_STOPPING)
3128c2ecf20Sopenharmony_ci		return;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (unlikely(usb_urb->status == -ENOENT ||	/* unlinked */
3158c2ecf20Sopenharmony_ci		     usb_urb->status == -ENODEV ||	/* device removed */
3168c2ecf20Sopenharmony_ci		     usb_urb->status == -ECONNRESET ||	/* unlinked */
3178c2ecf20Sopenharmony_ci		     usb_urb->status == -ESHUTDOWN)) {	/* device disabled */
3188c2ecf20Sopenharmony_ci		goto out_fail;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (rt->stream_state == STREAM_STARTING) {
3228c2ecf20Sopenharmony_ci		rt->stream_wait_cond = true;
3238c2ecf20Sopenharmony_ci		wake_up(&rt->stream_wait_queue);
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* now send our playback data (if a free out urb was found) */
3278c2ecf20Sopenharmony_ci	sub = &rt->playback;
3288c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
3298c2ecf20Sopenharmony_ci	if (sub->active)
3308c2ecf20Sopenharmony_ci		do_period_elapsed = hiface_pcm_playback(sub, out_urb);
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		memset(out_urb->buffer, 0, PCM_PACKET_SIZE);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (do_period_elapsed)
3378c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(sub->instance);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ret = usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
3408c2ecf20Sopenharmony_ci	if (ret < 0)
3418c2ecf20Sopenharmony_ci		goto out_fail;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ciout_fail:
3468c2ecf20Sopenharmony_ci	rt->panic = true;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int hiface_pcm_open(struct snd_pcm_substream *alsa_sub)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
3528c2ecf20Sopenharmony_ci	struct pcm_substream *sub = NULL;
3538c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
3548c2ecf20Sopenharmony_ci	int ret;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (rt->panic)
3578c2ecf20Sopenharmony_ci		return -EPIPE;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	mutex_lock(&rt->stream_mutex);
3608c2ecf20Sopenharmony_ci	alsa_rt->hw = pcm_hw;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
3638c2ecf20Sopenharmony_ci		sub = &rt->playback;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!sub) {
3668c2ecf20Sopenharmony_ci		struct device *device = &rt->chip->dev->dev;
3678c2ecf20Sopenharmony_ci		mutex_unlock(&rt->stream_mutex);
3688c2ecf20Sopenharmony_ci		dev_err(device, "Invalid stream type\n");
3698c2ecf20Sopenharmony_ci		return -EINVAL;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (rt->extra_freq) {
3738c2ecf20Sopenharmony_ci		alsa_rt->hw.rates |= SNDRV_PCM_RATE_KNOT;
3748c2ecf20Sopenharmony_ci		alsa_rt->hw.rate_max = 384000;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		/* explicit constraints needed as we added SNDRV_PCM_RATE_KNOT */
3778c2ecf20Sopenharmony_ci		ret = snd_pcm_hw_constraint_list(alsa_sub->runtime, 0,
3788c2ecf20Sopenharmony_ci						 SNDRV_PCM_HW_PARAM_RATE,
3798c2ecf20Sopenharmony_ci						 &constraints_extra_rates);
3808c2ecf20Sopenharmony_ci		if (ret < 0) {
3818c2ecf20Sopenharmony_ci			mutex_unlock(&rt->stream_mutex);
3828c2ecf20Sopenharmony_ci			return ret;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	sub->instance = alsa_sub;
3878c2ecf20Sopenharmony_ci	sub->active = false;
3888c2ecf20Sopenharmony_ci	mutex_unlock(&rt->stream_mutex);
3898c2ecf20Sopenharmony_ci	return 0;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int hiface_pcm_close(struct snd_pcm_substream *alsa_sub)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
3958c2ecf20Sopenharmony_ci	struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
3968c2ecf20Sopenharmony_ci	unsigned long flags;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (rt->panic)
3998c2ecf20Sopenharmony_ci		return 0;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	mutex_lock(&rt->stream_mutex);
4028c2ecf20Sopenharmony_ci	if (sub) {
4038c2ecf20Sopenharmony_ci		hiface_pcm_stream_stop(rt);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		/* deactivate substream */
4068c2ecf20Sopenharmony_ci		spin_lock_irqsave(&sub->lock, flags);
4078c2ecf20Sopenharmony_ci		sub->instance = NULL;
4088c2ecf20Sopenharmony_ci		sub->active = false;
4098c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&sub->lock, flags);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci	mutex_unlock(&rt->stream_mutex);
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int hiface_pcm_prepare(struct snd_pcm_substream *alsa_sub)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
4198c2ecf20Sopenharmony_ci	struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
4208c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
4218c2ecf20Sopenharmony_ci	int ret;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (rt->panic)
4248c2ecf20Sopenharmony_ci		return -EPIPE;
4258c2ecf20Sopenharmony_ci	if (!sub)
4268c2ecf20Sopenharmony_ci		return -ENODEV;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	mutex_lock(&rt->stream_mutex);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	hiface_pcm_stream_stop(rt);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	sub->dma_off = 0;
4338c2ecf20Sopenharmony_ci	sub->period_off = 0;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (rt->stream_state == STREAM_DISABLED) {
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		ret = hiface_pcm_set_rate(rt, alsa_rt->rate);
4388c2ecf20Sopenharmony_ci		if (ret) {
4398c2ecf20Sopenharmony_ci			mutex_unlock(&rt->stream_mutex);
4408c2ecf20Sopenharmony_ci			return ret;
4418c2ecf20Sopenharmony_ci		}
4428c2ecf20Sopenharmony_ci		ret = hiface_pcm_stream_start(rt);
4438c2ecf20Sopenharmony_ci		if (ret) {
4448c2ecf20Sopenharmony_ci			mutex_unlock(&rt->stream_mutex);
4458c2ecf20Sopenharmony_ci			return ret;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	mutex_unlock(&rt->stream_mutex);
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int hiface_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
4558c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (rt->panic)
4588c2ecf20Sopenharmony_ci		return -EPIPE;
4598c2ecf20Sopenharmony_ci	if (!sub)
4608c2ecf20Sopenharmony_ci		return -ENODEV;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	switch (cmd) {
4638c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4648c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
4658c2ecf20Sopenharmony_ci		spin_lock_irq(&sub->lock);
4668c2ecf20Sopenharmony_ci		sub->active = true;
4678c2ecf20Sopenharmony_ci		spin_unlock_irq(&sub->lock);
4688c2ecf20Sopenharmony_ci		return 0;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4718c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
4728c2ecf20Sopenharmony_ci		spin_lock_irq(&sub->lock);
4738c2ecf20Sopenharmony_ci		sub->active = false;
4748c2ecf20Sopenharmony_ci		spin_unlock_irq(&sub->lock);
4758c2ecf20Sopenharmony_ci		return 0;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	default:
4788c2ecf20Sopenharmony_ci		return -EINVAL;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t hiface_pcm_pointer(struct snd_pcm_substream *alsa_sub)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct pcm_substream *sub = hiface_pcm_get_substream(alsa_sub);
4858c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
4868c2ecf20Sopenharmony_ci	unsigned long flags;
4878c2ecf20Sopenharmony_ci	snd_pcm_uframes_t dma_offset;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (rt->panic || !sub)
4908c2ecf20Sopenharmony_ci		return SNDRV_PCM_POS_XRUN;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
4938c2ecf20Sopenharmony_ci	dma_offset = sub->dma_off;
4948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
4958c2ecf20Sopenharmony_ci	return bytes_to_frames(alsa_sub->runtime, dma_offset);
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops pcm_ops = {
4998c2ecf20Sopenharmony_ci	.open = hiface_pcm_open,
5008c2ecf20Sopenharmony_ci	.close = hiface_pcm_close,
5018c2ecf20Sopenharmony_ci	.prepare = hiface_pcm_prepare,
5028c2ecf20Sopenharmony_ci	.trigger = hiface_pcm_trigger,
5038c2ecf20Sopenharmony_ci	.pointer = hiface_pcm_pointer,
5048c2ecf20Sopenharmony_ci};
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int hiface_pcm_init_urb(struct pcm_urb *urb,
5078c2ecf20Sopenharmony_ci			       struct hiface_chip *chip,
5088c2ecf20Sopenharmony_ci			       unsigned int ep,
5098c2ecf20Sopenharmony_ci			       void (*handler)(struct urb *))
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	urb->chip = chip;
5128c2ecf20Sopenharmony_ci	usb_init_urb(&urb->instance);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	urb->buffer = kzalloc(PCM_PACKET_SIZE, GFP_KERNEL);
5158c2ecf20Sopenharmony_ci	if (!urb->buffer)
5168c2ecf20Sopenharmony_ci		return -ENOMEM;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(&urb->instance, chip->dev,
5198c2ecf20Sopenharmony_ci			  usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer,
5208c2ecf20Sopenharmony_ci			  PCM_PACKET_SIZE, handler, urb);
5218c2ecf20Sopenharmony_ci	if (usb_urb_ep_type_check(&urb->instance))
5228c2ecf20Sopenharmony_ci		return -EINVAL;
5238c2ecf20Sopenharmony_ci	init_usb_anchor(&urb->submitted);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_civoid hiface_pcm_abort(struct hiface_chip *chip)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = chip->pcm;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (rt) {
5338c2ecf20Sopenharmony_ci		rt->panic = true;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		mutex_lock(&rt->stream_mutex);
5368c2ecf20Sopenharmony_ci		hiface_pcm_stream_stop(rt);
5378c2ecf20Sopenharmony_ci		mutex_unlock(&rt->stream_mutex);
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic void hiface_pcm_destroy(struct hiface_chip *chip)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = chip->pcm;
5448c2ecf20Sopenharmony_ci	int i;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	for (i = 0; i < PCM_N_URBS; i++)
5478c2ecf20Sopenharmony_ci		kfree(rt->out_urbs[i].buffer);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	kfree(chip->pcm);
5508c2ecf20Sopenharmony_ci	chip->pcm = NULL;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic void hiface_pcm_free(struct snd_pcm *pcm)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct pcm_runtime *rt = pcm->private_data;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (rt)
5588c2ecf20Sopenharmony_ci		hiface_pcm_destroy(rt->chip);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ciint hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	int i;
5648c2ecf20Sopenharmony_ci	int ret;
5658c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
5668c2ecf20Sopenharmony_ci	struct pcm_runtime *rt;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	rt = kzalloc(sizeof(*rt), GFP_KERNEL);
5698c2ecf20Sopenharmony_ci	if (!rt)
5708c2ecf20Sopenharmony_ci		return -ENOMEM;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	rt->chip = chip;
5738c2ecf20Sopenharmony_ci	rt->stream_state = STREAM_DISABLED;
5748c2ecf20Sopenharmony_ci	if (extra_freq)
5758c2ecf20Sopenharmony_ci		rt->extra_freq = 1;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	init_waitqueue_head(&rt->stream_wait_queue);
5788c2ecf20Sopenharmony_ci	mutex_init(&rt->stream_mutex);
5798c2ecf20Sopenharmony_ci	spin_lock_init(&rt->playback.lock);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	for (i = 0; i < PCM_N_URBS; i++) {
5828c2ecf20Sopenharmony_ci		ret = hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP,
5838c2ecf20Sopenharmony_ci				    hiface_pcm_out_urb_handler);
5848c2ecf20Sopenharmony_ci		if (ret < 0)
5858c2ecf20Sopenharmony_ci			goto error;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm);
5898c2ecf20Sopenharmony_ci	if (ret < 0) {
5908c2ecf20Sopenharmony_ci		dev_err(&chip->dev->dev, "Cannot create pcm instance\n");
5918c2ecf20Sopenharmony_ci		goto error;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	pcm->private_data = rt;
5958c2ecf20Sopenharmony_ci	pcm->private_free = hiface_pcm_free;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	strlcpy(pcm->name, "USB-SPDIF Audio", sizeof(pcm->name));
5988c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
5998c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_VMALLOC,
6008c2ecf20Sopenharmony_ci				       NULL, 0, 0);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	rt->instance = pcm;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	chip->pcm = rt;
6058c2ecf20Sopenharmony_ci	return 0;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cierror:
6088c2ecf20Sopenharmony_ci	for (i = 0; i < PCM_N_URBS; i++)
6098c2ecf20Sopenharmony_ci		kfree(rt->out_urbs[i].buffer);
6108c2ecf20Sopenharmony_ci	kfree(rt);
6118c2ecf20Sopenharmony_ci	return ret;
6128c2ecf20Sopenharmony_ci}
613