18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Xen para-virtual sound device
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2016-2018 EPAM Systems Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <sound/core.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm.h>
158c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
188c2ecf20Sopenharmony_ci#include <xen/xen-front-pgdir-shbuf.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "xen_snd_front.h"
218c2ecf20Sopenharmony_ci#include "xen_snd_front_alsa.h"
228c2ecf20Sopenharmony_ci#include "xen_snd_front_cfg.h"
238c2ecf20Sopenharmony_ci#include "xen_snd_front_evtchnl.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct xen_snd_front_pcm_stream_info {
268c2ecf20Sopenharmony_ci	struct xen_snd_front_info *front_info;
278c2ecf20Sopenharmony_ci	struct xen_snd_front_evtchnl_pair *evt_pair;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/* This is the shared buffer with its backing storage. */
308c2ecf20Sopenharmony_ci	struct xen_front_pgdir_shbuf shbuf;
318c2ecf20Sopenharmony_ci	u8 *buffer;
328c2ecf20Sopenharmony_ci	size_t buffer_sz;
338c2ecf20Sopenharmony_ci	int num_pages;
348c2ecf20Sopenharmony_ci	struct page **pages;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	int index;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	bool is_open;
398c2ecf20Sopenharmony_ci	struct snd_pcm_hardware pcm_hw;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Number of processed frames as reported by the backend. */
428c2ecf20Sopenharmony_ci	snd_pcm_uframes_t be_cur_frame;
438c2ecf20Sopenharmony_ci	/* Current HW pointer to be reported via .period callback. */
448c2ecf20Sopenharmony_ci	atomic_t hw_ptr;
458c2ecf20Sopenharmony_ci	/* Modulo of the number of processed frames - for period detection. */
468c2ecf20Sopenharmony_ci	u32 out_frames;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct xen_snd_front_pcm_instance_info {
508c2ecf20Sopenharmony_ci	struct xen_snd_front_card_info *card_info;
518c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
528c2ecf20Sopenharmony_ci	struct snd_pcm_hardware pcm_hw;
538c2ecf20Sopenharmony_ci	int num_pcm_streams_pb;
548c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *streams_pb;
558c2ecf20Sopenharmony_ci	int num_pcm_streams_cap;
568c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *streams_cap;
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct xen_snd_front_card_info {
608c2ecf20Sopenharmony_ci	struct xen_snd_front_info *front_info;
618c2ecf20Sopenharmony_ci	struct snd_card *card;
628c2ecf20Sopenharmony_ci	struct snd_pcm_hardware pcm_hw;
638c2ecf20Sopenharmony_ci	int num_pcm_instances;
648c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_instance_info *pcm_instances;
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct alsa_sndif_sample_format {
688c2ecf20Sopenharmony_ci	u8 sndif;
698c2ecf20Sopenharmony_ci	snd_pcm_format_t alsa;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct alsa_sndif_hw_param {
738c2ecf20Sopenharmony_ci	u8 sndif;
748c2ecf20Sopenharmony_ci	snd_pcm_hw_param_t alsa;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic const struct alsa_sndif_sample_format ALSA_SNDIF_FORMATS[] = {
788c2ecf20Sopenharmony_ci	{
798c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_U8,
808c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_U8
818c2ecf20Sopenharmony_ci	},
828c2ecf20Sopenharmony_ci	{
838c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_S8,
848c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_S8
858c2ecf20Sopenharmony_ci	},
868c2ecf20Sopenharmony_ci	{
878c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_U16_LE,
888c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_U16_LE
898c2ecf20Sopenharmony_ci	},
908c2ecf20Sopenharmony_ci	{
918c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_U16_BE,
928c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_U16_BE
938c2ecf20Sopenharmony_ci	},
948c2ecf20Sopenharmony_ci	{
958c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_S16_LE,
968c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_S16_LE
978c2ecf20Sopenharmony_ci	},
988c2ecf20Sopenharmony_ci	{
998c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_S16_BE,
1008c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_S16_BE
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci	{
1038c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_U24_LE,
1048c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_U24_LE
1058c2ecf20Sopenharmony_ci	},
1068c2ecf20Sopenharmony_ci	{
1078c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_U24_BE,
1088c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_U24_BE
1098c2ecf20Sopenharmony_ci	},
1108c2ecf20Sopenharmony_ci	{
1118c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_S24_LE,
1128c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_S24_LE
1138c2ecf20Sopenharmony_ci	},
1148c2ecf20Sopenharmony_ci	{
1158c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_S24_BE,
1168c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_S24_BE
1178c2ecf20Sopenharmony_ci	},
1188c2ecf20Sopenharmony_ci	{
1198c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_U32_LE,
1208c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_U32_LE
1218c2ecf20Sopenharmony_ci	},
1228c2ecf20Sopenharmony_ci	{
1238c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_U32_BE,
1248c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_U32_BE
1258c2ecf20Sopenharmony_ci	},
1268c2ecf20Sopenharmony_ci	{
1278c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_S32_LE,
1288c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_S32_LE
1298c2ecf20Sopenharmony_ci	},
1308c2ecf20Sopenharmony_ci	{
1318c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_S32_BE,
1328c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_S32_BE
1338c2ecf20Sopenharmony_ci	},
1348c2ecf20Sopenharmony_ci	{
1358c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_A_LAW,
1368c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_A_LAW
1378c2ecf20Sopenharmony_ci	},
1388c2ecf20Sopenharmony_ci	{
1398c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_MU_LAW,
1408c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_MU_LAW
1418c2ecf20Sopenharmony_ci	},
1428c2ecf20Sopenharmony_ci	{
1438c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_F32_LE,
1448c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_FLOAT_LE
1458c2ecf20Sopenharmony_ci	},
1468c2ecf20Sopenharmony_ci	{
1478c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_F32_BE,
1488c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_FLOAT_BE
1498c2ecf20Sopenharmony_ci	},
1508c2ecf20Sopenharmony_ci	{
1518c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_F64_LE,
1528c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_FLOAT64_LE
1538c2ecf20Sopenharmony_ci	},
1548c2ecf20Sopenharmony_ci	{
1558c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_F64_BE,
1568c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_FLOAT64_BE
1578c2ecf20Sopenharmony_ci	},
1588c2ecf20Sopenharmony_ci	{
1598c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_LE,
1608c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE
1618c2ecf20Sopenharmony_ci	},
1628c2ecf20Sopenharmony_ci	{
1638c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_IEC958_SUBFRAME_BE,
1648c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE
1658c2ecf20Sopenharmony_ci	},
1668c2ecf20Sopenharmony_ci	{
1678c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_IMA_ADPCM,
1688c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_IMA_ADPCM
1698c2ecf20Sopenharmony_ci	},
1708c2ecf20Sopenharmony_ci	{
1718c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_MPEG,
1728c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_MPEG
1738c2ecf20Sopenharmony_ci	},
1748c2ecf20Sopenharmony_ci	{
1758c2ecf20Sopenharmony_ci		.sndif = XENSND_PCM_FORMAT_GSM,
1768c2ecf20Sopenharmony_ci		.alsa = SNDRV_PCM_FORMAT_GSM
1778c2ecf20Sopenharmony_ci	},
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int to_sndif_format(snd_pcm_format_t format)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	int i;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
1858c2ecf20Sopenharmony_ci		if (ALSA_SNDIF_FORMATS[i].alsa == format)
1868c2ecf20Sopenharmony_ci			return ALSA_SNDIF_FORMATS[i].sndif;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return -EINVAL;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic u64 to_sndif_formats_mask(u64 alsa_formats)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	u64 mask;
1948c2ecf20Sopenharmony_ci	int i;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	mask = 0;
1978c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
1988c2ecf20Sopenharmony_ci		if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)
1998c2ecf20Sopenharmony_ci			mask |= BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return mask;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic u64 to_alsa_formats_mask(u64 sndif_formats)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	u64 mask;
2078c2ecf20Sopenharmony_ci	int i;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	mask = 0;
2108c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
2118c2ecf20Sopenharmony_ci		if (BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif) & sndif_formats)
2128c2ecf20Sopenharmony_ci			mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return mask;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic void stream_clear(struct xen_snd_front_pcm_stream_info *stream)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	stream->is_open = false;
2208c2ecf20Sopenharmony_ci	stream->be_cur_frame = 0;
2218c2ecf20Sopenharmony_ci	stream->out_frames = 0;
2228c2ecf20Sopenharmony_ci	atomic_set(&stream->hw_ptr, 0);
2238c2ecf20Sopenharmony_ci	xen_snd_front_evtchnl_pair_clear(stream->evt_pair);
2248c2ecf20Sopenharmony_ci	memset(&stream->shbuf, 0, sizeof(stream->shbuf));
2258c2ecf20Sopenharmony_ci	stream->buffer = NULL;
2268c2ecf20Sopenharmony_ci	stream->buffer_sz = 0;
2278c2ecf20Sopenharmony_ci	stream->pages = NULL;
2288c2ecf20Sopenharmony_ci	stream->num_pages = 0;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void stream_free(struct xen_snd_front_pcm_stream_info *stream)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	xen_front_pgdir_shbuf_unmap(&stream->shbuf);
2348c2ecf20Sopenharmony_ci	xen_front_pgdir_shbuf_free(&stream->shbuf);
2358c2ecf20Sopenharmony_ci	if (stream->buffer)
2368c2ecf20Sopenharmony_ci		free_pages_exact(stream->buffer, stream->buffer_sz);
2378c2ecf20Sopenharmony_ci	kfree(stream->pages);
2388c2ecf20Sopenharmony_ci	stream_clear(stream);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic struct xen_snd_front_pcm_stream_info *
2428c2ecf20Sopenharmony_cistream_get(struct snd_pcm_substream *substream)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_instance_info *pcm_instance =
2458c2ecf20Sopenharmony_ci			snd_pcm_substream_chip(substream);
2468c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2498c2ecf20Sopenharmony_ci		stream = &pcm_instance->streams_pb[substream->number];
2508c2ecf20Sopenharmony_ci	else
2518c2ecf20Sopenharmony_ci		stream = &pcm_instance->streams_cap[substream->number];
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return stream;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int alsa_hw_rule(struct snd_pcm_hw_params *params,
2578c2ecf20Sopenharmony_ci			struct snd_pcm_hw_rule *rule)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = rule->private;
2608c2ecf20Sopenharmony_ci	struct device *dev = &stream->front_info->xb_dev->dev;
2618c2ecf20Sopenharmony_ci	struct snd_mask *formats =
2628c2ecf20Sopenharmony_ci			hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
2638c2ecf20Sopenharmony_ci	struct snd_interval *rates =
2648c2ecf20Sopenharmony_ci			hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
2658c2ecf20Sopenharmony_ci	struct snd_interval *channels =
2668c2ecf20Sopenharmony_ci			hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
2678c2ecf20Sopenharmony_ci	struct snd_interval *period =
2688c2ecf20Sopenharmony_ci			hw_param_interval(params,
2698c2ecf20Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
2708c2ecf20Sopenharmony_ci	struct snd_interval *buffer =
2718c2ecf20Sopenharmony_ci			hw_param_interval(params,
2728c2ecf20Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
2738c2ecf20Sopenharmony_ci	struct xensnd_query_hw_param req;
2748c2ecf20Sopenharmony_ci	struct xensnd_query_hw_param resp;
2758c2ecf20Sopenharmony_ci	struct snd_interval interval;
2768c2ecf20Sopenharmony_ci	struct snd_mask mask;
2778c2ecf20Sopenharmony_ci	u64 sndif_formats;
2788c2ecf20Sopenharmony_ci	int changed, ret;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* Collect all the values we need for the query. */
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	req.formats = to_sndif_formats_mask((u64)formats->bits[0] |
2838c2ecf20Sopenharmony_ci					    (u64)(formats->bits[1]) << 32);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	req.rates.min = rates->min;
2868c2ecf20Sopenharmony_ci	req.rates.max = rates->max;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	req.channels.min = channels->min;
2898c2ecf20Sopenharmony_ci	req.channels.max = channels->max;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	req.buffer.min = buffer->min;
2928c2ecf20Sopenharmony_ci	req.buffer.max = buffer->max;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	req.period.min = period->min;
2958c2ecf20Sopenharmony_ci	req.period.max = period->max;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	ret = xen_snd_front_stream_query_hw_param(&stream->evt_pair->req,
2988c2ecf20Sopenharmony_ci						  &req, &resp);
2998c2ecf20Sopenharmony_ci	if (ret < 0) {
3008c2ecf20Sopenharmony_ci		/* Check if this is due to backend communication error. */
3018c2ecf20Sopenharmony_ci		if (ret == -EIO || ret == -ETIMEDOUT)
3028c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to query ALSA HW parameters\n");
3038c2ecf20Sopenharmony_ci		return ret;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Refine HW parameters after the query. */
3078c2ecf20Sopenharmony_ci	changed  = 0;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	sndif_formats = to_alsa_formats_mask(resp.formats);
3108c2ecf20Sopenharmony_ci	snd_mask_none(&mask);
3118c2ecf20Sopenharmony_ci	mask.bits[0] = (u32)sndif_formats;
3128c2ecf20Sopenharmony_ci	mask.bits[1] = (u32)(sndif_formats >> 32);
3138c2ecf20Sopenharmony_ci	ret = snd_mask_refine(formats, &mask);
3148c2ecf20Sopenharmony_ci	if (ret < 0)
3158c2ecf20Sopenharmony_ci		return ret;
3168c2ecf20Sopenharmony_ci	changed |= ret;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	interval.openmin = 0;
3198c2ecf20Sopenharmony_ci	interval.openmax = 0;
3208c2ecf20Sopenharmony_ci	interval.integer = 1;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	interval.min = resp.rates.min;
3238c2ecf20Sopenharmony_ci	interval.max = resp.rates.max;
3248c2ecf20Sopenharmony_ci	ret = snd_interval_refine(rates, &interval);
3258c2ecf20Sopenharmony_ci	if (ret < 0)
3268c2ecf20Sopenharmony_ci		return ret;
3278c2ecf20Sopenharmony_ci	changed |= ret;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	interval.min = resp.channels.min;
3308c2ecf20Sopenharmony_ci	interval.max = resp.channels.max;
3318c2ecf20Sopenharmony_ci	ret = snd_interval_refine(channels, &interval);
3328c2ecf20Sopenharmony_ci	if (ret < 0)
3338c2ecf20Sopenharmony_ci		return ret;
3348c2ecf20Sopenharmony_ci	changed |= ret;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	interval.min = resp.buffer.min;
3378c2ecf20Sopenharmony_ci	interval.max = resp.buffer.max;
3388c2ecf20Sopenharmony_ci	ret = snd_interval_refine(buffer, &interval);
3398c2ecf20Sopenharmony_ci	if (ret < 0)
3408c2ecf20Sopenharmony_ci		return ret;
3418c2ecf20Sopenharmony_ci	changed |= ret;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	interval.min = resp.period.min;
3448c2ecf20Sopenharmony_ci	interval.max = resp.period.max;
3458c2ecf20Sopenharmony_ci	ret = snd_interval_refine(period, &interval);
3468c2ecf20Sopenharmony_ci	if (ret < 0)
3478c2ecf20Sopenharmony_ci		return ret;
3488c2ecf20Sopenharmony_ci	changed |= ret;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return changed;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int alsa_open(struct snd_pcm_substream *substream)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_instance_info *pcm_instance =
3568c2ecf20Sopenharmony_ci			snd_pcm_substream_chip(substream);
3578c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
3588c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3598c2ecf20Sopenharmony_ci	struct xen_snd_front_info *front_info =
3608c2ecf20Sopenharmony_ci			pcm_instance->card_info->front_info;
3618c2ecf20Sopenharmony_ci	struct device *dev = &front_info->xb_dev->dev;
3628c2ecf20Sopenharmony_ci	int ret;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * Return our HW properties: override defaults with those configured
3668c2ecf20Sopenharmony_ci	 * via XenStore.
3678c2ecf20Sopenharmony_ci	 */
3688c2ecf20Sopenharmony_ci	runtime->hw = stream->pcm_hw;
3698c2ecf20Sopenharmony_ci	runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
3708c2ecf20Sopenharmony_ci			      SNDRV_PCM_INFO_MMAP_VALID |
3718c2ecf20Sopenharmony_ci			      SNDRV_PCM_INFO_DOUBLE |
3728c2ecf20Sopenharmony_ci			      SNDRV_PCM_INFO_BATCH |
3738c2ecf20Sopenharmony_ci			      SNDRV_PCM_INFO_NONINTERLEAVED |
3748c2ecf20Sopenharmony_ci			      SNDRV_PCM_INFO_RESUME |
3758c2ecf20Sopenharmony_ci			      SNDRV_PCM_INFO_PAUSE);
3768c2ecf20Sopenharmony_ci	runtime->hw.info |= SNDRV_PCM_INFO_INTERLEAVED;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	stream->evt_pair = &front_info->evt_pairs[stream->index];
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	stream->front_info = front_info;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	stream->evt_pair->evt.u.evt.substream = substream;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	stream_clear(stream);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, true);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
3898c2ecf20Sopenharmony_ci				  alsa_hw_rule, stream,
3908c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_FORMAT, -1);
3918c2ecf20Sopenharmony_ci	if (ret) {
3928c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_FORMAT\n");
3938c2ecf20Sopenharmony_ci		return ret;
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
3978c2ecf20Sopenharmony_ci				  alsa_hw_rule, stream,
3988c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_RATE, -1);
3998c2ecf20Sopenharmony_ci	if (ret) {
4008c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_RATE\n");
4018c2ecf20Sopenharmony_ci		return ret;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
4058c2ecf20Sopenharmony_ci				  alsa_hw_rule, stream,
4068c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
4078c2ecf20Sopenharmony_ci	if (ret) {
4088c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_CHANNELS\n");
4098c2ecf20Sopenharmony_ci		return ret;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
4138c2ecf20Sopenharmony_ci				  alsa_hw_rule, stream,
4148c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
4158c2ecf20Sopenharmony_ci	if (ret) {
4168c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_PERIOD_SIZE\n");
4178c2ecf20Sopenharmony_ci		return ret;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
4218c2ecf20Sopenharmony_ci				  alsa_hw_rule, stream,
4228c2ecf20Sopenharmony_ci				  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
4238c2ecf20Sopenharmony_ci	if (ret) {
4248c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to add HW rule for SNDRV_PCM_HW_PARAM_BUFFER_SIZE\n");
4258c2ecf20Sopenharmony_ci		return ret;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic int alsa_close(struct snd_pcm_substream *substream)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	xen_snd_front_evtchnl_pair_set_connected(stream->evt_pair, false);
4368c2ecf20Sopenharmony_ci	return 0;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int shbuf_setup_backstore(struct xen_snd_front_pcm_stream_info *stream,
4408c2ecf20Sopenharmony_ci				 size_t buffer_sz)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	int i;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	stream->buffer = alloc_pages_exact(buffer_sz, GFP_KERNEL);
4458c2ecf20Sopenharmony_ci	if (!stream->buffer)
4468c2ecf20Sopenharmony_ci		return -ENOMEM;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	stream->buffer_sz = buffer_sz;
4498c2ecf20Sopenharmony_ci	stream->num_pages = DIV_ROUND_UP(stream->buffer_sz, PAGE_SIZE);
4508c2ecf20Sopenharmony_ci	stream->pages = kcalloc(stream->num_pages, sizeof(struct page *),
4518c2ecf20Sopenharmony_ci				GFP_KERNEL);
4528c2ecf20Sopenharmony_ci	if (!stream->pages)
4538c2ecf20Sopenharmony_ci		return -ENOMEM;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	for (i = 0; i < stream->num_pages; i++)
4568c2ecf20Sopenharmony_ci		stream->pages[i] = virt_to_page(stream->buffer + i * PAGE_SIZE);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	return 0;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int alsa_hw_params(struct snd_pcm_substream *substream,
4628c2ecf20Sopenharmony_ci			  struct snd_pcm_hw_params *params)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
4658c2ecf20Sopenharmony_ci	struct xen_snd_front_info *front_info = stream->front_info;
4668c2ecf20Sopenharmony_ci	struct xen_front_pgdir_shbuf_cfg buf_cfg;
4678c2ecf20Sopenharmony_ci	int ret;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/*
4708c2ecf20Sopenharmony_ci	 * This callback may be called multiple times,
4718c2ecf20Sopenharmony_ci	 * so free the previously allocated shared buffer if any.
4728c2ecf20Sopenharmony_ci	 */
4738c2ecf20Sopenharmony_ci	stream_free(stream);
4748c2ecf20Sopenharmony_ci	ret = shbuf_setup_backstore(stream, params_buffer_bytes(params));
4758c2ecf20Sopenharmony_ci	if (ret < 0)
4768c2ecf20Sopenharmony_ci		goto fail;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	memset(&buf_cfg, 0, sizeof(buf_cfg));
4798c2ecf20Sopenharmony_ci	buf_cfg.xb_dev = front_info->xb_dev;
4808c2ecf20Sopenharmony_ci	buf_cfg.pgdir = &stream->shbuf;
4818c2ecf20Sopenharmony_ci	buf_cfg.num_pages = stream->num_pages;
4828c2ecf20Sopenharmony_ci	buf_cfg.pages = stream->pages;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	ret = xen_front_pgdir_shbuf_alloc(&buf_cfg);
4858c2ecf20Sopenharmony_ci	if (ret < 0)
4868c2ecf20Sopenharmony_ci		goto fail;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	ret = xen_front_pgdir_shbuf_map(&stream->shbuf);
4898c2ecf20Sopenharmony_ci	if (ret < 0)
4908c2ecf20Sopenharmony_ci		goto fail;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	return 0;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cifail:
4958c2ecf20Sopenharmony_ci	stream_free(stream);
4968c2ecf20Sopenharmony_ci	dev_err(&front_info->xb_dev->dev,
4978c2ecf20Sopenharmony_ci		"Failed to allocate buffers for stream with index %d\n",
4988c2ecf20Sopenharmony_ci		stream->index);
4998c2ecf20Sopenharmony_ci	return ret;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic int alsa_hw_free(struct snd_pcm_substream *substream)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
5058c2ecf20Sopenharmony_ci	int ret;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	ret = xen_snd_front_stream_close(&stream->evt_pair->req);
5088c2ecf20Sopenharmony_ci	stream_free(stream);
5098c2ecf20Sopenharmony_ci	return ret;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_cistatic int alsa_prepare(struct snd_pcm_substream *substream)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (!stream->is_open) {
5178c2ecf20Sopenharmony_ci		struct snd_pcm_runtime *runtime = substream->runtime;
5188c2ecf20Sopenharmony_ci		u8 sndif_format;
5198c2ecf20Sopenharmony_ci		int ret;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci		ret = to_sndif_format(runtime->format);
5228c2ecf20Sopenharmony_ci		if (ret < 0) {
5238c2ecf20Sopenharmony_ci			dev_err(&stream->front_info->xb_dev->dev,
5248c2ecf20Sopenharmony_ci				"Unsupported sample format: %d\n",
5258c2ecf20Sopenharmony_ci				runtime->format);
5268c2ecf20Sopenharmony_ci			return ret;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci		sndif_format = ret;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		ret = xen_snd_front_stream_prepare(&stream->evt_pair->req,
5318c2ecf20Sopenharmony_ci						   &stream->shbuf,
5328c2ecf20Sopenharmony_ci						   sndif_format,
5338c2ecf20Sopenharmony_ci						   runtime->channels,
5348c2ecf20Sopenharmony_ci						   runtime->rate,
5358c2ecf20Sopenharmony_ci						   snd_pcm_lib_buffer_bytes(substream),
5368c2ecf20Sopenharmony_ci						   snd_pcm_lib_period_bytes(substream));
5378c2ecf20Sopenharmony_ci		if (ret < 0)
5388c2ecf20Sopenharmony_ci			return ret;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		stream->is_open = true;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cistatic int alsa_trigger(struct snd_pcm_substream *substream, int cmd)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
5498c2ecf20Sopenharmony_ci	int type;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	switch (cmd) {
5528c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
5538c2ecf20Sopenharmony_ci		type = XENSND_OP_TRIGGER_START;
5548c2ecf20Sopenharmony_ci		break;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
5578c2ecf20Sopenharmony_ci		type = XENSND_OP_TRIGGER_RESUME;
5588c2ecf20Sopenharmony_ci		break;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
5618c2ecf20Sopenharmony_ci		type = XENSND_OP_TRIGGER_STOP;
5628c2ecf20Sopenharmony_ci		break;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
5658c2ecf20Sopenharmony_ci		type = XENSND_OP_TRIGGER_PAUSE;
5668c2ecf20Sopenharmony_ci		break;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	default:
5698c2ecf20Sopenharmony_ci		return -EINVAL;
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	return xen_snd_front_stream_trigger(&stream->evt_pair->req, type);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_civoid xen_snd_front_alsa_handle_cur_pos(struct xen_snd_front_evtchnl *evtchnl,
5768c2ecf20Sopenharmony_ci				       u64 pos_bytes)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream = evtchnl->u.evt.substream;
5798c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
5808c2ecf20Sopenharmony_ci	snd_pcm_uframes_t delta, new_hw_ptr, cur_frame;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	cur_frame = bytes_to_frames(substream->runtime, pos_bytes);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	delta = cur_frame - stream->be_cur_frame;
5858c2ecf20Sopenharmony_ci	stream->be_cur_frame = cur_frame;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	new_hw_ptr = (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
5888c2ecf20Sopenharmony_ci	new_hw_ptr = (new_hw_ptr + delta) % substream->runtime->buffer_size;
5898c2ecf20Sopenharmony_ci	atomic_set(&stream->hw_ptr, (int)new_hw_ptr);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	stream->out_frames += delta;
5928c2ecf20Sopenharmony_ci	if (stream->out_frames > substream->runtime->period_size) {
5938c2ecf20Sopenharmony_ci		stream->out_frames %= substream->runtime->period_size;
5948c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(substream);
5958c2ecf20Sopenharmony_ci	}
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t alsa_pointer(struct snd_pcm_substream *substream)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return (snd_pcm_uframes_t)atomic_read(&stream->hw_ptr);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int alsa_pb_copy_user(struct snd_pcm_substream *substream,
6068c2ecf20Sopenharmony_ci			     int channel, unsigned long pos, void __user *src,
6078c2ecf20Sopenharmony_ci			     unsigned long count)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (unlikely(pos + count > stream->buffer_sz))
6128c2ecf20Sopenharmony_ci		return -EINVAL;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (copy_from_user(stream->buffer + pos, src, count))
6158c2ecf20Sopenharmony_ci		return -EFAULT;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int alsa_pb_copy_kernel(struct snd_pcm_substream *substream,
6218c2ecf20Sopenharmony_ci			       int channel, unsigned long pos, void *src,
6228c2ecf20Sopenharmony_ci			       unsigned long count)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (unlikely(pos + count > stream->buffer_sz))
6278c2ecf20Sopenharmony_ci		return -EINVAL;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	memcpy(stream->buffer + pos, src, count);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic int alsa_cap_copy_user(struct snd_pcm_substream *substream,
6358c2ecf20Sopenharmony_ci			      int channel, unsigned long pos, void __user *dst,
6368c2ecf20Sopenharmony_ci			      unsigned long count)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
6398c2ecf20Sopenharmony_ci	int ret;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (unlikely(pos + count > stream->buffer_sz))
6428c2ecf20Sopenharmony_ci		return -EINVAL;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
6458c2ecf20Sopenharmony_ci	if (ret < 0)
6468c2ecf20Sopenharmony_ci		return ret;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return copy_to_user(dst, stream->buffer + pos, count) ?
6498c2ecf20Sopenharmony_ci		-EFAULT : 0;
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int alsa_cap_copy_kernel(struct snd_pcm_substream *substream,
6538c2ecf20Sopenharmony_ci				int channel, unsigned long pos, void *dst,
6548c2ecf20Sopenharmony_ci				unsigned long count)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
6578c2ecf20Sopenharmony_ci	int ret;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	if (unlikely(pos + count > stream->buffer_sz))
6608c2ecf20Sopenharmony_ci		return -EINVAL;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	ret = xen_snd_front_stream_read(&stream->evt_pair->req, pos, count);
6638c2ecf20Sopenharmony_ci	if (ret < 0)
6648c2ecf20Sopenharmony_ci		return ret;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	memcpy(dst, stream->buffer + pos, count);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	return 0;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic int alsa_pb_fill_silence(struct snd_pcm_substream *substream,
6728c2ecf20Sopenharmony_ci				int channel, unsigned long pos,
6738c2ecf20Sopenharmony_ci				unsigned long count)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	struct xen_snd_front_pcm_stream_info *stream = stream_get(substream);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (unlikely(pos + count > stream->buffer_sz))
6788c2ecf20Sopenharmony_ci		return -EINVAL;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	memset(stream->buffer + pos, 0, count);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	return xen_snd_front_stream_write(&stream->evt_pair->req, pos, count);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/*
6868c2ecf20Sopenharmony_ci * FIXME: The mmaped data transfer is asynchronous and there is no
6878c2ecf20Sopenharmony_ci * ack signal from user-space when it is done. This is the
6888c2ecf20Sopenharmony_ci * reason it is not implemented in the PV driver as we do need
6898c2ecf20Sopenharmony_ci * to know when the buffer can be transferred to the backend.
6908c2ecf20Sopenharmony_ci */
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_drv_alsa_playback_ops = {
6938c2ecf20Sopenharmony_ci	.open		= alsa_open,
6948c2ecf20Sopenharmony_ci	.close		= alsa_close,
6958c2ecf20Sopenharmony_ci	.hw_params	= alsa_hw_params,
6968c2ecf20Sopenharmony_ci	.hw_free	= alsa_hw_free,
6978c2ecf20Sopenharmony_ci	.prepare	= alsa_prepare,
6988c2ecf20Sopenharmony_ci	.trigger	= alsa_trigger,
6998c2ecf20Sopenharmony_ci	.pointer	= alsa_pointer,
7008c2ecf20Sopenharmony_ci	.copy_user	= alsa_pb_copy_user,
7018c2ecf20Sopenharmony_ci	.copy_kernel	= alsa_pb_copy_kernel,
7028c2ecf20Sopenharmony_ci	.fill_silence	= alsa_pb_fill_silence,
7038c2ecf20Sopenharmony_ci};
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_drv_alsa_capture_ops = {
7068c2ecf20Sopenharmony_ci	.open		= alsa_open,
7078c2ecf20Sopenharmony_ci	.close		= alsa_close,
7088c2ecf20Sopenharmony_ci	.hw_params	= alsa_hw_params,
7098c2ecf20Sopenharmony_ci	.hw_free	= alsa_hw_free,
7108c2ecf20Sopenharmony_ci	.prepare	= alsa_prepare,
7118c2ecf20Sopenharmony_ci	.trigger	= alsa_trigger,
7128c2ecf20Sopenharmony_ci	.pointer	= alsa_pointer,
7138c2ecf20Sopenharmony_ci	.copy_user	= alsa_cap_copy_user,
7148c2ecf20Sopenharmony_ci	.copy_kernel	= alsa_cap_copy_kernel,
7158c2ecf20Sopenharmony_ci};
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic int new_pcm_instance(struct xen_snd_front_card_info *card_info,
7188c2ecf20Sopenharmony_ci			    struct xen_front_cfg_pcm_instance *instance_cfg,
7198c2ecf20Sopenharmony_ci			    struct xen_snd_front_pcm_instance_info *pcm_instance_info)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
7228c2ecf20Sopenharmony_ci	int ret, i;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	dev_dbg(&card_info->front_info->xb_dev->dev,
7258c2ecf20Sopenharmony_ci		"New PCM device \"%s\" with id %d playback %d capture %d",
7268c2ecf20Sopenharmony_ci		instance_cfg->name,
7278c2ecf20Sopenharmony_ci		instance_cfg->device_id,
7288c2ecf20Sopenharmony_ci		instance_cfg->num_streams_pb,
7298c2ecf20Sopenharmony_ci		instance_cfg->num_streams_cap);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	pcm_instance_info->card_info = card_info;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	pcm_instance_info->pcm_hw = instance_cfg->pcm_hw;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (instance_cfg->num_streams_pb) {
7368c2ecf20Sopenharmony_ci		pcm_instance_info->streams_pb =
7378c2ecf20Sopenharmony_ci				devm_kcalloc(&card_info->card->card_dev,
7388c2ecf20Sopenharmony_ci					     instance_cfg->num_streams_pb,
7398c2ecf20Sopenharmony_ci					     sizeof(struct xen_snd_front_pcm_stream_info),
7408c2ecf20Sopenharmony_ci					     GFP_KERNEL);
7418c2ecf20Sopenharmony_ci		if (!pcm_instance_info->streams_pb)
7428c2ecf20Sopenharmony_ci			return -ENOMEM;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	if (instance_cfg->num_streams_cap) {
7468c2ecf20Sopenharmony_ci		pcm_instance_info->streams_cap =
7478c2ecf20Sopenharmony_ci				devm_kcalloc(&card_info->card->card_dev,
7488c2ecf20Sopenharmony_ci					     instance_cfg->num_streams_cap,
7498c2ecf20Sopenharmony_ci					     sizeof(struct xen_snd_front_pcm_stream_info),
7508c2ecf20Sopenharmony_ci					     GFP_KERNEL);
7518c2ecf20Sopenharmony_ci		if (!pcm_instance_info->streams_cap)
7528c2ecf20Sopenharmony_ci			return -ENOMEM;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	pcm_instance_info->num_pcm_streams_pb =
7568c2ecf20Sopenharmony_ci			instance_cfg->num_streams_pb;
7578c2ecf20Sopenharmony_ci	pcm_instance_info->num_pcm_streams_cap =
7588c2ecf20Sopenharmony_ci			instance_cfg->num_streams_cap;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	for (i = 0; i < pcm_instance_info->num_pcm_streams_pb; i++) {
7618c2ecf20Sopenharmony_ci		pcm_instance_info->streams_pb[i].pcm_hw =
7628c2ecf20Sopenharmony_ci			instance_cfg->streams_pb[i].pcm_hw;
7638c2ecf20Sopenharmony_ci		pcm_instance_info->streams_pb[i].index =
7648c2ecf20Sopenharmony_ci			instance_cfg->streams_pb[i].index;
7658c2ecf20Sopenharmony_ci	}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	for (i = 0; i < pcm_instance_info->num_pcm_streams_cap; i++) {
7688c2ecf20Sopenharmony_ci		pcm_instance_info->streams_cap[i].pcm_hw =
7698c2ecf20Sopenharmony_ci			instance_cfg->streams_cap[i].pcm_hw;
7708c2ecf20Sopenharmony_ci		pcm_instance_info->streams_cap[i].index =
7718c2ecf20Sopenharmony_ci			instance_cfg->streams_cap[i].index;
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	ret = snd_pcm_new(card_info->card, instance_cfg->name,
7758c2ecf20Sopenharmony_ci			  instance_cfg->device_id,
7768c2ecf20Sopenharmony_ci			  instance_cfg->num_streams_pb,
7778c2ecf20Sopenharmony_ci			  instance_cfg->num_streams_cap,
7788c2ecf20Sopenharmony_ci			  &pcm);
7798c2ecf20Sopenharmony_ci	if (ret < 0)
7808c2ecf20Sopenharmony_ci		return ret;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	pcm->private_data = pcm_instance_info;
7838c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
7848c2ecf20Sopenharmony_ci	/* we want to handle all PCM operations in non-atomic context */
7858c2ecf20Sopenharmony_ci	pcm->nonatomic = true;
7868c2ecf20Sopenharmony_ci	strncpy(pcm->name, "Virtual card PCM", sizeof(pcm->name));
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (instance_cfg->num_streams_pb)
7898c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
7908c2ecf20Sopenharmony_ci				&snd_drv_alsa_playback_ops);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (instance_cfg->num_streams_cap)
7938c2ecf20Sopenharmony_ci		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
7948c2ecf20Sopenharmony_ci				&snd_drv_alsa_capture_ops);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	pcm_instance_info->pcm = pcm;
7978c2ecf20Sopenharmony_ci	return 0;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ciint xen_snd_front_alsa_init(struct xen_snd_front_info *front_info)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	struct device *dev = &front_info->xb_dev->dev;
8038c2ecf20Sopenharmony_ci	struct xen_front_cfg_card *cfg = &front_info->cfg;
8048c2ecf20Sopenharmony_ci	struct xen_snd_front_card_info *card_info;
8058c2ecf20Sopenharmony_ci	struct snd_card *card;
8068c2ecf20Sopenharmony_ci	int ret, i;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	dev_dbg(dev, "Creating virtual sound card\n");
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	ret = snd_card_new(dev, 0, XENSND_DRIVER_NAME, THIS_MODULE,
8118c2ecf20Sopenharmony_ci			   sizeof(struct xen_snd_front_card_info), &card);
8128c2ecf20Sopenharmony_ci	if (ret < 0)
8138c2ecf20Sopenharmony_ci		return ret;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	card_info = card->private_data;
8168c2ecf20Sopenharmony_ci	card_info->front_info = front_info;
8178c2ecf20Sopenharmony_ci	front_info->card_info = card_info;
8188c2ecf20Sopenharmony_ci	card_info->card = card;
8198c2ecf20Sopenharmony_ci	card_info->pcm_instances =
8208c2ecf20Sopenharmony_ci			devm_kcalloc(dev, cfg->num_pcm_instances,
8218c2ecf20Sopenharmony_ci				     sizeof(struct xen_snd_front_pcm_instance_info),
8228c2ecf20Sopenharmony_ci				     GFP_KERNEL);
8238c2ecf20Sopenharmony_ci	if (!card_info->pcm_instances) {
8248c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8258c2ecf20Sopenharmony_ci		goto fail;
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	card_info->num_pcm_instances = cfg->num_pcm_instances;
8298c2ecf20Sopenharmony_ci	card_info->pcm_hw = cfg->pcm_hw;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	for (i = 0; i < cfg->num_pcm_instances; i++) {
8328c2ecf20Sopenharmony_ci		ret = new_pcm_instance(card_info, &cfg->pcm_instances[i],
8338c2ecf20Sopenharmony_ci				       &card_info->pcm_instances[i]);
8348c2ecf20Sopenharmony_ci		if (ret < 0)
8358c2ecf20Sopenharmony_ci			goto fail;
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	strncpy(card->driver, XENSND_DRIVER_NAME, sizeof(card->driver));
8398c2ecf20Sopenharmony_ci	strncpy(card->shortname, cfg->name_short, sizeof(card->shortname));
8408c2ecf20Sopenharmony_ci	strncpy(card->longname, cfg->name_long, sizeof(card->longname));
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	ret = snd_card_register(card);
8438c2ecf20Sopenharmony_ci	if (ret < 0)
8448c2ecf20Sopenharmony_ci		goto fail;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	return 0;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cifail:
8498c2ecf20Sopenharmony_ci	snd_card_free(card);
8508c2ecf20Sopenharmony_ci	return ret;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_civoid xen_snd_front_alsa_fini(struct xen_snd_front_info *front_info)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci	struct xen_snd_front_card_info *card_info;
8568c2ecf20Sopenharmony_ci	struct snd_card *card;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	card_info = front_info->card_info;
8598c2ecf20Sopenharmony_ci	if (!card_info)
8608c2ecf20Sopenharmony_ci		return;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	card = card_info->card;
8638c2ecf20Sopenharmony_ci	if (!card)
8648c2ecf20Sopenharmony_ci		return;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	dev_dbg(&front_info->xb_dev->dev, "Removing virtual sound card %d\n",
8678c2ecf20Sopenharmony_ci		card->number);
8688c2ecf20Sopenharmony_ci	snd_card_free(card);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* Card_info will be freed when destroying front_info->xb_dev->dev. */
8718c2ecf20Sopenharmony_ci	card_info->card = NULL;
8728c2ecf20Sopenharmony_ci}
873