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