xref: /third_party/alsa-lib/src/pcm/pcm_rate.c (revision d5ac70f0)
1d5ac70f0Sopenharmony_ci/**
2d5ac70f0Sopenharmony_ci * \file pcm/pcm_rate.c
3d5ac70f0Sopenharmony_ci * \ingroup PCM_Plugins
4d5ac70f0Sopenharmony_ci * \brief PCM Rate Plugin Interface
5d5ac70f0Sopenharmony_ci * \author Abramo Bagnara <abramo@alsa-project.org>
6d5ac70f0Sopenharmony_ci * \author Jaroslav Kysela <perex@perex.cz>
7d5ac70f0Sopenharmony_ci * \date 2000-2004
8d5ac70f0Sopenharmony_ci */
9d5ac70f0Sopenharmony_ci/*
10d5ac70f0Sopenharmony_ci *  PCM - Rate conversion
11d5ac70f0Sopenharmony_ci *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12d5ac70f0Sopenharmony_ci *                2004 by Jaroslav Kysela <perex@perex.cz>
13d5ac70f0Sopenharmony_ci *
14d5ac70f0Sopenharmony_ci *
15d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
16d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
17d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
18d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
19d5ac70f0Sopenharmony_ci *
20d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
21d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
22d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
24d5ac70f0Sopenharmony_ci *
25d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
26d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
27d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28d5ac70f0Sopenharmony_ci *
29d5ac70f0Sopenharmony_ci */
30d5ac70f0Sopenharmony_ci#include "pcm_local.h"
31d5ac70f0Sopenharmony_ci#include "pcm_plugin.h"
32d5ac70f0Sopenharmony_ci#include "pcm_rate.h"
33d5ac70f0Sopenharmony_ci#include "plugin_ops.h"
34d5ac70f0Sopenharmony_ci#include "bswap.h"
35d5ac70f0Sopenharmony_ci#include <inttypes.h>
36d5ac70f0Sopenharmony_ci
37d5ac70f0Sopenharmony_ci#if 0
38d5ac70f0Sopenharmony_ci#define DEBUG_REFINE
39d5ac70f0Sopenharmony_ci#endif
40d5ac70f0Sopenharmony_ci
41d5ac70f0Sopenharmony_ci#ifndef PIC
42d5ac70f0Sopenharmony_ci/* entry for static linking */
43d5ac70f0Sopenharmony_ciconst char *_snd_module_pcm_rate = "";
44d5ac70f0Sopenharmony_ci#endif
45d5ac70f0Sopenharmony_ci
46d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
47d5ac70f0Sopenharmony_ci
48d5ac70f0Sopenharmony_citypedef struct _snd_pcm_rate snd_pcm_rate_t;
49d5ac70f0Sopenharmony_ci
50d5ac70f0Sopenharmony_cistruct _snd_pcm_rate {
51d5ac70f0Sopenharmony_ci	snd_pcm_generic_t gen;
52d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t appl_ptr, hw_ptr, last_slave_hw_ptr;
53d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t last_commit_ptr;
54d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t orig_avail_min;
55d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_t sw_params;
56d5ac70f0Sopenharmony_ci	snd_pcm_format_t sformat;
57d5ac70f0Sopenharmony_ci	unsigned int srate;
58d5ac70f0Sopenharmony_ci	snd_pcm_channel_area_t *pareas;	/* areas for splitted period (rate pcm) */
59d5ac70f0Sopenharmony_ci	snd_pcm_channel_area_t *sareas;	/* areas for splitted period (slave pcm) */
60d5ac70f0Sopenharmony_ci	snd_pcm_rate_info_t info;
61d5ac70f0Sopenharmony_ci	void *open_func;
62d5ac70f0Sopenharmony_ci	void *obj;
63d5ac70f0Sopenharmony_ci	snd_pcm_rate_ops_t ops;
64d5ac70f0Sopenharmony_ci	unsigned int src_conv_idx;
65d5ac70f0Sopenharmony_ci	unsigned int dst_conv_idx;
66d5ac70f0Sopenharmony_ci	snd_pcm_channel_area_t *src_buf;
67d5ac70f0Sopenharmony_ci	snd_pcm_channel_area_t *dst_buf;
68d5ac70f0Sopenharmony_ci	int start_pending; /* start is triggered but not commited to slave */
69d5ac70f0Sopenharmony_ci	snd_htimestamp_t trigger_tstamp;
70d5ac70f0Sopenharmony_ci	unsigned int plugin_version;
71d5ac70f0Sopenharmony_ci	unsigned int rate_min, rate_max;
72d5ac70f0Sopenharmony_ci	snd_pcm_format_t orig_in_format;
73d5ac70f0Sopenharmony_ci	snd_pcm_format_t orig_out_format;
74d5ac70f0Sopenharmony_ci	uint64_t in_formats;
75d5ac70f0Sopenharmony_ci	uint64_t out_formats;
76d5ac70f0Sopenharmony_ci	unsigned int format_flags;
77d5ac70f0Sopenharmony_ci};
78d5ac70f0Sopenharmony_ci
79d5ac70f0Sopenharmony_ci#define SND_PCM_RATE_PLUGIN_VERSION_OLD	0x010001	/* old rate plugin */
80d5ac70f0Sopenharmony_ci#endif /* DOC_HIDDEN */
81d5ac70f0Sopenharmony_ci
82d5ac70f0Sopenharmony_ci/* allocate a channel area and a temporary buffer for the given size */
83d5ac70f0Sopenharmony_cistatic snd_pcm_channel_area_t *
84d5ac70f0Sopenharmony_cirate_alloc_tmp_buf(snd_pcm_format_t format,
85d5ac70f0Sopenharmony_ci		   unsigned int channels, unsigned int frames)
86d5ac70f0Sopenharmony_ci{
87d5ac70f0Sopenharmony_ci	snd_pcm_channel_area_t *ap;
88d5ac70f0Sopenharmony_ci	int width = snd_pcm_format_physical_width(format);
89d5ac70f0Sopenharmony_ci	unsigned int i;
90d5ac70f0Sopenharmony_ci
91d5ac70f0Sopenharmony_ci	ap = malloc(sizeof(*ap) * channels);
92d5ac70f0Sopenharmony_ci	if (!ap)
93d5ac70f0Sopenharmony_ci		return NULL;
94d5ac70f0Sopenharmony_ci	ap->addr = malloc(frames * channels * width / 8);
95d5ac70f0Sopenharmony_ci	if (!ap->addr) {
96d5ac70f0Sopenharmony_ci		free(ap);
97d5ac70f0Sopenharmony_ci		return NULL;
98d5ac70f0Sopenharmony_ci	}
99d5ac70f0Sopenharmony_ci
100d5ac70f0Sopenharmony_ci	/* set up in interleaved format */
101d5ac70f0Sopenharmony_ci	for (i = 0; i < channels; i++) {
102d5ac70f0Sopenharmony_ci		ap[i].addr = ap[0].addr + (i * width) / 8;
103d5ac70f0Sopenharmony_ci		ap[i].first = 0;
104d5ac70f0Sopenharmony_ci		ap[i].step = width * channels;
105d5ac70f0Sopenharmony_ci	}
106d5ac70f0Sopenharmony_ci
107d5ac70f0Sopenharmony_ci	return ap;
108d5ac70f0Sopenharmony_ci}
109d5ac70f0Sopenharmony_ci
110d5ac70f0Sopenharmony_cistatic void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr)
111d5ac70f0Sopenharmony_ci{
112d5ac70f0Sopenharmony_ci	snd_pcm_channel_area_t *c = *ptr;
113d5ac70f0Sopenharmony_ci
114d5ac70f0Sopenharmony_ci	if (c) {
115d5ac70f0Sopenharmony_ci		free(c->addr);
116d5ac70f0Sopenharmony_ci		free(c);
117d5ac70f0Sopenharmony_ci		*ptr = NULL;
118d5ac70f0Sopenharmony_ci	}
119d5ac70f0Sopenharmony_ci}
120d5ac70f0Sopenharmony_ci
121d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
122d5ac70f0Sopenharmony_ci{
123d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
124d5ac70f0Sopenharmony_ci	int err;
125d5ac70f0Sopenharmony_ci	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
126d5ac70f0Sopenharmony_ci	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
127d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
128d5ac70f0Sopenharmony_ci					 &access_mask);
129d5ac70f0Sopenharmony_ci	if (err < 0)
130d5ac70f0Sopenharmony_ci		return err;
131d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
132d5ac70f0Sopenharmony_ci					 &format_mask);
133d5ac70f0Sopenharmony_ci	if (err < 0)
134d5ac70f0Sopenharmony_ci		return err;
135d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
136d5ac70f0Sopenharmony_ci	if (err < 0)
137d5ac70f0Sopenharmony_ci		return err;
138d5ac70f0Sopenharmony_ci	if (rate->rate_min) {
139d5ac70f0Sopenharmony_ci		err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
140d5ac70f0Sopenharmony_ci						rate->rate_min, 0);
141d5ac70f0Sopenharmony_ci		if (err < 0)
142d5ac70f0Sopenharmony_ci			return err;
143d5ac70f0Sopenharmony_ci	}
144d5ac70f0Sopenharmony_ci	if (rate->rate_max) {
145d5ac70f0Sopenharmony_ci		err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
146d5ac70f0Sopenharmony_ci						rate->rate_max, 0);
147d5ac70f0Sopenharmony_ci		if (err < 0)
148d5ac70f0Sopenharmony_ci			return err;
149d5ac70f0Sopenharmony_ci	}
150d5ac70f0Sopenharmony_ci	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
151d5ac70f0Sopenharmony_ci	return 0;
152d5ac70f0Sopenharmony_ci}
153d5ac70f0Sopenharmony_ci
154d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
155d5ac70f0Sopenharmony_ci{
156d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
157d5ac70f0Sopenharmony_ci	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
158d5ac70f0Sopenharmony_ci	_snd_pcm_hw_params_any(sparams);
159d5ac70f0Sopenharmony_ci	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
160d5ac70f0Sopenharmony_ci				   &saccess_mask);
161d5ac70f0Sopenharmony_ci	if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
162d5ac70f0Sopenharmony_ci		_snd_pcm_hw_params_set_format(sparams, rate->sformat);
163d5ac70f0Sopenharmony_ci		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
164d5ac70f0Sopenharmony_ci	}
165d5ac70f0Sopenharmony_ci	_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
166d5ac70f0Sopenharmony_ci				     rate->srate, 0, rate->srate + 1, -1);
167d5ac70f0Sopenharmony_ci	return 0;
168d5ac70f0Sopenharmony_ci}
169d5ac70f0Sopenharmony_ci
170d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
171d5ac70f0Sopenharmony_ci					  snd_pcm_hw_params_t *sparams)
172d5ac70f0Sopenharmony_ci{
173d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
174d5ac70f0Sopenharmony_ci	snd_interval_t t, buffer_size;
175d5ac70f0Sopenharmony_ci	const snd_interval_t *srate, *crate;
176d5ac70f0Sopenharmony_ci	int err;
177d5ac70f0Sopenharmony_ci	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
178d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_TIME |
179d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_TICK_TIME);
180d5ac70f0Sopenharmony_ci	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
181d5ac70f0Sopenharmony_ci		links |= (SND_PCM_HW_PARBIT_FORMAT |
182d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SUBFORMAT |
183d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SAMPLE_BITS |
184d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_FRAME_BITS);
185d5ac70f0Sopenharmony_ci	snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
186d5ac70f0Sopenharmony_ci	snd_interval_unfloor(&buffer_size);
187d5ac70f0Sopenharmony_ci	crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
188d5ac70f0Sopenharmony_ci	srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
189d5ac70f0Sopenharmony_ci	snd_interval_muldiv(&buffer_size, srate, crate, &t);
190d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
191d5ac70f0Sopenharmony_ci	if (err < 0)
192d5ac70f0Sopenharmony_ci		return err;
193d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_params_refine(sparams, links, params);
194d5ac70f0Sopenharmony_ci	if (err < 0)
195d5ac70f0Sopenharmony_ci		return err;
196d5ac70f0Sopenharmony_ci	return 0;
197d5ac70f0Sopenharmony_ci}
198d5ac70f0Sopenharmony_ci
199d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
200d5ac70f0Sopenharmony_ci					  snd_pcm_hw_params_t *sparams)
201d5ac70f0Sopenharmony_ci{
202d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
203d5ac70f0Sopenharmony_ci	snd_interval_t t;
204d5ac70f0Sopenharmony_ci#ifdef DEBUG_REFINE
205d5ac70f0Sopenharmony_ci	snd_output_t *out;
206d5ac70f0Sopenharmony_ci#endif
207d5ac70f0Sopenharmony_ci	const snd_interval_t *sbuffer_size, *buffer_size;
208d5ac70f0Sopenharmony_ci	const snd_interval_t *srate, *crate;
209d5ac70f0Sopenharmony_ci	int err;
210d5ac70f0Sopenharmony_ci	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
211d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_PERIOD_TIME |
212d5ac70f0Sopenharmony_ci			      SND_PCM_HW_PARBIT_TICK_TIME);
213d5ac70f0Sopenharmony_ci	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
214d5ac70f0Sopenharmony_ci		links |= (SND_PCM_HW_PARBIT_FORMAT |
215d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SUBFORMAT |
216d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_SAMPLE_BITS |
217d5ac70f0Sopenharmony_ci			  SND_PCM_HW_PARBIT_FRAME_BITS);
218d5ac70f0Sopenharmony_ci	sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
219d5ac70f0Sopenharmony_ci	crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
220d5ac70f0Sopenharmony_ci	srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
221d5ac70f0Sopenharmony_ci	snd_interval_muldiv(sbuffer_size, crate, srate, &t);
222d5ac70f0Sopenharmony_ci	snd_interval_floor(&t);
223d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
224d5ac70f0Sopenharmony_ci	if (err < 0)
225d5ac70f0Sopenharmony_ci		return err;
226d5ac70f0Sopenharmony_ci	buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
227d5ac70f0Sopenharmony_ci	/*
228d5ac70f0Sopenharmony_ci	 * this condition probably needs more work:
229d5ac70f0Sopenharmony_ci	 *   in case when the buffer_size is known and we are looking
230d5ac70f0Sopenharmony_ci	 *   for best period_size, we should prefer situation when
231d5ac70f0Sopenharmony_ci	 *   (buffer_size / period_size) * period_size == buffer_size
232d5ac70f0Sopenharmony_ci	 */
233d5ac70f0Sopenharmony_ci	if (snd_interval_single(buffer_size) && buffer_size->integer) {
234d5ac70f0Sopenharmony_ci		snd_interval_t *period_size;
235d5ac70f0Sopenharmony_ci		period_size = (snd_interval_t *)snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE);
236d5ac70f0Sopenharmony_ci		if (!snd_interval_checkempty(period_size) &&
237d5ac70f0Sopenharmony_ci		    period_size->openmin && period_size->openmax &&
238d5ac70f0Sopenharmony_ci		    period_size->min + 1 == period_size->max) {
239d5ac70f0Sopenharmony_ci			if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
240d5ac70f0Sopenharmony_ci		    		snd_interval_set_value(period_size, period_size->min);
241d5ac70f0Sopenharmony_ci		    	} else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) {
242d5ac70f0Sopenharmony_ci		    		snd_interval_set_value(period_size, period_size->max);
243d5ac70f0Sopenharmony_ci		    	}
244d5ac70f0Sopenharmony_ci		}
245d5ac70f0Sopenharmony_ci	}
246d5ac70f0Sopenharmony_ci#ifdef DEBUG_REFINE
247d5ac70f0Sopenharmony_ci	snd_output_stdio_attach(&out, stderr, 0);
248d5ac70f0Sopenharmony_ci	snd_output_printf(out, "REFINE (params):\n");
249d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_dump(params, out);
250d5ac70f0Sopenharmony_ci	snd_output_printf(out, "REFINE (slave params):\n");
251d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_dump(sparams, out);
252d5ac70f0Sopenharmony_ci	snd_output_close(out);
253d5ac70f0Sopenharmony_ci#endif
254d5ac70f0Sopenharmony_ci	err = _snd_pcm_hw_params_refine(params, links, sparams);
255d5ac70f0Sopenharmony_ci#ifdef DEBUG_REFINE
256d5ac70f0Sopenharmony_ci	snd_output_stdio_attach(&out, stderr, 0);
257d5ac70f0Sopenharmony_ci	snd_output_printf(out, "********************\n");
258d5ac70f0Sopenharmony_ci	snd_output_printf(out, "REFINE (params) (%i):\n", err);
259d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_dump(params, out);
260d5ac70f0Sopenharmony_ci	snd_output_printf(out, "REFINE (slave params):\n");
261d5ac70f0Sopenharmony_ci	snd_pcm_hw_params_dump(sparams, out);
262d5ac70f0Sopenharmony_ci	snd_output_close(out);
263d5ac70f0Sopenharmony_ci#endif
264d5ac70f0Sopenharmony_ci	if (err < 0)
265d5ac70f0Sopenharmony_ci		return err;
266d5ac70f0Sopenharmony_ci	return 0;
267d5ac70f0Sopenharmony_ci}
268d5ac70f0Sopenharmony_ci
269d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
270d5ac70f0Sopenharmony_ci				  snd_pcm_hw_params_t *params)
271d5ac70f0Sopenharmony_ci{
272d5ac70f0Sopenharmony_ci	return snd_pcm_hw_refine_slave(pcm, params,
273d5ac70f0Sopenharmony_ci				       snd_pcm_rate_hw_refine_cprepare,
274d5ac70f0Sopenharmony_ci				       snd_pcm_rate_hw_refine_cchange,
275d5ac70f0Sopenharmony_ci				       snd_pcm_rate_hw_refine_sprepare,
276d5ac70f0Sopenharmony_ci				       snd_pcm_rate_hw_refine_schange,
277d5ac70f0Sopenharmony_ci				       snd_pcm_generic_hw_refine);
278d5ac70f0Sopenharmony_ci}
279d5ac70f0Sopenharmony_ci
280d5ac70f0Sopenharmony_ci/* evaluate the best matching available format to the given format */
281d5ac70f0Sopenharmony_cistatic int get_best_format(uint64_t mask, snd_pcm_format_t orig)
282d5ac70f0Sopenharmony_ci{
283d5ac70f0Sopenharmony_ci	int pwidth = snd_pcm_format_physical_width(orig);
284d5ac70f0Sopenharmony_ci	int width = snd_pcm_format_width(orig);
285d5ac70f0Sopenharmony_ci	int signd = snd_pcm_format_signed(orig);
286d5ac70f0Sopenharmony_ci	int best_score = -1;
287d5ac70f0Sopenharmony_ci	int match = -1;
288d5ac70f0Sopenharmony_ci	int f, score;
289d5ac70f0Sopenharmony_ci
290d5ac70f0Sopenharmony_ci	for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
291d5ac70f0Sopenharmony_ci		if (!(mask & (1ULL << f)))
292d5ac70f0Sopenharmony_ci			continue;
293d5ac70f0Sopenharmony_ci		score = 0;
294d5ac70f0Sopenharmony_ci		if (snd_pcm_format_linear(f)) {
295d5ac70f0Sopenharmony_ci			if (snd_pcm_format_physical_width(f) == pwidth)
296d5ac70f0Sopenharmony_ci				score++;
297d5ac70f0Sopenharmony_ci			if (snd_pcm_format_physical_width(f) >= pwidth)
298d5ac70f0Sopenharmony_ci				score++;
299d5ac70f0Sopenharmony_ci			if (snd_pcm_format_width(f) == width)
300d5ac70f0Sopenharmony_ci				score++;
301d5ac70f0Sopenharmony_ci			if (snd_pcm_format_signed(f) == signd)
302d5ac70f0Sopenharmony_ci				score++;
303d5ac70f0Sopenharmony_ci		}
304d5ac70f0Sopenharmony_ci		if (score > best_score) {
305d5ac70f0Sopenharmony_ci			match = f;
306d5ac70f0Sopenharmony_ci			best_score = score;
307d5ac70f0Sopenharmony_ci		}
308d5ac70f0Sopenharmony_ci	}
309d5ac70f0Sopenharmony_ci
310d5ac70f0Sopenharmony_ci	return match;
311d5ac70f0Sopenharmony_ci}
312d5ac70f0Sopenharmony_ci
313d5ac70f0Sopenharmony_ci/* set up the input and output formats from the available lists */
314d5ac70f0Sopenharmony_cistatic int choose_preferred_format(snd_pcm_rate_t *rate)
315d5ac70f0Sopenharmony_ci{
316d5ac70f0Sopenharmony_ci	uint64_t in_mask = rate->in_formats;
317d5ac70f0Sopenharmony_ci	uint64_t out_mask = rate->out_formats;
318d5ac70f0Sopenharmony_ci	int in, out;
319d5ac70f0Sopenharmony_ci
320d5ac70f0Sopenharmony_ci	if (!in_mask || !out_mask)
321d5ac70f0Sopenharmony_ci		return 0;
322d5ac70f0Sopenharmony_ci
323d5ac70f0Sopenharmony_ci	if (rate->orig_in_format == rate->orig_out_format)
324d5ac70f0Sopenharmony_ci		if (in_mask & out_mask & (1ULL << rate->orig_in_format))
325d5ac70f0Sopenharmony_ci			return 0; /* nothing changed */
326d5ac70f0Sopenharmony_ci
327d5ac70f0Sopenharmony_ci repeat:
328d5ac70f0Sopenharmony_ci	in = get_best_format(in_mask, rate->orig_in_format);
329d5ac70f0Sopenharmony_ci	out = get_best_format(out_mask, rate->orig_out_format);
330d5ac70f0Sopenharmony_ci	if (in < 0 || out < 0)
331d5ac70f0Sopenharmony_ci		return -ENOENT;
332d5ac70f0Sopenharmony_ci
333d5ac70f0Sopenharmony_ci	if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
334d5ac70f0Sopenharmony_ci	    in != out) {
335d5ac70f0Sopenharmony_ci		if (out_mask & (1ULL << in))
336d5ac70f0Sopenharmony_ci			out = in;
337d5ac70f0Sopenharmony_ci		else if (in_mask & (1ULL << out))
338d5ac70f0Sopenharmony_ci			in = out;
339d5ac70f0Sopenharmony_ci		else {
340d5ac70f0Sopenharmony_ci			in_mask &= ~(1ULL << in);
341d5ac70f0Sopenharmony_ci			out_mask &= ~(1ULL << out);
342d5ac70f0Sopenharmony_ci			goto repeat;
343d5ac70f0Sopenharmony_ci		}
344d5ac70f0Sopenharmony_ci	}
345d5ac70f0Sopenharmony_ci
346d5ac70f0Sopenharmony_ci	rate->info.in.format = in;
347d5ac70f0Sopenharmony_ci	rate->info.out.format = out;
348d5ac70f0Sopenharmony_ci	return 0;
349d5ac70f0Sopenharmony_ci}
350d5ac70f0Sopenharmony_ci
351d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
352d5ac70f0Sopenharmony_ci{
353d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
354d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = rate->gen.slave;
355d5ac70f0Sopenharmony_ci	snd_pcm_rate_side_info_t *sinfo, *cinfo;
356d5ac70f0Sopenharmony_ci	unsigned int channels, acc;
357d5ac70f0Sopenharmony_ci	int need_src_buf, need_dst_buf;
358d5ac70f0Sopenharmony_ci	int err = snd_pcm_hw_params_slave(pcm, params,
359d5ac70f0Sopenharmony_ci					  snd_pcm_rate_hw_refine_cchange,
360d5ac70f0Sopenharmony_ci					  snd_pcm_rate_hw_refine_sprepare,
361d5ac70f0Sopenharmony_ci					  snd_pcm_rate_hw_refine_schange,
362d5ac70f0Sopenharmony_ci					  snd_pcm_generic_hw_params);
363d5ac70f0Sopenharmony_ci	if (err < 0)
364d5ac70f0Sopenharmony_ci		return err;
365d5ac70f0Sopenharmony_ci
366d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
367d5ac70f0Sopenharmony_ci		cinfo = &rate->info.in;
368d5ac70f0Sopenharmony_ci		sinfo = &rate->info.out;
369d5ac70f0Sopenharmony_ci	} else {
370d5ac70f0Sopenharmony_ci		sinfo = &rate->info.in;
371d5ac70f0Sopenharmony_ci		cinfo = &rate->info.out;
372d5ac70f0Sopenharmony_ci	}
373d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format);
374d5ac70f0Sopenharmony_ci	if (err < 0)
375d5ac70f0Sopenharmony_ci		return err;
376d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0);
377d5ac70f0Sopenharmony_ci	if (err < 0)
378d5ac70f0Sopenharmony_ci		return err;
379d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
380d5ac70f0Sopenharmony_ci	if (err < 0)
381d5ac70f0Sopenharmony_ci		return err;
382d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size);
383d5ac70f0Sopenharmony_ci	if (err < 0)
384d5ac70f0Sopenharmony_ci		return err;
385d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
386d5ac70f0Sopenharmony_ci	if (err < 0)
387d5ac70f0Sopenharmony_ci		return err;
388d5ac70f0Sopenharmony_ci	err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
389d5ac70f0Sopenharmony_ci	if (err < 0)
390d5ac70f0Sopenharmony_ci		return err;
391d5ac70f0Sopenharmony_ci
392d5ac70f0Sopenharmony_ci	rate->info.channels = channels;
393d5ac70f0Sopenharmony_ci	sinfo->format = slave->format;
394d5ac70f0Sopenharmony_ci	sinfo->rate = slave->rate;
395d5ac70f0Sopenharmony_ci	sinfo->buffer_size = slave->buffer_size;
396d5ac70f0Sopenharmony_ci	sinfo->period_size = slave->period_size;
397d5ac70f0Sopenharmony_ci
398d5ac70f0Sopenharmony_ci	if (CHECK_SANITY(rate->pareas)) {
399d5ac70f0Sopenharmony_ci		SNDMSG("rate plugin already in use");
400d5ac70f0Sopenharmony_ci		return -EBUSY;
401d5ac70f0Sopenharmony_ci	}
402d5ac70f0Sopenharmony_ci
403d5ac70f0Sopenharmony_ci	rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels,
404d5ac70f0Sopenharmony_ci					  cinfo->period_size);
405d5ac70f0Sopenharmony_ci	rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels,
406d5ac70f0Sopenharmony_ci					  sinfo->period_size);
407d5ac70f0Sopenharmony_ci	if (!rate->pareas || !rate->sareas) {
408d5ac70f0Sopenharmony_ci		err = -ENOMEM;
409d5ac70f0Sopenharmony_ci		goto error_pareas;
410d5ac70f0Sopenharmony_ci	}
411d5ac70f0Sopenharmony_ci
412d5ac70f0Sopenharmony_ci	rate->orig_in_format = rate->info.in.format;
413d5ac70f0Sopenharmony_ci	rate->orig_out_format = rate->info.out.format;
414d5ac70f0Sopenharmony_ci	if (choose_preferred_format(rate) < 0) {
415d5ac70f0Sopenharmony_ci		SNDERR("No matching format in rate plugin");
416d5ac70f0Sopenharmony_ci		err = -EINVAL;
417d5ac70f0Sopenharmony_ci		goto error_pareas;
418d5ac70f0Sopenharmony_ci	}
419d5ac70f0Sopenharmony_ci
420d5ac70f0Sopenharmony_ci	err = rate->ops.init(rate->obj, &rate->info);
421d5ac70f0Sopenharmony_ci	if (err < 0)
422d5ac70f0Sopenharmony_ci		goto error_init;
423d5ac70f0Sopenharmony_ci
424d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->src_buf);
425d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->dst_buf);
426d5ac70f0Sopenharmony_ci
427d5ac70f0Sopenharmony_ci	need_src_buf = need_dst_buf = 0;
428d5ac70f0Sopenharmony_ci
429d5ac70f0Sopenharmony_ci	if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) &&
430d5ac70f0Sopenharmony_ci	    !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
431d5ac70f0Sopenharmony_ci	      acc == SND_PCM_ACCESS_RW_INTERLEAVED)) {
432d5ac70f0Sopenharmony_ci		need_src_buf = need_dst_buf = 1;
433d5ac70f0Sopenharmony_ci	} else {
434d5ac70f0Sopenharmony_ci		if (rate->orig_in_format != rate->info.in.format)
435d5ac70f0Sopenharmony_ci			need_src_buf = 1;
436d5ac70f0Sopenharmony_ci		if (rate->orig_out_format != rate->info.out.format)
437d5ac70f0Sopenharmony_ci			need_dst_buf = 1;
438d5ac70f0Sopenharmony_ci	}
439d5ac70f0Sopenharmony_ci
440d5ac70f0Sopenharmony_ci	if (need_src_buf) {
441d5ac70f0Sopenharmony_ci		rate->src_conv_idx =
442d5ac70f0Sopenharmony_ci			snd_pcm_linear_convert_index(rate->orig_in_format,
443d5ac70f0Sopenharmony_ci						     rate->info.in.format);
444d5ac70f0Sopenharmony_ci		rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format,
445d5ac70f0Sopenharmony_ci						   channels, rate->info.in.period_size);
446d5ac70f0Sopenharmony_ci		if (!rate->src_buf) {
447d5ac70f0Sopenharmony_ci			err = -ENOMEM;
448d5ac70f0Sopenharmony_ci			goto error;
449d5ac70f0Sopenharmony_ci		}
450d5ac70f0Sopenharmony_ci	}
451d5ac70f0Sopenharmony_ci
452d5ac70f0Sopenharmony_ci	if (need_dst_buf) {
453d5ac70f0Sopenharmony_ci		rate->dst_conv_idx =
454d5ac70f0Sopenharmony_ci			snd_pcm_linear_convert_index(rate->info.out.format,
455d5ac70f0Sopenharmony_ci						     rate->orig_out_format);
456d5ac70f0Sopenharmony_ci		rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format,
457d5ac70f0Sopenharmony_ci						   channels, rate->info.out.period_size);
458d5ac70f0Sopenharmony_ci		if (!rate->dst_buf) {
459d5ac70f0Sopenharmony_ci			err = -ENOMEM;
460d5ac70f0Sopenharmony_ci			goto error;
461d5ac70f0Sopenharmony_ci		}
462d5ac70f0Sopenharmony_ci	}
463d5ac70f0Sopenharmony_ci
464d5ac70f0Sopenharmony_ci	return 0;
465d5ac70f0Sopenharmony_ci
466d5ac70f0Sopenharmony_ci error:
467d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->src_buf);
468d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->dst_buf);
469d5ac70f0Sopenharmony_ci error_init:
470d5ac70f0Sopenharmony_ci	if (rate->ops.free)
471d5ac70f0Sopenharmony_ci		rate->ops.free(rate->obj);
472d5ac70f0Sopenharmony_ci error_pareas:
473d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->pareas);
474d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->sareas);
475d5ac70f0Sopenharmony_ci	return err;
476d5ac70f0Sopenharmony_ci}
477d5ac70f0Sopenharmony_ci
478d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
479d5ac70f0Sopenharmony_ci{
480d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
481d5ac70f0Sopenharmony_ci
482d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->pareas);
483d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->sareas);
484d5ac70f0Sopenharmony_ci	if (rate->ops.free)
485d5ac70f0Sopenharmony_ci		rate->ops.free(rate->obj);
486d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->src_buf);
487d5ac70f0Sopenharmony_ci	rate_free_tmp_buf(&rate->dst_buf);
488d5ac70f0Sopenharmony_ci	return snd_pcm_hw_free(rate->gen.slave);
489d5ac70f0Sopenharmony_ci}
490d5ac70f0Sopenharmony_ci
491d5ac70f0Sopenharmony_cistatic void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
492d5ac70f0Sopenharmony_ci{
493d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
494d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = rate->gen.slave;
495d5ac70f0Sopenharmony_ci	unsigned long div;
496d5ac70f0Sopenharmony_ci
497d5ac70f0Sopenharmony_ci	if (*val == pcm->buffer_size) {
498d5ac70f0Sopenharmony_ci		*val = slave->buffer_size;
499d5ac70f0Sopenharmony_ci	} else {
500d5ac70f0Sopenharmony_ci		div = *val / pcm->period_size;
501d5ac70f0Sopenharmony_ci		if (div * pcm->period_size == *val)
502d5ac70f0Sopenharmony_ci			*val = div * slave->period_size;
503d5ac70f0Sopenharmony_ci		else
504d5ac70f0Sopenharmony_ci			*val = muldiv_near(*val, slave->period_size, pcm->period_size);
505d5ac70f0Sopenharmony_ci	}
506d5ac70f0Sopenharmony_ci}
507d5ac70f0Sopenharmony_ci
508d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
509d5ac70f0Sopenharmony_ci{
510d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
511d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = rate->gen.slave;
512d5ac70f0Sopenharmony_ci	snd_pcm_sw_params_t *sparams;
513d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t boundary1, boundary2, sboundary;
514d5ac70f0Sopenharmony_ci	int err;
515d5ac70f0Sopenharmony_ci
516d5ac70f0Sopenharmony_ci	sparams = &rate->sw_params;
517d5ac70f0Sopenharmony_ci	err = snd_pcm_sw_params_current(slave, sparams);
518d5ac70f0Sopenharmony_ci	if (err < 0)
519d5ac70f0Sopenharmony_ci		return err;
520d5ac70f0Sopenharmony_ci	sboundary = sparams->boundary;
521d5ac70f0Sopenharmony_ci	*sparams = *params;
522d5ac70f0Sopenharmony_ci	boundary1 = pcm->buffer_size;
523d5ac70f0Sopenharmony_ci	boundary2 = slave->buffer_size;
524d5ac70f0Sopenharmony_ci	while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size &&
525d5ac70f0Sopenharmony_ci	       boundary2 * 2 <= LONG_MAX - slave->buffer_size) {
526d5ac70f0Sopenharmony_ci		boundary1 *= 2;
527d5ac70f0Sopenharmony_ci		boundary2 *= 2;
528d5ac70f0Sopenharmony_ci	}
529d5ac70f0Sopenharmony_ci	params->boundary = boundary1;
530d5ac70f0Sopenharmony_ci	sparams->boundary = sboundary;
531d5ac70f0Sopenharmony_ci
532d5ac70f0Sopenharmony_ci	if (rate->ops.adjust_pitch)
533d5ac70f0Sopenharmony_ci		rate->ops.adjust_pitch(rate->obj, &rate->info);
534d5ac70f0Sopenharmony_ci
535d5ac70f0Sopenharmony_ci	recalc(pcm, &sparams->avail_min);
536d5ac70f0Sopenharmony_ci	rate->orig_avail_min = sparams->avail_min;
537d5ac70f0Sopenharmony_ci	recalc(pcm, &sparams->start_threshold);
538d5ac70f0Sopenharmony_ci	if (sparams->avail_min < 1) sparams->avail_min = 1;
539d5ac70f0Sopenharmony_ci	if (sparams->start_threshold <= slave->buffer_size) {
540d5ac70f0Sopenharmony_ci		if (sparams->start_threshold > (slave->buffer_size / sparams->avail_min) * sparams->avail_min)
541d5ac70f0Sopenharmony_ci			sparams->start_threshold = (slave->buffer_size / sparams->avail_min) * sparams->avail_min;
542d5ac70f0Sopenharmony_ci	}
543d5ac70f0Sopenharmony_ci	if (sparams->stop_threshold >= params->boundary) {
544d5ac70f0Sopenharmony_ci		sparams->stop_threshold = sparams->boundary;
545d5ac70f0Sopenharmony_ci	} else {
546d5ac70f0Sopenharmony_ci		recalc(pcm, &sparams->stop_threshold);
547d5ac70f0Sopenharmony_ci	}
548d5ac70f0Sopenharmony_ci	recalc(pcm, &sparams->silence_threshold);
549d5ac70f0Sopenharmony_ci	if (sparams->silence_size >= params->boundary) {
550d5ac70f0Sopenharmony_ci		sparams->silence_size = sparams->boundary;
551d5ac70f0Sopenharmony_ci	} else {
552d5ac70f0Sopenharmony_ci		recalc(pcm, &sparams->silence_size);
553d5ac70f0Sopenharmony_ci	}
554d5ac70f0Sopenharmony_ci	return snd_pcm_sw_params(slave, sparams);
555d5ac70f0Sopenharmony_ci}
556d5ac70f0Sopenharmony_ci
557d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_init(snd_pcm_t *pcm)
558d5ac70f0Sopenharmony_ci{
559d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
560d5ac70f0Sopenharmony_ci
561d5ac70f0Sopenharmony_ci	if (rate->ops.reset)
562d5ac70f0Sopenharmony_ci		rate->ops.reset(rate->obj);
563d5ac70f0Sopenharmony_ci	rate->last_commit_ptr = 0;
564d5ac70f0Sopenharmony_ci	rate->start_pending = 0;
565d5ac70f0Sopenharmony_ci	return 0;
566d5ac70f0Sopenharmony_ci}
567d5ac70f0Sopenharmony_ci
568d5ac70f0Sopenharmony_cistatic void do_convert(const snd_pcm_channel_area_t *dst_areas,
569d5ac70f0Sopenharmony_ci		       snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
570d5ac70f0Sopenharmony_ci		       const snd_pcm_channel_area_t *src_areas,
571d5ac70f0Sopenharmony_ci		       snd_pcm_uframes_t src_offset, unsigned int src_frames,
572d5ac70f0Sopenharmony_ci		       unsigned int channels,
573d5ac70f0Sopenharmony_ci		       snd_pcm_rate_t *rate)
574d5ac70f0Sopenharmony_ci{
575d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *out_areas;
576d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t out_offset;
577d5ac70f0Sopenharmony_ci
578d5ac70f0Sopenharmony_ci	if (rate->dst_buf) {
579d5ac70f0Sopenharmony_ci		out_areas = rate->dst_buf;
580d5ac70f0Sopenharmony_ci		out_offset = 0;
581d5ac70f0Sopenharmony_ci	} else {
582d5ac70f0Sopenharmony_ci		out_areas = dst_areas;
583d5ac70f0Sopenharmony_ci		out_offset = dst_offset;
584d5ac70f0Sopenharmony_ci	}
585d5ac70f0Sopenharmony_ci
586d5ac70f0Sopenharmony_ci	if (rate->src_buf) {
587d5ac70f0Sopenharmony_ci		snd_pcm_linear_convert(rate->src_buf, 0,
588d5ac70f0Sopenharmony_ci				       src_areas, src_offset,
589d5ac70f0Sopenharmony_ci				       channels, src_frames,
590d5ac70f0Sopenharmony_ci				       rate->src_conv_idx);
591d5ac70f0Sopenharmony_ci		src_areas = rate->src_buf;
592d5ac70f0Sopenharmony_ci		src_offset = 0;
593d5ac70f0Sopenharmony_ci	}
594d5ac70f0Sopenharmony_ci
595d5ac70f0Sopenharmony_ci	if (rate->ops.convert)
596d5ac70f0Sopenharmony_ci		rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
597d5ac70f0Sopenharmony_ci				   src_areas, src_offset, src_frames);
598d5ac70f0Sopenharmony_ci	else
599d5ac70f0Sopenharmony_ci		rate->ops.convert_s16(rate->obj,
600d5ac70f0Sopenharmony_ci				      snd_pcm_channel_area_addr(out_areas, out_offset),
601d5ac70f0Sopenharmony_ci				      dst_frames,
602d5ac70f0Sopenharmony_ci				      snd_pcm_channel_area_addr(src_areas, src_offset),
603d5ac70f0Sopenharmony_ci				      src_frames);
604d5ac70f0Sopenharmony_ci	if (rate->dst_buf)
605d5ac70f0Sopenharmony_ci		snd_pcm_linear_convert(dst_areas, dst_offset,
606d5ac70f0Sopenharmony_ci				       rate->dst_buf, 0,
607d5ac70f0Sopenharmony_ci				       channels, dst_frames,
608d5ac70f0Sopenharmony_ci				       rate->dst_conv_idx);
609d5ac70f0Sopenharmony_ci}
610d5ac70f0Sopenharmony_ci
611d5ac70f0Sopenharmony_cistatic inline void
612d5ac70f0Sopenharmony_cisnd_pcm_rate_write_areas1(snd_pcm_t *pcm,
613d5ac70f0Sopenharmony_ci			 const snd_pcm_channel_area_t *areas,
614d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t offset,
615d5ac70f0Sopenharmony_ci			 const snd_pcm_channel_area_t *slave_areas,
616d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t slave_offset)
617d5ac70f0Sopenharmony_ci{
618d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
619d5ac70f0Sopenharmony_ci	do_convert(slave_areas, slave_offset, rate->gen.slave->period_size,
620d5ac70f0Sopenharmony_ci		   areas, offset, pcm->period_size,
621d5ac70f0Sopenharmony_ci		   pcm->channels, rate);
622d5ac70f0Sopenharmony_ci}
623d5ac70f0Sopenharmony_ci
624d5ac70f0Sopenharmony_cistatic inline void
625d5ac70f0Sopenharmony_cisnd_pcm_rate_read_areas1(snd_pcm_t *pcm,
626d5ac70f0Sopenharmony_ci			 const snd_pcm_channel_area_t *areas,
627d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t offset,
628d5ac70f0Sopenharmony_ci			 const snd_pcm_channel_area_t *slave_areas,
629d5ac70f0Sopenharmony_ci			 snd_pcm_uframes_t slave_offset)
630d5ac70f0Sopenharmony_ci{
631d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
632d5ac70f0Sopenharmony_ci	do_convert(areas, offset, pcm->period_size,
633d5ac70f0Sopenharmony_ci		   slave_areas, slave_offset, rate->gen.slave->period_size,
634d5ac70f0Sopenharmony_ci		   pcm->channels, rate);
635d5ac70f0Sopenharmony_ci}
636d5ac70f0Sopenharmony_ci
637d5ac70f0Sopenharmony_cistatic inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
638d5ac70f0Sopenharmony_ci{
639d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate;
640d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t slave_hw_ptr_diff;
641d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t last_slave_hw_ptr_frac;
642d5ac70f0Sopenharmony_ci
643d5ac70f0Sopenharmony_ci	if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
644d5ac70f0Sopenharmony_ci		return;
645d5ac70f0Sopenharmony_ci
646d5ac70f0Sopenharmony_ci	rate = pcm->private_data;
647d5ac70f0Sopenharmony_ci	slave_hw_ptr_diff = pcm_frame_diff(slave_hw_ptr, rate->last_slave_hw_ptr, rate->gen.slave->boundary);
648d5ac70f0Sopenharmony_ci	if (slave_hw_ptr_diff == 0)
649d5ac70f0Sopenharmony_ci		return;
650d5ac70f0Sopenharmony_ci	last_slave_hw_ptr_frac = rate->last_slave_hw_ptr % rate->gen.slave->period_size;
651d5ac70f0Sopenharmony_ci	/* While handling fraction part fo slave period, rounded value will be
652d5ac70f0Sopenharmony_ci	 * introduced by input_frames().
653d5ac70f0Sopenharmony_ci	 * To eliminate rounding issue on rate->hw_ptr, subtract last rounded
654d5ac70f0Sopenharmony_ci	 * value from rate->hw_ptr and add new rounded value of present
655d5ac70f0Sopenharmony_ci	 * slave_hw_ptr fraction part to rate->hw_ptr. Hence,
656d5ac70f0Sopenharmony_ci	 * rate->hw_ptr += [ (no. of updated slave periods * pcm rate period size) -
657d5ac70f0Sopenharmony_ci	 * 	fractional part of last_slave_hw_ptr rounded value +
658d5ac70f0Sopenharmony_ci	 * 	fractional part of updated slave hw ptr's rounded value ]
659d5ac70f0Sopenharmony_ci	 */
660d5ac70f0Sopenharmony_ci	rate->hw_ptr += (
661d5ac70f0Sopenharmony_ci			(((last_slave_hw_ptr_frac + slave_hw_ptr_diff) / rate->gen.slave->period_size) * pcm->period_size) -
662d5ac70f0Sopenharmony_ci			rate->ops.input_frames(rate->obj, last_slave_hw_ptr_frac) +
663d5ac70f0Sopenharmony_ci			rate->ops.input_frames(rate->obj, (last_slave_hw_ptr_frac + slave_hw_ptr_diff) % rate->gen.slave->period_size));
664d5ac70f0Sopenharmony_ci	rate->last_slave_hw_ptr = slave_hw_ptr;
665d5ac70f0Sopenharmony_ci
666d5ac70f0Sopenharmony_ci	rate->hw_ptr %= pcm->boundary;
667d5ac70f0Sopenharmony_ci}
668d5ac70f0Sopenharmony_ci
669d5ac70f0Sopenharmony_cistatic inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
670d5ac70f0Sopenharmony_ci{
671d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
672d5ac70f0Sopenharmony_ci	snd_pcm_rate_sync_hwptr0(pcm, *rate->gen.slave->hw.ptr);
673d5ac70f0Sopenharmony_ci}
674d5ac70f0Sopenharmony_ci
675d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
676d5ac70f0Sopenharmony_ci{
677d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
678d5ac70f0Sopenharmony_ci	int err = snd_pcm_hwsync(rate->gen.slave);
679d5ac70f0Sopenharmony_ci	if (err < 0)
680d5ac70f0Sopenharmony_ci		return err;
681d5ac70f0Sopenharmony_ci	snd_pcm_rate_sync_hwptr(pcm);
682d5ac70f0Sopenharmony_ci	return 0;
683d5ac70f0Sopenharmony_ci}
684d5ac70f0Sopenharmony_ci
685d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t snd_pcm_rate_playback_internal_delay(snd_pcm_t *pcm)
686d5ac70f0Sopenharmony_ci{
687d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
688d5ac70f0Sopenharmony_ci
689d5ac70f0Sopenharmony_ci	return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
690d5ac70f0Sopenharmony_ci}
691d5ac70f0Sopenharmony_ci
692d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
693d5ac70f0Sopenharmony_ci{
694d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
695d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t slave_delay;
696d5ac70f0Sopenharmony_ci	int err;
697d5ac70f0Sopenharmony_ci
698d5ac70f0Sopenharmony_ci	snd_pcm_rate_hwsync(pcm);
699d5ac70f0Sopenharmony_ci
700d5ac70f0Sopenharmony_ci	err = snd_pcm_delay(rate->gen.slave, &slave_delay);
701d5ac70f0Sopenharmony_ci	if (err < 0) {
702d5ac70f0Sopenharmony_ci		return err;
703d5ac70f0Sopenharmony_ci	}
704d5ac70f0Sopenharmony_ci
705d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
706d5ac70f0Sopenharmony_ci		*delayp = rate->ops.input_frames(rate->obj, slave_delay)
707d5ac70f0Sopenharmony_ci				+ snd_pcm_rate_playback_internal_delay(pcm);
708d5ac70f0Sopenharmony_ci	} else {
709d5ac70f0Sopenharmony_ci		*delayp = rate->ops.output_frames(rate->obj, slave_delay)
710d5ac70f0Sopenharmony_ci				+ snd_pcm_mmap_capture_delay(pcm);
711d5ac70f0Sopenharmony_ci	}
712d5ac70f0Sopenharmony_ci	return 0;
713d5ac70f0Sopenharmony_ci}
714d5ac70f0Sopenharmony_ci
715d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_prepare(snd_pcm_t *pcm)
716d5ac70f0Sopenharmony_ci{
717d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
718d5ac70f0Sopenharmony_ci	int err;
719d5ac70f0Sopenharmony_ci
720d5ac70f0Sopenharmony_ci	err = snd_pcm_prepare(rate->gen.slave);
721d5ac70f0Sopenharmony_ci	if (err < 0)
722d5ac70f0Sopenharmony_ci		return err;
723d5ac70f0Sopenharmony_ci	*pcm->hw.ptr = 0;
724d5ac70f0Sopenharmony_ci	*pcm->appl.ptr = 0;
725d5ac70f0Sopenharmony_ci	rate->last_slave_hw_ptr = 0;
726d5ac70f0Sopenharmony_ci	err = snd_pcm_rate_init(pcm);
727d5ac70f0Sopenharmony_ci	if (err < 0)
728d5ac70f0Sopenharmony_ci		return err;
729d5ac70f0Sopenharmony_ci	return 0;
730d5ac70f0Sopenharmony_ci}
731d5ac70f0Sopenharmony_ci
732d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_reset(snd_pcm_t *pcm)
733d5ac70f0Sopenharmony_ci{
734d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
735d5ac70f0Sopenharmony_ci	int err;
736d5ac70f0Sopenharmony_ci	err = snd_pcm_reset(rate->gen.slave);
737d5ac70f0Sopenharmony_ci	if (err < 0)
738d5ac70f0Sopenharmony_ci		return err;
739d5ac70f0Sopenharmony_ci	*pcm->hw.ptr = 0;
740d5ac70f0Sopenharmony_ci	*pcm->appl.ptr = 0;
741d5ac70f0Sopenharmony_ci	rate->last_slave_hw_ptr = 0;
742d5ac70f0Sopenharmony_ci	err = snd_pcm_rate_init(pcm);
743d5ac70f0Sopenharmony_ci	if (err < 0)
744d5ac70f0Sopenharmony_ci		return err;
745d5ac70f0Sopenharmony_ci	return 0;
746d5ac70f0Sopenharmony_ci}
747d5ac70f0Sopenharmony_ci
748d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
749d5ac70f0Sopenharmony_ci{
750d5ac70f0Sopenharmony_ci	return 0;
751d5ac70f0Sopenharmony_ci}
752d5ac70f0Sopenharmony_ci
753d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
754d5ac70f0Sopenharmony_ci{
755d5ac70f0Sopenharmony_ci	return 0;
756d5ac70f0Sopenharmony_ci}
757d5ac70f0Sopenharmony_ci
758d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
759d5ac70f0Sopenharmony_ci                                             snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
760d5ac70f0Sopenharmony_ci{
761d5ac70f0Sopenharmony_ci        return 0;
762d5ac70f0Sopenharmony_ci}
763d5ac70f0Sopenharmony_ci
764d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
765d5ac70f0Sopenharmony_ci                                              snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
766d5ac70f0Sopenharmony_ci{
767d5ac70f0Sopenharmony_ci        return 0;
768d5ac70f0Sopenharmony_ci}
769d5ac70f0Sopenharmony_ci
770d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate,
771d5ac70f0Sopenharmony_ci				    snd_pcm_uframes_t appl_offset,
772d5ac70f0Sopenharmony_ci				    snd_pcm_uframes_t size ATTRIBUTE_UNUSED,
773d5ac70f0Sopenharmony_ci				    snd_pcm_uframes_t slave_size)
774d5ac70f0Sopenharmony_ci{
775d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
776d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *areas;
777d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *slave_areas;
778d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t slave_offset, xfer;
779d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t slave_frames = ULONG_MAX;
780d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t result;
781d5ac70f0Sopenharmony_ci
782d5ac70f0Sopenharmony_ci	areas = snd_pcm_mmap_areas(pcm);
783d5ac70f0Sopenharmony_ci	/*
784d5ac70f0Sopenharmony_ci	 * Because snd_pcm_rate_write_areas1() below will convert a full source period
785d5ac70f0Sopenharmony_ci	 * then there had better be a full period available in the current buffer.
786d5ac70f0Sopenharmony_ci	 */
787d5ac70f0Sopenharmony_ci	if (cont >= pcm->period_size) {
788d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
789d5ac70f0Sopenharmony_ci		if (result < 0)
790d5ac70f0Sopenharmony_ci			return result;
791d5ac70f0Sopenharmony_ci		/*
792d5ac70f0Sopenharmony_ci		 * Because snd_pcm_rate_write_areas1() below will convert to a full slave period
793d5ac70f0Sopenharmony_ci		 * then there had better be a full slave period available in the slave buffer.
794d5ac70f0Sopenharmony_ci		 */
795d5ac70f0Sopenharmony_ci		if (slave_frames < rate->gen.slave->period_size) {
796d5ac70f0Sopenharmony_ci			snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0);
797d5ac70f0Sopenharmony_ci			goto __partial;
798d5ac70f0Sopenharmony_ci		}
799d5ac70f0Sopenharmony_ci		snd_pcm_rate_write_areas1(pcm, areas, appl_offset,
800d5ac70f0Sopenharmony_ci					  slave_areas, slave_offset);
801d5ac70f0Sopenharmony_ci		/* Only commit the requested slave_size, even if more was actually converted */
802d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size);
803d5ac70f0Sopenharmony_ci		if (result < (snd_pcm_sframes_t)slave_size) {
804d5ac70f0Sopenharmony_ci			if (result < 0)
805d5ac70f0Sopenharmony_ci				return result;
806d5ac70f0Sopenharmony_ci			result = snd_pcm_rewind(rate->gen.slave, result);
807d5ac70f0Sopenharmony_ci			if (result < 0)
808d5ac70f0Sopenharmony_ci				return result;
809d5ac70f0Sopenharmony_ci			return 0;
810d5ac70f0Sopenharmony_ci		}
811d5ac70f0Sopenharmony_ci	} else {
812d5ac70f0Sopenharmony_ci		snd_pcm_areas_copy(rate->pareas, 0,
813d5ac70f0Sopenharmony_ci				   areas, appl_offset,
814d5ac70f0Sopenharmony_ci				   pcm->channels, cont,
815d5ac70f0Sopenharmony_ci				   pcm->format);
816d5ac70f0Sopenharmony_ci		snd_pcm_areas_copy(rate->pareas, cont,
817d5ac70f0Sopenharmony_ci				   areas, 0,
818d5ac70f0Sopenharmony_ci				   pcm->channels, pcm->period_size - cont,
819d5ac70f0Sopenharmony_ci				   pcm->format);
820d5ac70f0Sopenharmony_ci
821d5ac70f0Sopenharmony_ci		snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
822d5ac70f0Sopenharmony_ci
823d5ac70f0Sopenharmony_ci		/* ok, commit first fragment */
824d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
825d5ac70f0Sopenharmony_ci		if (result < 0)
826d5ac70f0Sopenharmony_ci			return result;
827d5ac70f0Sopenharmony_ci	      __partial:
828d5ac70f0Sopenharmony_ci		cont = slave_frames;
829d5ac70f0Sopenharmony_ci		if (cont > slave_size)
830d5ac70f0Sopenharmony_ci			cont = slave_size;
831d5ac70f0Sopenharmony_ci		snd_pcm_areas_copy(slave_areas, slave_offset,
832d5ac70f0Sopenharmony_ci				   rate->sareas, 0,
833d5ac70f0Sopenharmony_ci				   pcm->channels, cont,
834d5ac70f0Sopenharmony_ci				   rate->gen.slave->format);
835d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
836d5ac70f0Sopenharmony_ci		if (result < (snd_pcm_sframes_t)cont) {
837d5ac70f0Sopenharmony_ci			if (result < 0)
838d5ac70f0Sopenharmony_ci				return result;
839d5ac70f0Sopenharmony_ci			result = snd_pcm_rewind(rate->gen.slave, result);
840d5ac70f0Sopenharmony_ci			if (result < 0)
841d5ac70f0Sopenharmony_ci				return result;
842d5ac70f0Sopenharmony_ci			return 0;
843d5ac70f0Sopenharmony_ci		}
844d5ac70f0Sopenharmony_ci		xfer = cont;
845d5ac70f0Sopenharmony_ci
846d5ac70f0Sopenharmony_ci		if (xfer == slave_size)
847d5ac70f0Sopenharmony_ci			goto commit_done;
848d5ac70f0Sopenharmony_ci
849d5ac70f0Sopenharmony_ci		/* commit second fragment */
850d5ac70f0Sopenharmony_ci		cont = slave_size - cont;
851d5ac70f0Sopenharmony_ci		slave_frames = cont;
852d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
853d5ac70f0Sopenharmony_ci		if (result < 0)
854d5ac70f0Sopenharmony_ci			return result;
855d5ac70f0Sopenharmony_ci#if 0
856d5ac70f0Sopenharmony_ci		if (slave_offset) {
857d5ac70f0Sopenharmony_ci			SNDERR("non-zero slave_offset %ld", slave_offset);
858d5ac70f0Sopenharmony_ci			return -EIO;
859d5ac70f0Sopenharmony_ci		}
860d5ac70f0Sopenharmony_ci#endif
861d5ac70f0Sopenharmony_ci		snd_pcm_areas_copy(slave_areas, slave_offset,
862d5ac70f0Sopenharmony_ci				   rate->sareas, xfer,
863d5ac70f0Sopenharmony_ci				   pcm->channels, cont,
864d5ac70f0Sopenharmony_ci				   rate->gen.slave->format);
865d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
866d5ac70f0Sopenharmony_ci		if (result < (snd_pcm_sframes_t)cont) {
867d5ac70f0Sopenharmony_ci			if (result < 0)
868d5ac70f0Sopenharmony_ci				return result;
869d5ac70f0Sopenharmony_ci			result = snd_pcm_rewind(rate->gen.slave, result + xfer);
870d5ac70f0Sopenharmony_ci			if (result < 0)
871d5ac70f0Sopenharmony_ci				return result;
872d5ac70f0Sopenharmony_ci			return 0;
873d5ac70f0Sopenharmony_ci		}
874d5ac70f0Sopenharmony_ci	}
875d5ac70f0Sopenharmony_ci
876d5ac70f0Sopenharmony_ci commit_done:
877d5ac70f0Sopenharmony_ci	if (rate->start_pending) {
878d5ac70f0Sopenharmony_ci		/* we have pending start-trigger.  let's issue it now */
879d5ac70f0Sopenharmony_ci		snd_pcm_start(rate->gen.slave);
880d5ac70f0Sopenharmony_ci		rate->start_pending = 0;
881d5ac70f0Sopenharmony_ci	}
882d5ac70f0Sopenharmony_ci	return 1;
883d5ac70f0Sopenharmony_ci}
884d5ac70f0Sopenharmony_ci
885d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
886d5ac70f0Sopenharmony_ci{
887d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
888d5ac70f0Sopenharmony_ci
889d5ac70f0Sopenharmony_ci	return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
890d5ac70f0Sopenharmony_ci					rate->gen.slave->period_size);
891d5ac70f0Sopenharmony_ci}
892d5ac70f0Sopenharmony_ci
893d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
894d5ac70f0Sopenharmony_ci{
895d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
896d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
897d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *areas;
898d5ac70f0Sopenharmony_ci	const snd_pcm_channel_area_t *slave_areas;
899d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t slave_offset, xfer;
900d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t slave_frames = ULONG_MAX;
901d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t result;
902d5ac70f0Sopenharmony_ci
903d5ac70f0Sopenharmony_ci	areas = snd_pcm_mmap_areas(pcm);
904d5ac70f0Sopenharmony_ci	if (cont >= pcm->period_size) {
905d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
906d5ac70f0Sopenharmony_ci		if (result < 0)
907d5ac70f0Sopenharmony_ci			return result;
908d5ac70f0Sopenharmony_ci		if (slave_frames < rate->gen.slave->period_size)
909d5ac70f0Sopenharmony_ci			goto __partial;
910d5ac70f0Sopenharmony_ci		snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
911d5ac70f0Sopenharmony_ci					 slave_areas, slave_offset);
912d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, rate->gen.slave->period_size);
913d5ac70f0Sopenharmony_ci		if (result < (snd_pcm_sframes_t)rate->gen.slave->period_size) {
914d5ac70f0Sopenharmony_ci			if (result < 0)
915d5ac70f0Sopenharmony_ci				return result;
916d5ac70f0Sopenharmony_ci			result = snd_pcm_rewind(rate->gen.slave, result);
917d5ac70f0Sopenharmony_ci			if (result < 0)
918d5ac70f0Sopenharmony_ci				return result;
919d5ac70f0Sopenharmony_ci			return 0;
920d5ac70f0Sopenharmony_ci		}
921d5ac70f0Sopenharmony_ci	} else {
922d5ac70f0Sopenharmony_ci		/* ok, grab first fragment */
923d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
924d5ac70f0Sopenharmony_ci		if (result < 0)
925d5ac70f0Sopenharmony_ci			return result;
926d5ac70f0Sopenharmony_ci	      __partial:
927d5ac70f0Sopenharmony_ci		cont = slave_frames;
928d5ac70f0Sopenharmony_ci		if (cont > rate->gen.slave->period_size)
929d5ac70f0Sopenharmony_ci			cont = rate->gen.slave->period_size;
930d5ac70f0Sopenharmony_ci		snd_pcm_areas_copy(rate->sareas, 0,
931d5ac70f0Sopenharmony_ci				   slave_areas, slave_offset,
932d5ac70f0Sopenharmony_ci				   pcm->channels, cont,
933d5ac70f0Sopenharmony_ci				   rate->gen.slave->format);
934d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
935d5ac70f0Sopenharmony_ci		if (result < (snd_pcm_sframes_t)cont) {
936d5ac70f0Sopenharmony_ci			if (result < 0)
937d5ac70f0Sopenharmony_ci				return result;
938d5ac70f0Sopenharmony_ci			result = snd_pcm_rewind(rate->gen.slave, result);
939d5ac70f0Sopenharmony_ci			if (result < 0)
940d5ac70f0Sopenharmony_ci				return result;
941d5ac70f0Sopenharmony_ci			return 0;
942d5ac70f0Sopenharmony_ci		}
943d5ac70f0Sopenharmony_ci		xfer = cont;
944d5ac70f0Sopenharmony_ci
945d5ac70f0Sopenharmony_ci		if (xfer == rate->gen.slave->period_size)
946d5ac70f0Sopenharmony_ci			goto __transfer;
947d5ac70f0Sopenharmony_ci
948d5ac70f0Sopenharmony_ci		/* grab second fragment */
949d5ac70f0Sopenharmony_ci		cont = rate->gen.slave->period_size - cont;
950d5ac70f0Sopenharmony_ci		slave_frames = cont;
951d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
952d5ac70f0Sopenharmony_ci		if (result < 0)
953d5ac70f0Sopenharmony_ci			return result;
954d5ac70f0Sopenharmony_ci#if 0
955d5ac70f0Sopenharmony_ci		if (slave_offset) {
956d5ac70f0Sopenharmony_ci			SNDERR("non-zero slave_offset %ld", slave_offset);
957d5ac70f0Sopenharmony_ci			return -EIO;
958d5ac70f0Sopenharmony_ci		}
959d5ac70f0Sopenharmony_ci#endif
960d5ac70f0Sopenharmony_ci		snd_pcm_areas_copy(rate->sareas, xfer,
961d5ac70f0Sopenharmony_ci		                   slave_areas, slave_offset,
962d5ac70f0Sopenharmony_ci				   pcm->channels, cont,
963d5ac70f0Sopenharmony_ci				   rate->gen.slave->format);
964d5ac70f0Sopenharmony_ci		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
965d5ac70f0Sopenharmony_ci		if (result < (snd_pcm_sframes_t)cont) {
966d5ac70f0Sopenharmony_ci			if (result < 0)
967d5ac70f0Sopenharmony_ci				return result;
968d5ac70f0Sopenharmony_ci			result = snd_pcm_rewind(rate->gen.slave, result + xfer);
969d5ac70f0Sopenharmony_ci			if (result < 0)
970d5ac70f0Sopenharmony_ci				return result;
971d5ac70f0Sopenharmony_ci			return 0;
972d5ac70f0Sopenharmony_ci		}
973d5ac70f0Sopenharmony_ci
974d5ac70f0Sopenharmony_ci	      __transfer:
975d5ac70f0Sopenharmony_ci		cont = pcm->buffer_size - hw_offset;
976d5ac70f0Sopenharmony_ci		if (cont >= pcm->period_size) {
977d5ac70f0Sopenharmony_ci			snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
978d5ac70f0Sopenharmony_ci						 rate->sareas, 0);
979d5ac70f0Sopenharmony_ci		} else {
980d5ac70f0Sopenharmony_ci			snd_pcm_rate_read_areas1(pcm,
981d5ac70f0Sopenharmony_ci						 rate->pareas, 0,
982d5ac70f0Sopenharmony_ci						 rate->sareas, 0);
983d5ac70f0Sopenharmony_ci			snd_pcm_areas_copy(areas, hw_offset,
984d5ac70f0Sopenharmony_ci					   rate->pareas, 0,
985d5ac70f0Sopenharmony_ci					   pcm->channels, cont,
986d5ac70f0Sopenharmony_ci					   pcm->format);
987d5ac70f0Sopenharmony_ci			snd_pcm_areas_copy(areas, 0,
988d5ac70f0Sopenharmony_ci					   rate->pareas, cont,
989d5ac70f0Sopenharmony_ci					   pcm->channels, pcm->period_size - cont,
990d5ac70f0Sopenharmony_ci					   pcm->format);
991d5ac70f0Sopenharmony_ci		}
992d5ac70f0Sopenharmony_ci	}
993d5ac70f0Sopenharmony_ci	return 1;
994d5ac70f0Sopenharmony_ci}
995d5ac70f0Sopenharmony_ci
996d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
997d5ac70f0Sopenharmony_ci{
998d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
999d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = rate->gen.slave;
1000d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t xfer;
1001d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t slave_size;
1002d5ac70f0Sopenharmony_ci	int err;
1003d5ac70f0Sopenharmony_ci
1004d5ac70f0Sopenharmony_ci	slave_size = snd_pcm_avail_update(slave);
1005d5ac70f0Sopenharmony_ci	if (slave_size < 0)
1006d5ac70f0Sopenharmony_ci		return slave_size;
1007d5ac70f0Sopenharmony_ci
1008d5ac70f0Sopenharmony_ci	xfer = pcm_frame_diff(appl_ptr, rate->last_commit_ptr, pcm->boundary);
1009d5ac70f0Sopenharmony_ci	while (xfer >= pcm->period_size &&
1010d5ac70f0Sopenharmony_ci	       (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) {
1011d5ac70f0Sopenharmony_ci		err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size);
1012d5ac70f0Sopenharmony_ci		if (err == 0)
1013d5ac70f0Sopenharmony_ci			break;
1014d5ac70f0Sopenharmony_ci		if (err < 0)
1015d5ac70f0Sopenharmony_ci			return err;
1016d5ac70f0Sopenharmony_ci		xfer -= pcm->period_size;
1017d5ac70f0Sopenharmony_ci		slave_size -= rate->gen.slave->period_size;
1018d5ac70f0Sopenharmony_ci		rate->last_commit_ptr += pcm->period_size;
1019d5ac70f0Sopenharmony_ci		if (rate->last_commit_ptr >= pcm->boundary)
1020d5ac70f0Sopenharmony_ci			rate->last_commit_ptr -= pcm->boundary;
1021d5ac70f0Sopenharmony_ci	}
1022d5ac70f0Sopenharmony_ci	return 0;
1023d5ac70f0Sopenharmony_ci}
1024d5ac70f0Sopenharmony_ci
1025d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_mmap_commit(snd_pcm_t *pcm,
1026d5ac70f0Sopenharmony_ci						  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
1027d5ac70f0Sopenharmony_ci						  snd_pcm_uframes_t size)
1028d5ac70f0Sopenharmony_ci{
1029d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1030d5ac70f0Sopenharmony_ci	int err;
1031d5ac70f0Sopenharmony_ci
1032d5ac70f0Sopenharmony_ci	if (size == 0)
1033d5ac70f0Sopenharmony_ci		return 0;
1034d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1035d5ac70f0Sopenharmony_ci		err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size);
1036d5ac70f0Sopenharmony_ci		if (err < 0)
1037d5ac70f0Sopenharmony_ci			return err;
1038d5ac70f0Sopenharmony_ci	}
1039d5ac70f0Sopenharmony_ci	snd_pcm_mmap_appl_forward(pcm, size);
1040d5ac70f0Sopenharmony_ci	return size;
1041d5ac70f0Sopenharmony_ci}
1042d5ac70f0Sopenharmony_ci
1043d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm,
1044d5ac70f0Sopenharmony_ci							   snd_pcm_sframes_t slave_size)
1045d5ac70f0Sopenharmony_ci{
1046d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1047d5ac70f0Sopenharmony_ci	snd_pcm_t *slave = rate->gen.slave;
1048d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t xfer, hw_offset, size;
1049d5ac70f0Sopenharmony_ci
1050d5ac70f0Sopenharmony_ci	xfer = snd_pcm_mmap_capture_avail(pcm);
1051d5ac70f0Sopenharmony_ci	size = pcm->buffer_size - xfer;
1052d5ac70f0Sopenharmony_ci	hw_offset = snd_pcm_mmap_hw_offset(pcm);
1053d5ac70f0Sopenharmony_ci	while (size >= pcm->period_size &&
1054d5ac70f0Sopenharmony_ci	       (snd_pcm_uframes_t)slave_size >= slave->period_size) {
1055d5ac70f0Sopenharmony_ci		int err = snd_pcm_rate_grab_next_period(pcm, hw_offset);
1056d5ac70f0Sopenharmony_ci		if (err < 0)
1057d5ac70f0Sopenharmony_ci			return err;
1058d5ac70f0Sopenharmony_ci		if (err == 0)
1059d5ac70f0Sopenharmony_ci			return (snd_pcm_sframes_t)xfer;
1060d5ac70f0Sopenharmony_ci		xfer += pcm->period_size;
1061d5ac70f0Sopenharmony_ci		size -= pcm->period_size;
1062d5ac70f0Sopenharmony_ci		slave_size -= slave->period_size;
1063d5ac70f0Sopenharmony_ci		hw_offset += pcm->period_size;
1064d5ac70f0Sopenharmony_ci		hw_offset %= pcm->buffer_size;
1065d5ac70f0Sopenharmony_ci		snd_pcm_mmap_hw_forward(pcm, pcm->period_size);
1066d5ac70f0Sopenharmony_ci	}
1067d5ac70f0Sopenharmony_ci	return (snd_pcm_sframes_t)xfer;
1068d5ac70f0Sopenharmony_ci}
1069d5ac70f0Sopenharmony_ci
1070d5ac70f0Sopenharmony_cistatic snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
1071d5ac70f0Sopenharmony_ci{
1072d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1073d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t slave_size;
1074d5ac70f0Sopenharmony_ci
1075d5ac70f0Sopenharmony_ci	slave_size = snd_pcm_avail_update(rate->gen.slave);
1076d5ac70f0Sopenharmony_ci	if (slave_size < 0)
1077d5ac70f0Sopenharmony_ci		return slave_size;
1078d5ac70f0Sopenharmony_ci
1079d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1080d5ac70f0Sopenharmony_ci		return snd_pcm_rate_avail_update_capture(pcm, slave_size);
1081d5ac70f0Sopenharmony_ci
1082d5ac70f0Sopenharmony_ci	snd_pcm_rate_sync_hwptr(pcm);
1083d5ac70f0Sopenharmony_ci	snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1084d5ac70f0Sopenharmony_ci	return snd_pcm_mmap_avail(pcm);
1085d5ac70f0Sopenharmony_ci}
1086d5ac70f0Sopenharmony_ci
1087d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
1088d5ac70f0Sopenharmony_ci				   snd_pcm_uframes_t *avail,
1089d5ac70f0Sopenharmony_ci				   snd_htimestamp_t *tstamp)
1090d5ac70f0Sopenharmony_ci{
1091d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1092d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t avail1;
1093d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t tmp;
1094d5ac70f0Sopenharmony_ci	int ok = 0, err;
1095d5ac70f0Sopenharmony_ci
1096d5ac70f0Sopenharmony_ci	while (1) {
1097d5ac70f0Sopenharmony_ci		/* the position is from this plugin itself */
1098d5ac70f0Sopenharmony_ci		avail1 = snd_pcm_avail_update(pcm);
1099d5ac70f0Sopenharmony_ci		if (avail1 < 0)
1100d5ac70f0Sopenharmony_ci			return avail1;
1101d5ac70f0Sopenharmony_ci		if (ok && (snd_pcm_uframes_t)avail1 == *avail)
1102d5ac70f0Sopenharmony_ci			break;
1103d5ac70f0Sopenharmony_ci		*avail = avail1;
1104d5ac70f0Sopenharmony_ci		/* timestamp is taken from the slave PCM */
1105d5ac70f0Sopenharmony_ci		err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp);
1106d5ac70f0Sopenharmony_ci		if (err < 0)
1107d5ac70f0Sopenharmony_ci			return err;
1108d5ac70f0Sopenharmony_ci		ok = 1;
1109d5ac70f0Sopenharmony_ci	}
1110d5ac70f0Sopenharmony_ci	return 0;
1111d5ac70f0Sopenharmony_ci}
1112d5ac70f0Sopenharmony_ci
1113d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1114d5ac70f0Sopenharmony_ci{
1115d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1116d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1117d5ac70f0Sopenharmony_ci		/* Try to sync as much as possible */
1118d5ac70f0Sopenharmony_ci		snd_pcm_rate_hwsync(pcm);
1119d5ac70f0Sopenharmony_ci		snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1120d5ac70f0Sopenharmony_ci	}
1121d5ac70f0Sopenharmony_ci	return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
1122d5ac70f0Sopenharmony_ci}
1123d5ac70f0Sopenharmony_ci
1124d5ac70f0Sopenharmony_ci/* locking */
1125d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_drain(snd_pcm_t *pcm)
1126d5ac70f0Sopenharmony_ci{
1127d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1128d5ac70f0Sopenharmony_ci
1129d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1130d5ac70f0Sopenharmony_ci		/* commit the remaining fraction (if any) */
1131d5ac70f0Sopenharmony_ci		snd_pcm_uframes_t size, ofs, saved_avail_min;
1132d5ac70f0Sopenharmony_ci		snd_pcm_sw_params_t sw_params;
1133d5ac70f0Sopenharmony_ci		int commit_err = 0;
1134d5ac70f0Sopenharmony_ci
1135d5ac70f0Sopenharmony_ci		__snd_pcm_lock(pcm);
1136d5ac70f0Sopenharmony_ci		/* temporarily set avail_min to one */
1137d5ac70f0Sopenharmony_ci		sw_params = rate->sw_params;
1138d5ac70f0Sopenharmony_ci		saved_avail_min = sw_params.avail_min;
1139d5ac70f0Sopenharmony_ci		sw_params.avail_min = 1;
1140d5ac70f0Sopenharmony_ci		snd_pcm_sw_params(rate->gen.slave, &sw_params);
1141d5ac70f0Sopenharmony_ci
1142d5ac70f0Sopenharmony_ci		size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
1143d5ac70f0Sopenharmony_ci		ofs = rate->last_commit_ptr % pcm->buffer_size;
1144d5ac70f0Sopenharmony_ci		while (size > 0) {
1145d5ac70f0Sopenharmony_ci			snd_pcm_uframes_t psize, spsize;
1146d5ac70f0Sopenharmony_ci			int err;
1147d5ac70f0Sopenharmony_ci
1148d5ac70f0Sopenharmony_ci			err = __snd_pcm_wait_in_lock(rate->gen.slave, SND_PCM_WAIT_DRAIN);
1149d5ac70f0Sopenharmony_ci			if (err < 0)
1150d5ac70f0Sopenharmony_ci				break;
1151d5ac70f0Sopenharmony_ci			if (size > pcm->period_size) {
1152d5ac70f0Sopenharmony_ci				psize = pcm->period_size;
1153d5ac70f0Sopenharmony_ci				spsize = rate->gen.slave->period_size;
1154d5ac70f0Sopenharmony_ci			} else {
1155d5ac70f0Sopenharmony_ci				psize = size;
1156d5ac70f0Sopenharmony_ci				spsize = rate->ops.output_frames(rate->obj, size);
1157d5ac70f0Sopenharmony_ci				if (! spsize)
1158d5ac70f0Sopenharmony_ci					break;
1159d5ac70f0Sopenharmony_ci			}
1160d5ac70f0Sopenharmony_ci			commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs,
1161d5ac70f0Sopenharmony_ci						 psize, spsize);
1162d5ac70f0Sopenharmony_ci			if (commit_err == 1) {
1163d5ac70f0Sopenharmony_ci				rate->last_commit_ptr += psize;
1164d5ac70f0Sopenharmony_ci				if (rate->last_commit_ptr >= pcm->boundary)
1165d5ac70f0Sopenharmony_ci					rate->last_commit_ptr -= pcm->boundary;
1166d5ac70f0Sopenharmony_ci			} else if (commit_err == 0) {
1167d5ac70f0Sopenharmony_ci				if (pcm->mode & SND_PCM_NONBLOCK) {
1168d5ac70f0Sopenharmony_ci					commit_err = -EAGAIN;
1169d5ac70f0Sopenharmony_ci					break;
1170d5ac70f0Sopenharmony_ci				}
1171d5ac70f0Sopenharmony_ci				continue;
1172d5ac70f0Sopenharmony_ci			} else
1173d5ac70f0Sopenharmony_ci				break;
1174d5ac70f0Sopenharmony_ci
1175d5ac70f0Sopenharmony_ci			ofs = (ofs + psize) % pcm->buffer_size;
1176d5ac70f0Sopenharmony_ci			size -= psize;
1177d5ac70f0Sopenharmony_ci		}
1178d5ac70f0Sopenharmony_ci		sw_params.avail_min = saved_avail_min;
1179d5ac70f0Sopenharmony_ci		snd_pcm_sw_params(rate->gen.slave, &sw_params);
1180d5ac70f0Sopenharmony_ci		__snd_pcm_unlock(pcm);
1181d5ac70f0Sopenharmony_ci		if (commit_err < 0)
1182d5ac70f0Sopenharmony_ci			return commit_err;
1183d5ac70f0Sopenharmony_ci	}
1184d5ac70f0Sopenharmony_ci	return snd_pcm_drain(rate->gen.slave);
1185d5ac70f0Sopenharmony_ci}
1186d5ac70f0Sopenharmony_ci
1187d5ac70f0Sopenharmony_cistatic snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
1188d5ac70f0Sopenharmony_ci{
1189d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1190d5ac70f0Sopenharmony_ci	if (rate->start_pending) /* pseudo-state */
1191d5ac70f0Sopenharmony_ci		return SND_PCM_STATE_RUNNING;
1192d5ac70f0Sopenharmony_ci	return snd_pcm_state(rate->gen.slave);
1193d5ac70f0Sopenharmony_ci}
1194d5ac70f0Sopenharmony_ci
1195d5ac70f0Sopenharmony_ci
1196d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_start(snd_pcm_t *pcm)
1197d5ac70f0Sopenharmony_ci{
1198d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1199d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t avail;
1200d5ac70f0Sopenharmony_ci
1201d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1202d5ac70f0Sopenharmony_ci		return snd_pcm_start(rate->gen.slave);
1203d5ac70f0Sopenharmony_ci
1204d5ac70f0Sopenharmony_ci	if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
1205d5ac70f0Sopenharmony_ci		return -EBADFD;
1206d5ac70f0Sopenharmony_ci
1207d5ac70f0Sopenharmony_ci	gettimestamp(&rate->trigger_tstamp, pcm->tstamp_type);
1208d5ac70f0Sopenharmony_ci
1209d5ac70f0Sopenharmony_ci	avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave);
1210d5ac70f0Sopenharmony_ci	if (avail < 0) /* can't happen on healthy drivers */
1211d5ac70f0Sopenharmony_ci		return -EBADFD;
1212d5ac70f0Sopenharmony_ci
1213d5ac70f0Sopenharmony_ci	if (avail == 0) {
1214d5ac70f0Sopenharmony_ci		/* postpone the trigger since we have no data committed yet */
1215d5ac70f0Sopenharmony_ci		rate->start_pending = 1;
1216d5ac70f0Sopenharmony_ci		return 0;
1217d5ac70f0Sopenharmony_ci	}
1218d5ac70f0Sopenharmony_ci	rate->start_pending = 0;
1219d5ac70f0Sopenharmony_ci	return snd_pcm_start(rate->gen.slave);
1220d5ac70f0Sopenharmony_ci}
1221d5ac70f0Sopenharmony_ci
1222d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
1223d5ac70f0Sopenharmony_ci{
1224d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1225d5ac70f0Sopenharmony_ci	snd_pcm_sframes_t err;
1226d5ac70f0Sopenharmony_ci
1227d5ac70f0Sopenharmony_ci	err = snd_pcm_status(rate->gen.slave, status);
1228d5ac70f0Sopenharmony_ci	if (err < 0)
1229d5ac70f0Sopenharmony_ci		return err;
1230d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1231d5ac70f0Sopenharmony_ci		if (rate->start_pending)
1232d5ac70f0Sopenharmony_ci			status->state = SND_PCM_STATE_RUNNING;
1233d5ac70f0Sopenharmony_ci		status->trigger_tstamp = rate->trigger_tstamp;
1234d5ac70f0Sopenharmony_ci	}
1235d5ac70f0Sopenharmony_ci	snd_pcm_rate_sync_hwptr0(pcm, status->hw_ptr);
1236d5ac70f0Sopenharmony_ci	status->appl_ptr = *pcm->appl.ptr;
1237d5ac70f0Sopenharmony_ci	status->hw_ptr = *pcm->hw.ptr;
1238d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1239d5ac70f0Sopenharmony_ci		status->delay = rate->ops.input_frames(rate->obj, status->delay)
1240d5ac70f0Sopenharmony_ci					+ snd_pcm_rate_playback_internal_delay(pcm);
1241d5ac70f0Sopenharmony_ci		status->avail = snd_pcm_mmap_playback_avail(pcm);
1242d5ac70f0Sopenharmony_ci		status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max);
1243d5ac70f0Sopenharmony_ci	} else {
1244d5ac70f0Sopenharmony_ci		status->delay = rate->ops.output_frames(rate->obj, status->delay)
1245d5ac70f0Sopenharmony_ci					+ snd_pcm_mmap_capture_delay(pcm);
1246d5ac70f0Sopenharmony_ci		status->avail = snd_pcm_mmap_capture_avail(pcm);
1247d5ac70f0Sopenharmony_ci		status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max);
1248d5ac70f0Sopenharmony_ci	}
1249d5ac70f0Sopenharmony_ci	return 0;
1250d5ac70f0Sopenharmony_ci}
1251d5ac70f0Sopenharmony_ci
1252d5ac70f0Sopenharmony_cistatic void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
1253d5ac70f0Sopenharmony_ci{
1254d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1255d5ac70f0Sopenharmony_ci	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
1256d5ac70f0Sopenharmony_ci		snd_output_printf(out, "Rate conversion PCM (%d)\n",
1257d5ac70f0Sopenharmony_ci			rate->srate);
1258d5ac70f0Sopenharmony_ci	else
1259d5ac70f0Sopenharmony_ci		snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
1260d5ac70f0Sopenharmony_ci			rate->srate,
1261d5ac70f0Sopenharmony_ci			snd_pcm_format_name(rate->sformat));
1262d5ac70f0Sopenharmony_ci	if (rate->ops.dump)
1263d5ac70f0Sopenharmony_ci		rate->ops.dump(rate->obj, out);
1264d5ac70f0Sopenharmony_ci	snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
1265d5ac70f0Sopenharmony_ci	if (pcm->setup) {
1266d5ac70f0Sopenharmony_ci		snd_output_printf(out, "Its setup is:\n");
1267d5ac70f0Sopenharmony_ci		snd_pcm_dump_setup(pcm, out);
1268d5ac70f0Sopenharmony_ci	}
1269d5ac70f0Sopenharmony_ci	snd_output_printf(out, "Slave: ");
1270d5ac70f0Sopenharmony_ci	snd_pcm_dump(rate->gen.slave, out);
1271d5ac70f0Sopenharmony_ci}
1272d5ac70f0Sopenharmony_ci
1273d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_close(snd_pcm_t *pcm)
1274d5ac70f0Sopenharmony_ci{
1275d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1276d5ac70f0Sopenharmony_ci
1277d5ac70f0Sopenharmony_ci	if (rate->ops.close)
1278d5ac70f0Sopenharmony_ci		rate->ops.close(rate->obj);
1279d5ac70f0Sopenharmony_ci	if (rate->open_func)
1280d5ac70f0Sopenharmony_ci		snd_dlobj_cache_put(rate->open_func);
1281d5ac70f0Sopenharmony_ci	return snd_pcm_generic_close(pcm);
1282d5ac70f0Sopenharmony_ci}
1283d5ac70f0Sopenharmony_ci
1284d5ac70f0Sopenharmony_ci/**
1285d5ac70f0Sopenharmony_ci * \brief Convert rate pcm frames to corresponding rate slave pcm frames
1286d5ac70f0Sopenharmony_ci * \param pcm PCM handle
1287d5ac70f0Sopenharmony_ci * \param frames Frames to be converted to slave frames
1288d5ac70f0Sopenharmony_ci * \retval Corresponding slave frames
1289d5ac70f0Sopenharmony_ci*/
1290d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
1291d5ac70f0Sopenharmony_ci{
1292d5ac70f0Sopenharmony_ci	snd_pcm_uframes_t sframes;
1293d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate = pcm->private_data;
1294d5ac70f0Sopenharmony_ci
1295d5ac70f0Sopenharmony_ci	if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
1296d5ac70f0Sopenharmony_ci		sframes = rate->ops.output_frames(rate->obj, frames);
1297d5ac70f0Sopenharmony_ci	else
1298d5ac70f0Sopenharmony_ci		sframes = rate->ops.input_frames(rate->obj, frames);
1299d5ac70f0Sopenharmony_ci
1300d5ac70f0Sopenharmony_ci	return sframes;
1301d5ac70f0Sopenharmony_ci}
1302d5ac70f0Sopenharmony_ci
1303d5ac70f0Sopenharmony_cistatic int snd_pcm_rate_may_wait_for_avail_min(snd_pcm_t *pcm,
1304d5ac70f0Sopenharmony_ci					       snd_pcm_uframes_t avail)
1305d5ac70f0Sopenharmony_ci{
1306d5ac70f0Sopenharmony_ci	return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail,
1307d5ac70f0Sopenharmony_ci							  snd_pcm_rate_slave_frames);
1308d5ac70f0Sopenharmony_ci}
1309d5ac70f0Sopenharmony_ci
1310d5ac70f0Sopenharmony_cistatic const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
1311d5ac70f0Sopenharmony_ci	.status = snd_pcm_rate_status,
1312d5ac70f0Sopenharmony_ci	.state = snd_pcm_rate_state,
1313d5ac70f0Sopenharmony_ci	.hwsync = snd_pcm_rate_hwsync,
1314d5ac70f0Sopenharmony_ci	.delay = snd_pcm_rate_delay,
1315d5ac70f0Sopenharmony_ci	.prepare = snd_pcm_rate_prepare,
1316d5ac70f0Sopenharmony_ci	.reset = snd_pcm_rate_reset,
1317d5ac70f0Sopenharmony_ci	.start = snd_pcm_rate_start,
1318d5ac70f0Sopenharmony_ci	.drop = snd_pcm_generic_drop,
1319d5ac70f0Sopenharmony_ci	.drain = snd_pcm_rate_drain,
1320d5ac70f0Sopenharmony_ci	.pause = snd_pcm_generic_pause,
1321d5ac70f0Sopenharmony_ci	.rewindable = snd_pcm_rate_rewindable,
1322d5ac70f0Sopenharmony_ci	.rewind = snd_pcm_rate_rewind,
1323d5ac70f0Sopenharmony_ci	.forwardable = snd_pcm_rate_forwardable,
1324d5ac70f0Sopenharmony_ci	.forward = snd_pcm_rate_forward,
1325d5ac70f0Sopenharmony_ci	.resume = snd_pcm_generic_resume,
1326d5ac70f0Sopenharmony_ci	.writei = snd_pcm_mmap_writei,
1327d5ac70f0Sopenharmony_ci	.writen = snd_pcm_mmap_writen,
1328d5ac70f0Sopenharmony_ci	.readi = snd_pcm_mmap_readi,
1329d5ac70f0Sopenharmony_ci	.readn = snd_pcm_mmap_readn,
1330d5ac70f0Sopenharmony_ci	.avail_update = snd_pcm_rate_avail_update,
1331d5ac70f0Sopenharmony_ci	.mmap_commit = snd_pcm_rate_mmap_commit,
1332d5ac70f0Sopenharmony_ci	.htimestamp = snd_pcm_rate_htimestamp,
1333d5ac70f0Sopenharmony_ci	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
1334d5ac70f0Sopenharmony_ci	.poll_descriptors = snd_pcm_generic_poll_descriptors,
1335d5ac70f0Sopenharmony_ci	.poll_revents = snd_pcm_rate_poll_revents,
1336d5ac70f0Sopenharmony_ci	.may_wait_for_avail_min = snd_pcm_rate_may_wait_for_avail_min,
1337d5ac70f0Sopenharmony_ci};
1338d5ac70f0Sopenharmony_ci
1339d5ac70f0Sopenharmony_cistatic const snd_pcm_ops_t snd_pcm_rate_ops = {
1340d5ac70f0Sopenharmony_ci	.close = snd_pcm_rate_close,
1341d5ac70f0Sopenharmony_ci	.info = snd_pcm_generic_info,
1342d5ac70f0Sopenharmony_ci	.hw_refine = snd_pcm_rate_hw_refine,
1343d5ac70f0Sopenharmony_ci	.hw_params = snd_pcm_rate_hw_params,
1344d5ac70f0Sopenharmony_ci	.hw_free = snd_pcm_rate_hw_free,
1345d5ac70f0Sopenharmony_ci	.sw_params = snd_pcm_rate_sw_params,
1346d5ac70f0Sopenharmony_ci	.channel_info = snd_pcm_generic_channel_info,
1347d5ac70f0Sopenharmony_ci	.dump = snd_pcm_rate_dump,
1348d5ac70f0Sopenharmony_ci	.nonblock = snd_pcm_generic_nonblock,
1349d5ac70f0Sopenharmony_ci	.async = snd_pcm_generic_async,
1350d5ac70f0Sopenharmony_ci	.mmap = snd_pcm_generic_mmap,
1351d5ac70f0Sopenharmony_ci	.munmap = snd_pcm_generic_munmap,
1352d5ac70f0Sopenharmony_ci	.query_chmaps = snd_pcm_generic_query_chmaps,
1353d5ac70f0Sopenharmony_ci	.get_chmap = snd_pcm_generic_get_chmap,
1354d5ac70f0Sopenharmony_ci	.set_chmap = snd_pcm_generic_set_chmap,
1355d5ac70f0Sopenharmony_ci};
1356d5ac70f0Sopenharmony_ci
1357d5ac70f0Sopenharmony_ci/**
1358d5ac70f0Sopenharmony_ci * \brief Get a default converter string
1359d5ac70f0Sopenharmony_ci * \param root Root configuration node
1360d5ac70f0Sopenharmony_ci * \retval A const config item if found, or NULL
1361d5ac70f0Sopenharmony_ci */
1362d5ac70f0Sopenharmony_ciconst snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
1363d5ac70f0Sopenharmony_ci{
1364d5ac70f0Sopenharmony_ci	snd_config_t *n;
1365d5ac70f0Sopenharmony_ci	/* look for default definition */
1366d5ac70f0Sopenharmony_ci	if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
1367d5ac70f0Sopenharmony_ci		return n;
1368d5ac70f0Sopenharmony_ci	return NULL;
1369d5ac70f0Sopenharmony_ci}
1370d5ac70f0Sopenharmony_ci
1371d5ac70f0Sopenharmony_cistatic void rate_initial_setup(snd_pcm_rate_t *rate)
1372d5ac70f0Sopenharmony_ci{
1373d5ac70f0Sopenharmony_ci	if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
1374d5ac70f0Sopenharmony_ci		rate->plugin_version = rate->ops.version;
1375d5ac70f0Sopenharmony_ci
1376d5ac70f0Sopenharmony_ci	if (rate->plugin_version >= 0x010002 &&
1377d5ac70f0Sopenharmony_ci	    rate->ops.get_supported_rates)
1378d5ac70f0Sopenharmony_ci		rate->ops.get_supported_rates(rate->obj,
1379d5ac70f0Sopenharmony_ci					      &rate->rate_min,
1380d5ac70f0Sopenharmony_ci					      &rate->rate_max);
1381d5ac70f0Sopenharmony_ci
1382d5ac70f0Sopenharmony_ci	if (rate->plugin_version >= 0x010003 &&
1383d5ac70f0Sopenharmony_ci	    rate->ops.get_supported_formats) {
1384d5ac70f0Sopenharmony_ci		rate->ops.get_supported_formats(rate->obj,
1385d5ac70f0Sopenharmony_ci						&rate->in_formats,
1386d5ac70f0Sopenharmony_ci						&rate->out_formats,
1387d5ac70f0Sopenharmony_ci						&rate->format_flags);
1388d5ac70f0Sopenharmony_ci	} else if (!rate->ops.convert && rate->ops.convert_s16) {
1389d5ac70f0Sopenharmony_ci		rate->in_formats = rate->out_formats =
1390d5ac70f0Sopenharmony_ci			1ULL << SND_PCM_FORMAT_S16;
1391d5ac70f0Sopenharmony_ci		rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED;
1392d5ac70f0Sopenharmony_ci	}
1393d5ac70f0Sopenharmony_ci}
1394d5ac70f0Sopenharmony_ci
1395d5ac70f0Sopenharmony_ci#ifdef PIC
1396d5ac70f0Sopenharmony_cistatic int is_builtin_plugin(const char *type)
1397d5ac70f0Sopenharmony_ci{
1398d5ac70f0Sopenharmony_ci	return strcmp(type, "linear") == 0;
1399d5ac70f0Sopenharmony_ci}
1400d5ac70f0Sopenharmony_ci
1401d5ac70f0Sopenharmony_cistatic const char *const default_rate_plugins[] = {
1402d5ac70f0Sopenharmony_ci	"speexrate", "linear", NULL
1403d5ac70f0Sopenharmony_ci};
1404d5ac70f0Sopenharmony_ci
1405d5ac70f0Sopenharmony_cistatic int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose)
1406d5ac70f0Sopenharmony_ci{
1407d5ac70f0Sopenharmony_ci	char open_name[64], open_conf_name[64], lib_name[64], *lib = NULL;
1408d5ac70f0Sopenharmony_ci	snd_pcm_rate_open_func_t open_func;
1409d5ac70f0Sopenharmony_ci	snd_pcm_rate_open_conf_func_t open_conf_func;
1410d5ac70f0Sopenharmony_ci	int err;
1411d5ac70f0Sopenharmony_ci
1412d5ac70f0Sopenharmony_ci	snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
1413d5ac70f0Sopenharmony_ci	snprintf(open_conf_name, sizeof(open_conf_name), "_snd_pcm_rate_%s_open_conf", type);
1414d5ac70f0Sopenharmony_ci	if (!is_builtin_plugin(type)) {
1415d5ac70f0Sopenharmony_ci		snprintf(lib_name, sizeof(lib_name),
1416d5ac70f0Sopenharmony_ci				 "libasound_module_rate_%s.so", type);
1417d5ac70f0Sopenharmony_ci		lib = lib_name;
1418d5ac70f0Sopenharmony_ci	}
1419d5ac70f0Sopenharmony_ci
1420d5ac70f0Sopenharmony_ci	open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL);
1421d5ac70f0Sopenharmony_ci	if (open_conf_func) {
1422d5ac70f0Sopenharmony_ci		err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION,
1423d5ac70f0Sopenharmony_ci				     &rate->obj, &rate->ops, converter_conf);
1424d5ac70f0Sopenharmony_ci		if (!err) {
1425d5ac70f0Sopenharmony_ci			rate->open_func = open_conf_func;
1426d5ac70f0Sopenharmony_ci			return 0;
1427d5ac70f0Sopenharmony_ci		} else {
1428d5ac70f0Sopenharmony_ci			snd_dlobj_cache_put(open_conf_func);
1429d5ac70f0Sopenharmony_ci			return err;
1430d5ac70f0Sopenharmony_ci		}
1431d5ac70f0Sopenharmony_ci	}
1432d5ac70f0Sopenharmony_ci
1433d5ac70f0Sopenharmony_ci	open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
1434d5ac70f0Sopenharmony_ci	if (!open_func)
1435d5ac70f0Sopenharmony_ci		return -ENOENT;
1436d5ac70f0Sopenharmony_ci
1437d5ac70f0Sopenharmony_ci	rate->open_func = open_func;
1438d5ac70f0Sopenharmony_ci
1439d5ac70f0Sopenharmony_ci	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1440d5ac70f0Sopenharmony_ci	if (!err)
1441d5ac70f0Sopenharmony_ci		return 0;
1442d5ac70f0Sopenharmony_ci
1443d5ac70f0Sopenharmony_ci	/* try to open with the old protocol version */
1444d5ac70f0Sopenharmony_ci	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
1445d5ac70f0Sopenharmony_ci	err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
1446d5ac70f0Sopenharmony_ci			&rate->obj, &rate->ops);
1447d5ac70f0Sopenharmony_ci	if (!err)
1448d5ac70f0Sopenharmony_ci		return 0;
1449d5ac70f0Sopenharmony_ci
1450d5ac70f0Sopenharmony_ci	snd_dlobj_cache_put(open_func);
1451d5ac70f0Sopenharmony_ci	rate->open_func = NULL;
1452d5ac70f0Sopenharmony_ci	return err;
1453d5ac70f0Sopenharmony_ci}
1454d5ac70f0Sopenharmony_ci#endif
1455d5ac70f0Sopenharmony_ci
1456d5ac70f0Sopenharmony_ci/*
1457d5ac70f0Sopenharmony_ci * If the conf is an array of alternatives then the id of
1458d5ac70f0Sopenharmony_ci * the first element will be "0" (or maybe NULL). Otherwise assume it is
1459d5ac70f0Sopenharmony_ci * a structure.
1460d5ac70f0Sopenharmony_ci */
1461d5ac70f0Sopenharmony_cistatic int is_string_array(const snd_config_t *conf)
1462d5ac70f0Sopenharmony_ci{
1463d5ac70f0Sopenharmony_ci	snd_config_iterator_t i;
1464d5ac70f0Sopenharmony_ci
1465d5ac70f0Sopenharmony_ci	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
1466d5ac70f0Sopenharmony_ci		return 0;
1467d5ac70f0Sopenharmony_ci
1468d5ac70f0Sopenharmony_ci	i = snd_config_iterator_first(conf);
1469d5ac70f0Sopenharmony_ci	if (i && i != snd_config_iterator_end(conf)) {
1470d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
1471d5ac70f0Sopenharmony_ci		const char *id;
1472d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1473d5ac70f0Sopenharmony_ci			return 0;
1474d5ac70f0Sopenharmony_ci		if (id && strcmp(id, "0") != 0)
1475d5ac70f0Sopenharmony_ci			return 0;
1476d5ac70f0Sopenharmony_ci	}
1477d5ac70f0Sopenharmony_ci
1478d5ac70f0Sopenharmony_ci	return 1;
1479d5ac70f0Sopenharmony_ci}
1480d5ac70f0Sopenharmony_ci
1481d5ac70f0Sopenharmony_ci/**
1482d5ac70f0Sopenharmony_ci * \brief Creates a new rate PCM
1483d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle
1484d5ac70f0Sopenharmony_ci * \param name Name of PCM
1485d5ac70f0Sopenharmony_ci * \param sformat Slave format
1486d5ac70f0Sopenharmony_ci * \param srate Slave rate
1487d5ac70f0Sopenharmony_ci * \param converter SRC type string node
1488d5ac70f0Sopenharmony_ci * \param slave Slave PCM handle
1489d5ac70f0Sopenharmony_ci * \param close_slave When set, the slave PCM handle is closed with copy PCM
1490d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1491d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
1492d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
1493d5ac70f0Sopenharmony_ci *          changed in future.
1494d5ac70f0Sopenharmony_ci */
1495d5ac70f0Sopenharmony_ciint snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1496d5ac70f0Sopenharmony_ci		      snd_pcm_format_t sformat, unsigned int srate,
1497d5ac70f0Sopenharmony_ci		      const snd_config_t *converter,
1498d5ac70f0Sopenharmony_ci		      snd_pcm_t *slave, int close_slave)
1499d5ac70f0Sopenharmony_ci{
1500d5ac70f0Sopenharmony_ci	snd_pcm_t *pcm;
1501d5ac70f0Sopenharmony_ci	snd_pcm_rate_t *rate;
1502d5ac70f0Sopenharmony_ci	const char *type = NULL;
1503d5ac70f0Sopenharmony_ci	int err;
1504d5ac70f0Sopenharmony_ci#ifndef PIC
1505d5ac70f0Sopenharmony_ci	snd_pcm_rate_open_func_t open_func;
1506d5ac70f0Sopenharmony_ci	extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops);
1507d5ac70f0Sopenharmony_ci#endif
1508d5ac70f0Sopenharmony_ci
1509d5ac70f0Sopenharmony_ci	assert(pcmp && slave);
1510d5ac70f0Sopenharmony_ci	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1511d5ac70f0Sopenharmony_ci	    snd_pcm_format_linear(sformat) != 1)
1512d5ac70f0Sopenharmony_ci		return -EINVAL;
1513d5ac70f0Sopenharmony_ci	rate = calloc(1, sizeof(snd_pcm_rate_t));
1514d5ac70f0Sopenharmony_ci	if (!rate) {
1515d5ac70f0Sopenharmony_ci		return -ENOMEM;
1516d5ac70f0Sopenharmony_ci	}
1517d5ac70f0Sopenharmony_ci	rate->gen.slave = slave;
1518d5ac70f0Sopenharmony_ci	rate->gen.close_slave = close_slave;
1519d5ac70f0Sopenharmony_ci	rate->srate = srate;
1520d5ac70f0Sopenharmony_ci	rate->sformat = sformat;
1521d5ac70f0Sopenharmony_ci
1522d5ac70f0Sopenharmony_ci	rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
1523d5ac70f0Sopenharmony_ci	rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
1524d5ac70f0Sopenharmony_ci	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
1525d5ac70f0Sopenharmony_ci
1526d5ac70f0Sopenharmony_ci	err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
1527d5ac70f0Sopenharmony_ci	if (err < 0) {
1528d5ac70f0Sopenharmony_ci		free(rate);
1529d5ac70f0Sopenharmony_ci		return err;
1530d5ac70f0Sopenharmony_ci	}
1531d5ac70f0Sopenharmony_ci
1532d5ac70f0Sopenharmony_ci#ifdef PIC
1533d5ac70f0Sopenharmony_ci	err = -ENOENT;
1534d5ac70f0Sopenharmony_ci	if (!converter) {
1535d5ac70f0Sopenharmony_ci		const char *const *types;
1536d5ac70f0Sopenharmony_ci		for (types = default_rate_plugins; *types; types++) {
1537d5ac70f0Sopenharmony_ci			err = rate_open_func(rate, *types, NULL, 0);
1538d5ac70f0Sopenharmony_ci			if (!err) {
1539d5ac70f0Sopenharmony_ci				type = *types;
1540d5ac70f0Sopenharmony_ci				break;
1541d5ac70f0Sopenharmony_ci			}
1542d5ac70f0Sopenharmony_ci		}
1543d5ac70f0Sopenharmony_ci	} else if (!snd_config_get_string(converter, &type))
1544d5ac70f0Sopenharmony_ci		err = rate_open_func(rate, type, NULL, 1);
1545d5ac70f0Sopenharmony_ci	else if (is_string_array(converter)) {
1546d5ac70f0Sopenharmony_ci		snd_config_iterator_t i, next;
1547d5ac70f0Sopenharmony_ci		snd_config_for_each(i, next, converter) {
1548d5ac70f0Sopenharmony_ci			snd_config_t *n = snd_config_iterator_entry(i);
1549d5ac70f0Sopenharmony_ci			if (snd_config_get_string(n, &type) < 0)
1550d5ac70f0Sopenharmony_ci				break;
1551d5ac70f0Sopenharmony_ci			err = rate_open_func(rate, type, NULL, 0);
1552d5ac70f0Sopenharmony_ci			if (!err)
1553d5ac70f0Sopenharmony_ci				break;
1554d5ac70f0Sopenharmony_ci		}
1555d5ac70f0Sopenharmony_ci	} else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) {
1556d5ac70f0Sopenharmony_ci		snd_config_iterator_t i, next;
1557d5ac70f0Sopenharmony_ci		snd_config_for_each(i, next, converter) {
1558d5ac70f0Sopenharmony_ci			snd_config_t *n = snd_config_iterator_entry(i);
1559d5ac70f0Sopenharmony_ci			const char *id;
1560d5ac70f0Sopenharmony_ci			if (snd_config_get_id(n, &id) < 0)
1561d5ac70f0Sopenharmony_ci				continue;
1562d5ac70f0Sopenharmony_ci			if (strcmp(id, "name") != 0)
1563d5ac70f0Sopenharmony_ci				continue;
1564d5ac70f0Sopenharmony_ci			snd_config_get_string(n, &type);
1565d5ac70f0Sopenharmony_ci			break;
1566d5ac70f0Sopenharmony_ci		}
1567d5ac70f0Sopenharmony_ci		if (!type) {
1568d5ac70f0Sopenharmony_ci			SNDERR("No name given for rate converter");
1569d5ac70f0Sopenharmony_ci			snd_pcm_free(pcm);
1570d5ac70f0Sopenharmony_ci			free(rate);
1571d5ac70f0Sopenharmony_ci			return -EINVAL;
1572d5ac70f0Sopenharmony_ci		}
1573d5ac70f0Sopenharmony_ci		err = rate_open_func(rate, type, converter, 1);
1574d5ac70f0Sopenharmony_ci	} else {
1575d5ac70f0Sopenharmony_ci		SNDERR("Invalid type for rate converter");
1576d5ac70f0Sopenharmony_ci		snd_pcm_free(pcm);
1577d5ac70f0Sopenharmony_ci		free(rate);
1578d5ac70f0Sopenharmony_ci		return -EINVAL;
1579d5ac70f0Sopenharmony_ci	}
1580d5ac70f0Sopenharmony_ci	if (err < 0) {
1581d5ac70f0Sopenharmony_ci		SNDERR("Cannot find rate converter");
1582d5ac70f0Sopenharmony_ci		snd_pcm_free(pcm);
1583d5ac70f0Sopenharmony_ci		free(rate);
1584d5ac70f0Sopenharmony_ci		return -ENOENT;
1585d5ac70f0Sopenharmony_ci	}
1586d5ac70f0Sopenharmony_ci#else
1587d5ac70f0Sopenharmony_ci	type = "linear";
1588d5ac70f0Sopenharmony_ci	open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
1589d5ac70f0Sopenharmony_ci	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1590d5ac70f0Sopenharmony_ci	if (err < 0) {
1591d5ac70f0Sopenharmony_ci		snd_pcm_free(pcm);
1592d5ac70f0Sopenharmony_ci		free(rate);
1593d5ac70f0Sopenharmony_ci		return err;
1594d5ac70f0Sopenharmony_ci	}
1595d5ac70f0Sopenharmony_ci#endif
1596d5ac70f0Sopenharmony_ci
1597d5ac70f0Sopenharmony_ci	if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) ||
1598d5ac70f0Sopenharmony_ci	    ! rate->ops.input_frames || ! rate->ops.output_frames) {
1599d5ac70f0Sopenharmony_ci		SNDERR("Inproper rate plugin %s initialization", type);
1600d5ac70f0Sopenharmony_ci		snd_pcm_free(pcm);
1601d5ac70f0Sopenharmony_ci		free(rate);
1602d5ac70f0Sopenharmony_ci		return err;
1603d5ac70f0Sopenharmony_ci	}
1604d5ac70f0Sopenharmony_ci
1605d5ac70f0Sopenharmony_ci	rate_initial_setup(rate);
1606d5ac70f0Sopenharmony_ci
1607d5ac70f0Sopenharmony_ci	pcm->ops = &snd_pcm_rate_ops;
1608d5ac70f0Sopenharmony_ci	pcm->fast_ops = &snd_pcm_rate_fast_ops;
1609d5ac70f0Sopenharmony_ci	pcm->private_data = rate;
1610d5ac70f0Sopenharmony_ci	pcm->poll_fd = slave->poll_fd;
1611d5ac70f0Sopenharmony_ci	pcm->poll_events = slave->poll_events;
1612d5ac70f0Sopenharmony_ci	pcm->mmap_rw = 1;
1613d5ac70f0Sopenharmony_ci	pcm->tstamp_type = slave->tstamp_type;
1614d5ac70f0Sopenharmony_ci	snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0);
1615d5ac70f0Sopenharmony_ci	snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0);
1616d5ac70f0Sopenharmony_ci	*pcmp = pcm;
1617d5ac70f0Sopenharmony_ci
1618d5ac70f0Sopenharmony_ci	return 0;
1619d5ac70f0Sopenharmony_ci}
1620d5ac70f0Sopenharmony_ci
1621d5ac70f0Sopenharmony_ci/*! \page pcm_plugins
1622d5ac70f0Sopenharmony_ci
1623d5ac70f0Sopenharmony_ci\section pcm_plugins_rate Plugin: Rate
1624d5ac70f0Sopenharmony_ci
1625d5ac70f0Sopenharmony_ciThis plugin converts a stream rate. The input and output formats must be linear.
1626d5ac70f0Sopenharmony_ci
1627d5ac70f0Sopenharmony_ci\code
1628d5ac70f0Sopenharmony_cipcm.name {
1629d5ac70f0Sopenharmony_ci	type rate               # Rate PCM
1630d5ac70f0Sopenharmony_ci        slave STR               # Slave name
1631d5ac70f0Sopenharmony_ci        # or
1632d5ac70f0Sopenharmony_ci        slave {                 # Slave definition
1633d5ac70f0Sopenharmony_ci                pcm STR         # Slave PCM name
1634d5ac70f0Sopenharmony_ci                # or
1635d5ac70f0Sopenharmony_ci                pcm { }         # Slave PCM definition
1636d5ac70f0Sopenharmony_ci                rate INT        # Slave rate
1637d5ac70f0Sopenharmony_ci                [format STR]    # Slave format
1638d5ac70f0Sopenharmony_ci        }
1639d5ac70f0Sopenharmony_ci	converter STR			# optional
1640d5ac70f0Sopenharmony_ci	# or
1641d5ac70f0Sopenharmony_ci	converter [ STR1 STR2 ... ]	# optional
1642d5ac70f0Sopenharmony_ci				# Converter type, default is taken from
1643d5ac70f0Sopenharmony_ci				# defaults.pcm.rate_converter
1644d5ac70f0Sopenharmony_ci	# or
1645d5ac70f0Sopenharmony_ci	converter {		# optional
1646d5ac70f0Sopenharmony_ci		name STR	# Convertor type
1647d5ac70f0Sopenharmony_ci		xxx yyy		# optional convertor-specific configuration
1648d5ac70f0Sopenharmony_ci	}
1649d5ac70f0Sopenharmony_ci}
1650d5ac70f0Sopenharmony_ci\endcode
1651d5ac70f0Sopenharmony_ci
1652d5ac70f0Sopenharmony_ci\subsection pcm_plugins_rate_funcref Function reference
1653d5ac70f0Sopenharmony_ci
1654d5ac70f0Sopenharmony_ci<UL>
1655d5ac70f0Sopenharmony_ci  <LI>snd_pcm_rate_open()
1656d5ac70f0Sopenharmony_ci  <LI>_snd_pcm_rate_open()
1657d5ac70f0Sopenharmony_ci</UL>
1658d5ac70f0Sopenharmony_ci
1659d5ac70f0Sopenharmony_ci*/
1660d5ac70f0Sopenharmony_ci
1661d5ac70f0Sopenharmony_ci/**
1662d5ac70f0Sopenharmony_ci * \brief Creates a new rate PCM
1663d5ac70f0Sopenharmony_ci * \param pcmp Returns created PCM handle
1664d5ac70f0Sopenharmony_ci * \param name Name of PCM
1665d5ac70f0Sopenharmony_ci * \param root Root configuration node
1666d5ac70f0Sopenharmony_ci * \param conf Configuration node with rate PCM description
1667d5ac70f0Sopenharmony_ci * \param stream Stream type
1668d5ac70f0Sopenharmony_ci * \param mode Stream mode
1669d5ac70f0Sopenharmony_ci * \retval zero on success otherwise a negative error code
1670d5ac70f0Sopenharmony_ci * \warning Using of this function might be dangerous in the sense
1671d5ac70f0Sopenharmony_ci *          of compatibility reasons. The prototype might be freely
1672d5ac70f0Sopenharmony_ci *          changed in future.
1673d5ac70f0Sopenharmony_ci */
1674d5ac70f0Sopenharmony_ciint _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1675d5ac70f0Sopenharmony_ci		       snd_config_t *root, snd_config_t *conf,
1676d5ac70f0Sopenharmony_ci		       snd_pcm_stream_t stream, int mode)
1677d5ac70f0Sopenharmony_ci{
1678d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
1679d5ac70f0Sopenharmony_ci	int err;
1680d5ac70f0Sopenharmony_ci	snd_pcm_t *spcm;
1681d5ac70f0Sopenharmony_ci	snd_config_t *slave = NULL, *sconf;
1682d5ac70f0Sopenharmony_ci	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1683d5ac70f0Sopenharmony_ci	int srate = -1;
1684d5ac70f0Sopenharmony_ci	const snd_config_t *converter = NULL;
1685d5ac70f0Sopenharmony_ci
1686d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
1687d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
1688d5ac70f0Sopenharmony_ci		const char *id;
1689d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
1690d5ac70f0Sopenharmony_ci			continue;
1691d5ac70f0Sopenharmony_ci		if (snd_pcm_conf_generic_id(id))
1692d5ac70f0Sopenharmony_ci			continue;
1693d5ac70f0Sopenharmony_ci		if (strcmp(id, "slave") == 0) {
1694d5ac70f0Sopenharmony_ci			slave = n;
1695d5ac70f0Sopenharmony_ci			continue;
1696d5ac70f0Sopenharmony_ci		}
1697d5ac70f0Sopenharmony_ci		if (strcmp(id, "converter") == 0) {
1698d5ac70f0Sopenharmony_ci			converter = n;
1699d5ac70f0Sopenharmony_ci			continue;
1700d5ac70f0Sopenharmony_ci		}
1701d5ac70f0Sopenharmony_ci		SNDERR("Unknown field %s", id);
1702d5ac70f0Sopenharmony_ci		return -EINVAL;
1703d5ac70f0Sopenharmony_ci	}
1704d5ac70f0Sopenharmony_ci	if (!slave) {
1705d5ac70f0Sopenharmony_ci		SNDERR("slave is not defined");
1706d5ac70f0Sopenharmony_ci		return -EINVAL;
1707d5ac70f0Sopenharmony_ci	}
1708d5ac70f0Sopenharmony_ci
1709d5ac70f0Sopenharmony_ci	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1710d5ac70f0Sopenharmony_ci				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1711d5ac70f0Sopenharmony_ci				 SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate);
1712d5ac70f0Sopenharmony_ci	if (err < 0)
1713d5ac70f0Sopenharmony_ci		return err;
1714d5ac70f0Sopenharmony_ci	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1715d5ac70f0Sopenharmony_ci	    snd_pcm_format_linear(sformat) != 1) {
1716d5ac70f0Sopenharmony_ci	    	snd_config_delete(sconf);
1717d5ac70f0Sopenharmony_ci		SNDERR("slave format is not linear");
1718d5ac70f0Sopenharmony_ci		return -EINVAL;
1719d5ac70f0Sopenharmony_ci	}
1720d5ac70f0Sopenharmony_ci	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1721d5ac70f0Sopenharmony_ci	snd_config_delete(sconf);
1722d5ac70f0Sopenharmony_ci	if (err < 0)
1723d5ac70f0Sopenharmony_ci		return err;
1724d5ac70f0Sopenharmony_ci	err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
1725d5ac70f0Sopenharmony_ci				converter, spcm, 1);
1726d5ac70f0Sopenharmony_ci	if (err < 0)
1727d5ac70f0Sopenharmony_ci		snd_pcm_close(spcm);
1728d5ac70f0Sopenharmony_ci	return err;
1729d5ac70f0Sopenharmony_ci}
1730d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
1731d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
1732d5ac70f0Sopenharmony_ci#endif
1733