• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file pcm/pcm_iec958.c
3  * \ingroup PCM_Plugins
4  * \brief PCM IEC958 Subframe Conversion Plugin Interface
5  * \author Takashi Iwai <tiwai@suse.de>
6  * \date 2004
7  */
8 /*
9  *  PCM - IEC958 Subframe Conversion Plugin
10  *  Copyright (c) 2004 by Takashi Iwai <tiwai@suse.de>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
26  *
27  */
28 
29 #include "bswap.h"
30 #include "pcm_local.h"
31 #include "pcm_plugin.h"
32 
33 #include "plugin_ops.h"
34 
35 #ifndef PIC
36 /* entry for static linking */
37 const char *_snd_module_pcm_iec958 = "";
38 #endif
39 
40 /*
41  */
42 
43 #ifndef DOC_HIDDEN
44 
45 typedef struct snd_pcm_iec958 snd_pcm_iec958_t;
46 
47 typedef void (*iec958_f)(snd_pcm_iec958_t *iec,
48 			 const snd_pcm_channel_area_t *dst_areas,
49 			 snd_pcm_uframes_t dst_offset,
50 			 const snd_pcm_channel_area_t *src_areas,
51 			 snd_pcm_uframes_t src_offset,
52 			 unsigned int channels, snd_pcm_uframes_t frames);
53 
54 struct snd_pcm_iec958 {
55 	/* This field need to be the first */
56 	snd_pcm_plugin_t plug;
57 	unsigned int getput_idx;
58 	iec958_f func;
59 	snd_pcm_format_t sformat;
60 	snd_pcm_format_t format;
61 	unsigned int counter;
62 	unsigned char status[24];
63 	unsigned int byteswap;
64 	unsigned char preamble[3];	/* B/M/W or Z/X/Y */
65 	snd_pcm_fast_ops_t fops;
66 	int hdmi_mode;
67 };
68 
69 enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y };
70 
71 #endif /* DOC_HIDDEN */
72 
73 /*
74  * Determine parity for time slots 4 upto 30
75  * to be sure that bit 4 upt 31 will carry
76  * an even number of ones and zeros.
77  */
iec958_parity(unsigned int data)78 static unsigned int iec958_parity(unsigned int data)
79 {
80 	unsigned int parity;
81 	int bit;
82 
83 	data >>= 4;     /* start from bit 4 */
84 	parity = 0;
85 	for (bit = 4; bit <= 30; bit++) {
86 		if (data & 1)
87 			parity++;
88 		data >>= 1;
89 	}
90 	return (parity & 1);
91 }
92 
93 /*
94  * Compose 32bit IEC958 subframe, two sub frames
95  * build one frame with two channels.
96  *
97  * bit 0-3  = preamble
98  *     4-7  = AUX (=0)
99  *     8-27 = data (12-27 for 16bit, 8-27 for 20bit, and 24bit without AUX)
100  *     28   = validity (0 for valid data, else 'in error')
101  *     29   = user data (0)
102  *     30   = channel status (24 bytes for 192 frames)
103  *     31   = parity
104  */
105 
iec958_subframe(snd_pcm_iec958_t * iec,uint32_t data,int channel)106 static inline uint32_t iec958_subframe(snd_pcm_iec958_t *iec, uint32_t data, int channel)
107 {
108 	unsigned int byte = iec->counter >> 3;
109 	unsigned int mask = 1 << (iec->counter - (byte << 3));
110 
111 	/* bit 4-27 */
112 	data >>= 4;
113 	data &= ~0xf;
114 
115 	/* set IEC status bits (up to 192 bits) */
116 	if (iec->status[byte] & mask)
117 		data |= 0x40000000;
118 
119 	if (iec958_parity(data))	/* parity bit 4-30 */
120 		data |= 0x80000000;
121 
122 	/* Preamble */
123 	if (channel)
124 		data |= iec->preamble[PREAMBLE_Y];	/* odd sub frame, 'Y' */
125 	else if (! iec->counter)
126 		data |= iec->preamble[PREAMBLE_Z];	/* Block start, 'Z' */
127 	else
128 		data |= iec->preamble[PREAMBLE_X];	/* even sub frame, 'X' */
129 
130 	if (iec->byteswap)
131 		data = bswap_32(data);
132 
133 	return data;
134 }
135 
iec958_to_s32(snd_pcm_iec958_t * iec,uint32_t data)136 static inline int32_t iec958_to_s32(snd_pcm_iec958_t *iec, uint32_t data)
137 {
138 	if (iec->byteswap)
139 		data = bswap_32(data);
140 	data &= ~0xf;
141 	data <<= 4;
142 	return (int32_t)data;
143 }
144 
145 #ifndef DOC_HIDDEN
snd_pcm_iec958_decode(snd_pcm_iec958_t * iec,const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int channels,snd_pcm_uframes_t frames)146 static void snd_pcm_iec958_decode(snd_pcm_iec958_t *iec,
147 				  const snd_pcm_channel_area_t *dst_areas,
148 				  snd_pcm_uframes_t dst_offset,
149 				  const snd_pcm_channel_area_t *src_areas,
150 				  snd_pcm_uframes_t src_offset,
151 				  unsigned int channels, snd_pcm_uframes_t frames)
152 {
153 #define PUT32_LABELS
154 #include "plugin_ops.h"
155 #undef PUT32_LABELS
156 	void *put = put32_labels[iec->getput_idx];
157 	unsigned int channel;
158 	for (channel = 0; channel < channels; ++channel) {
159 		const uint32_t *src;
160 		char *dst;
161 		int src_step, dst_step;
162 		snd_pcm_uframes_t frames1;
163 		const snd_pcm_channel_area_t *src_area = &src_areas[channel];
164 		const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
165 		src = snd_pcm_channel_area_addr(src_area, src_offset);
166 		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
167 		src_step = snd_pcm_channel_area_step(src_area) / sizeof(uint32_t);
168 		dst_step = snd_pcm_channel_area_step(dst_area);
169 		frames1 = frames;
170 		while (frames1-- > 0) {
171 			int32_t sample = iec958_to_s32(iec, *src);
172 			goto *put;
173 #define PUT32_END after
174 #include "plugin_ops.h"
175 #undef PUT32_END
176 		after:
177 			src += src_step;
178 			dst += dst_step;
179 		}
180 	}
181 }
182 
snd_pcm_iec958_encode(snd_pcm_iec958_t * iec,const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int channels,snd_pcm_uframes_t frames)183 static void snd_pcm_iec958_encode(snd_pcm_iec958_t *iec,
184 				  const snd_pcm_channel_area_t *dst_areas,
185 				  snd_pcm_uframes_t dst_offset,
186 				  const snd_pcm_channel_area_t *src_areas,
187 				  snd_pcm_uframes_t src_offset,
188 				  unsigned int channels, snd_pcm_uframes_t frames)
189 {
190 #define GET32_LABELS
191 #include "plugin_ops.h"
192 #undef GET32_LABELS
193 	void *get = get32_labels[iec->getput_idx];
194 	unsigned int channel;
195 	int32_t sample = 0;
196 	int counter = iec->counter;
197 	int single_stream = iec->hdmi_mode &&
198 			    (iec->status[0] & IEC958_AES0_NONAUDIO) &&
199 			    (channels == 8);
200 	int counter_step = single_stream ? ((channels + 1) >> 1) : 1;
201 	for (channel = 0; channel < channels; ++channel) {
202 		const char *src;
203 		uint32_t *dst;
204 		int src_step, dst_step;
205 		snd_pcm_uframes_t frames1;
206 		const snd_pcm_channel_area_t *src_area = &src_areas[channel];
207 		const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
208 		src = snd_pcm_channel_area_addr(src_area, src_offset);
209 		dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
210 		src_step = snd_pcm_channel_area_step(src_area);
211 		dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(uint32_t);
212 		frames1 = frames;
213 
214 		if (single_stream)
215 			iec->counter = (counter + (channel >> 1)) % 192;
216 		else
217 			iec->counter = counter;
218 
219 		while (frames1-- > 0) {
220 			goto *get;
221 #define GET32_END after
222 #include "plugin_ops.h"
223 #undef GET32_END
224 		after:
225 			sample = iec958_subframe(iec, sample, channel);
226 			// fprintf(stderr, "%d:%08x\n", frames1, sample);
227 			*dst = sample;
228 			src += src_step;
229 			dst += dst_step;
230 			iec->counter += counter_step;
231 			iec->counter %= 192;
232 		}
233 		if (single_stream) /* set counter to ch0 value for next iteration */
234 			iec->counter = (counter + frames * counter_step) % 192;
235 	}
236 }
237 #endif /* DOC_HIDDEN */
238 
snd_pcm_iec958_hw_refine_cprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)239 static int snd_pcm_iec958_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
240 {
241 	snd_pcm_iec958_t *iec = pcm->private_data;
242 	int err;
243 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
244 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
245 					 &access_mask);
246 	if (err < 0)
247 		return err;
248 	if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
249 	    iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
250 		snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
251 		err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
252 						 &format_mask);
253 	} else {
254 		snd_pcm_format_mask_t format_mask = {
255 			{ (1U << SND_PCM_FORMAT_IEC958_SUBFRAME_LE) |
256 			  (1U << SND_PCM_FORMAT_IEC958_SUBFRAME_BE) }
257 		};
258 		err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
259 						 &format_mask);
260 	}
261 	if (err < 0)
262 		return err;
263 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
264 	if (err < 0)
265 		return err;
266 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
267 	return 0;
268 }
269 
snd_pcm_iec958_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)270 static int snd_pcm_iec958_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
271 {
272 	snd_pcm_iec958_t *iec = pcm->private_data;
273 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
274 	_snd_pcm_hw_params_any(sparams);
275 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
276 				   &saccess_mask);
277 	_snd_pcm_hw_params_set_format(sparams, iec->sformat);
278 	_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
279 	return 0;
280 }
281 
snd_pcm_iec958_hw_refine_schange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)282 static int snd_pcm_iec958_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
283 					    snd_pcm_hw_params_t *sparams)
284 {
285 	int err;
286 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
287 			      SND_PCM_HW_PARBIT_RATE |
288 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
289 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
290 			      SND_PCM_HW_PARBIT_PERIODS |
291 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
292 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
293 			      SND_PCM_HW_PARBIT_TICK_TIME);
294 	err = _snd_pcm_hw_params_refine(sparams, links, params);
295 	if (err < 0)
296 		return err;
297 	return 0;
298 }
299 
snd_pcm_iec958_hw_refine_cchange(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)300 static int snd_pcm_iec958_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
301 					    snd_pcm_hw_params_t *sparams)
302 {
303 	int err;
304 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
305 			      SND_PCM_HW_PARBIT_RATE |
306 			      SND_PCM_HW_PARBIT_PERIOD_SIZE |
307 			      SND_PCM_HW_PARBIT_BUFFER_SIZE |
308 			      SND_PCM_HW_PARBIT_PERIODS |
309 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
310 			      SND_PCM_HW_PARBIT_BUFFER_TIME |
311 			      SND_PCM_HW_PARBIT_TICK_TIME);
312 	err = _snd_pcm_hw_params_refine(params, links, sparams);
313 	if (err < 0)
314 		return err;
315 	return 0;
316 }
317 
snd_pcm_iec958_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)318 static int snd_pcm_iec958_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
319 {
320 	return snd_pcm_hw_refine_slave(pcm, params,
321 				       snd_pcm_iec958_hw_refine_cprepare,
322 				       snd_pcm_iec958_hw_refine_cchange,
323 				       snd_pcm_iec958_hw_refine_sprepare,
324 				       snd_pcm_iec958_hw_refine_schange,
325 				       snd_pcm_generic_hw_refine);
326 }
327 
snd_pcm_iec958_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)328 static int snd_pcm_iec958_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
329 {
330 	snd_pcm_iec958_t *iec = pcm->private_data;
331 	snd_pcm_format_t format;
332 	int err = snd_pcm_hw_params_slave(pcm, params,
333 					  snd_pcm_iec958_hw_refine_cchange,
334 					  snd_pcm_iec958_hw_refine_sprepare,
335 					  snd_pcm_iec958_hw_refine_schange,
336 					  snd_pcm_generic_hw_params);
337 	if (err < 0)
338 		return err;
339 
340 	err = INTERNAL(snd_pcm_hw_params_get_format)(params, &format);
341 	if (err < 0)
342 		return err;
343 
344 	iec->format = format;
345 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
346 		if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
347 		    iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
348 			iec->getput_idx = snd_pcm_linear_get_index(format, SND_PCM_FORMAT_S32);
349 			iec->func = snd_pcm_iec958_encode;
350 			iec->byteswap = iec->sformat != SND_PCM_FORMAT_IEC958_SUBFRAME;
351 		} else {
352 			iec->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, iec->sformat);
353 			iec->func = snd_pcm_iec958_decode;
354 			iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME;
355 		}
356 	} else {
357 		if (iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_LE ||
358 		    iec->sformat == SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
359 			iec->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S32, format);
360 			iec->func = snd_pcm_iec958_decode;
361 			iec->byteswap = iec->sformat != SND_PCM_FORMAT_IEC958_SUBFRAME;
362 		} else {
363 			iec->getput_idx = snd_pcm_linear_get_index(iec->sformat, SND_PCM_FORMAT_S32);
364 			iec->func = snd_pcm_iec958_encode;
365 			iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME;
366 		}
367 	}
368 
369 	if ((iec->status[0] & IEC958_AES0_PROFESSIONAL) == 0) {
370 		if ((iec->status[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
371 			unsigned int rate = 0;
372 			unsigned char fs;
373 
374 			err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &rate, 0);
375 			if (err < 0)
376 				rate = 0;
377 
378 			switch (rate) {
379 			case 22050:
380 				fs = IEC958_AES3_CON_FS_22050;
381 				break;
382 			case 24000:
383 				fs = IEC958_AES3_CON_FS_24000;
384 				break;
385 			case 32000:
386 				fs = IEC958_AES3_CON_FS_32000;
387 				break;
388 			case 44100:
389 				fs = IEC958_AES3_CON_FS_44100;
390 				break;
391 			case 48000:
392 				fs = IEC958_AES3_CON_FS_48000;
393 				break;
394 			case 88200:
395 				fs = IEC958_AES3_CON_FS_88200;
396 				break;
397 			case 96000:
398 				fs = IEC958_AES3_CON_FS_96000;
399 				break;
400 			case 176400:
401 				fs = IEC958_AES3_CON_FS_176400;
402 				break;
403 			case 192000:
404 				fs = IEC958_AES3_CON_FS_192000;
405 				break;
406 			case 768000:
407 				fs = IEC958_AES3_CON_FS_768000;
408 				break;
409 			default:
410 				fs = IEC958_AES3_CON_FS_NOTID;
411 				break;
412 			}
413 
414 			iec->status[3] &= ~IEC958_AES3_CON_FS;
415 			iec->status[3] |= fs;
416 		}
417 
418 		if ((iec->status[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
419 			unsigned char ws;
420 			switch (snd_pcm_format_width(format)) {
421 			case 16:
422 				ws = IEC958_AES4_CON_WORDLEN_20_16;
423 				break;
424 			case 18:
425 				ws = IEC958_AES4_CON_WORDLEN_22_18;
426 				break;
427 			case 20:
428 				ws = IEC958_AES4_CON_WORDLEN_20_16 | IEC958_AES4_CON_MAX_WORDLEN_24;
429 				break;
430 			case 24:
431 			case 32: /* Assume 24-bit width for 32-bit samples. */
432 				ws = IEC958_AES4_CON_WORDLEN_24_20 | IEC958_AES4_CON_MAX_WORDLEN_24;
433 				break;
434 			default:
435 				ws = IEC958_AES4_CON_WORDLEN_NOTID;
436 				break;
437 			}
438 			iec->status[4] &= ~(IEC958_AES4_CON_MAX_WORDLEN_24 | IEC958_AES4_CON_WORDLEN);
439 			iec->status[4] |= ws;
440 		}
441 	}
442 	return 0;
443 }
444 
445 static snd_pcm_uframes_t
snd_pcm_iec958_write_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)446 snd_pcm_iec958_write_areas(snd_pcm_t *pcm,
447 			   const snd_pcm_channel_area_t *areas,
448 			   snd_pcm_uframes_t offset,
449 			   snd_pcm_uframes_t size,
450 			   const snd_pcm_channel_area_t *slave_areas,
451 			   snd_pcm_uframes_t slave_offset,
452 			   snd_pcm_uframes_t *slave_sizep)
453 {
454 	snd_pcm_iec958_t *iec = pcm->private_data;
455 	if (size > *slave_sizep)
456 		size = *slave_sizep;
457 	iec->func(iec, slave_areas, slave_offset,
458 		  areas, offset,
459 		  pcm->channels, size);
460 	*slave_sizep = size;
461 	return size;
462 }
463 
464 static snd_pcm_uframes_t
snd_pcm_iec958_read_areas(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,snd_pcm_uframes_t size,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset,snd_pcm_uframes_t * slave_sizep)465 snd_pcm_iec958_read_areas(snd_pcm_t *pcm,
466 			  const snd_pcm_channel_area_t *areas,
467 			  snd_pcm_uframes_t offset,
468 			  snd_pcm_uframes_t size,
469 			  const snd_pcm_channel_area_t *slave_areas,
470 			  snd_pcm_uframes_t slave_offset,
471 			  snd_pcm_uframes_t *slave_sizep)
472 {
473 	snd_pcm_iec958_t *iec = pcm->private_data;
474 	if (size > *slave_sizep)
475 		size = *slave_sizep;
476 	iec->func(iec, areas, offset,
477 		  slave_areas, slave_offset,
478 		  pcm->channels, size);
479 	*slave_sizep = size;
480 	return size;
481 }
482 
snd_pcm_iec958_init(snd_pcm_t * pcm)483 static int snd_pcm_iec958_init(snd_pcm_t *pcm)
484 {
485 	snd_pcm_iec958_t *iec = pcm->private_data;
486 	iec->counter = 0;
487 	return 0;
488 }
489 
snd_pcm_iec958_dump(snd_pcm_t * pcm,snd_output_t * out)490 static void snd_pcm_iec958_dump(snd_pcm_t *pcm, snd_output_t *out)
491 {
492 	snd_pcm_iec958_t *iec = pcm->private_data;
493 	snd_output_printf(out, "IEC958 subframe conversion PCM (%s)\n",
494 			  snd_pcm_format_name(iec->sformat));
495 	if (pcm->setup) {
496 		snd_output_printf(out, "Its setup is:\n");
497 		snd_pcm_dump_setup(pcm, out);
498 	}
499 	snd_output_printf(out, "Slave: ");
500 	snd_pcm_dump(iec->plug.gen.slave, out);
501 }
502 
snd_pcm_iec958_rewind(snd_pcm_t * pcm,snd_pcm_uframes_t frames)503 static snd_pcm_sframes_t snd_pcm_iec958_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
504 {
505 	unsigned int counter_decrement;
506 	snd_pcm_iec958_t *iec = pcm->private_data;
507 	snd_pcm_sframes_t result = snd_pcm_plugin_rewind(pcm, frames);
508 	if (result <= 0)
509 		return result;
510 
511 	counter_decrement = result % 192;
512 	iec->counter += 192 - counter_decrement;
513 	iec->counter %= 192;
514 	return result;
515 }
516 
snd_pcm_iec958_forward(snd_pcm_t * pcm,snd_pcm_uframes_t frames)517 static snd_pcm_sframes_t snd_pcm_iec958_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
518 
519 {
520 	unsigned int counter_increment;
521 	snd_pcm_iec958_t *iec = pcm->private_data;
522 	snd_pcm_sframes_t result = snd_pcm_plugin_rewind(pcm, frames);
523 	if (result <= 0)
524 		return result;
525 
526 	counter_increment = result % 192;
527 	iec->counter += counter_increment;
528 	iec->counter %= 192;
529 	return result;
530 }
531 
532 static const snd_pcm_ops_t snd_pcm_iec958_ops = {
533 	.close = snd_pcm_generic_close,
534 	.info = snd_pcm_generic_info,
535 	.hw_refine = snd_pcm_iec958_hw_refine,
536 	.hw_params = snd_pcm_iec958_hw_params,
537 	.hw_free = snd_pcm_generic_hw_free,
538 	.sw_params = snd_pcm_generic_sw_params,
539 	.channel_info = snd_pcm_generic_channel_info,
540 	.dump = snd_pcm_iec958_dump,
541 	.nonblock = snd_pcm_generic_nonblock,
542 	.async = snd_pcm_generic_async,
543 	.mmap = snd_pcm_generic_mmap,
544 	.munmap = snd_pcm_generic_munmap,
545 	.query_chmaps = snd_pcm_generic_query_chmaps,
546 	.get_chmap = snd_pcm_generic_get_chmap,
547 	.set_chmap = snd_pcm_generic_set_chmap,
548 };
549 
550 /**
551  * \brief Creates a new IEC958 subframe conversion PCM
552  * \param pcmp Returns created PCM handle
553  * \param name Name of PCM
554  * \param sformat Slave (destination) format
555  * \param slave Slave PCM handle
556  * \param close_slave When set, the slave PCM handle is closed with copy PCM
557  * \param status_bits The IEC958 status bits
558  * \param preamble_vals The preamble byte values
559  * \param hdmi_mode When set, enable HDMI compliant formatting
560  * \retval zero on success otherwise a negative error code
561  * \warning Using of this function might be dangerous in the sense
562  *          of compatibility reasons. The prototype might be freely
563  *          changed in future.
564  */
snd_pcm_iec958_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_format_t sformat,snd_pcm_t * slave,int close_slave,const unsigned char * status_bits,const unsigned char * preamble_vals,int hdmi_mode)565 int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat,
566 			snd_pcm_t *slave, int close_slave,
567 			const unsigned char *status_bits,
568 			const unsigned char *preamble_vals,
569 		        int hdmi_mode)
570 {
571 	snd_pcm_t *pcm;
572 	snd_pcm_iec958_t *iec;
573 	int err;
574 	static const unsigned char default_status_bits[] = {
575 		IEC958_AES0_CON_EMPHASIS_NONE,
576 		IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
577 		0,
578 		IEC958_AES3_CON_FS_NOTID, /* will be set in hwparams */
579 		IEC958_AES4_CON_WORDLEN_NOTID /* will be set in hwparams */
580 	};
581 
582 	assert(pcmp && slave);
583 	if (snd_pcm_format_linear(sformat) != 1 &&
584 	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_LE &&
585 	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_BE)
586 		return -EINVAL;
587 	iec = calloc(1, sizeof(snd_pcm_iec958_t));
588 	if (!iec) {
589 		return -ENOMEM;
590 	}
591 	snd_pcm_plugin_init(&iec->plug);
592 	iec->sformat = sformat;
593 	iec->plug.read = snd_pcm_iec958_read_areas;
594 	iec->plug.write = snd_pcm_iec958_write_areas;
595 	iec->plug.init = snd_pcm_iec958_init;
596 	iec->plug.undo_read = snd_pcm_plugin_undo_read_generic;
597 	iec->plug.undo_write = snd_pcm_plugin_undo_write_generic;
598 	iec->plug.gen.slave = slave;
599 	iec->plug.gen.close_slave = close_slave;
600 
601 	if (status_bits)
602 		memcpy(iec->status, status_bits, sizeof(iec->status));
603 	else
604 		memcpy(iec->status, default_status_bits, sizeof(default_status_bits));
605 
606 	memcpy(iec->preamble, preamble_vals, 3);
607 
608 	iec->hdmi_mode = hdmi_mode;
609 
610 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_IEC958, name, slave->stream, slave->mode);
611 	if (err < 0) {
612 		free(iec);
613 		return err;
614 	}
615 	pcm->ops = &snd_pcm_iec958_ops;
616 
617 	iec->fops = snd_pcm_plugin_fast_ops;
618 	iec->fops.rewind = snd_pcm_iec958_rewind;
619 	iec->fops.forward = snd_pcm_iec958_forward;
620 	pcm->fast_ops = &iec->fops;
621 
622 	pcm->private_data = iec;
623 	pcm->poll_fd = slave->poll_fd;
624 	pcm->poll_events = slave->poll_events;
625 	pcm->tstamp_type = slave->tstamp_type;
626 	snd_pcm_set_hw_ptr(pcm, &iec->plug.hw_ptr, -1, 0);
627 	snd_pcm_set_appl_ptr(pcm, &iec->plug.appl_ptr, -1, 0);
628 	*pcmp = pcm;
629 
630 	return 0;
631 }
632 
633 /*! \page pcm_plugins
634 
635 \section pcm_plugins_iec958 Plugin: IEC958
636 
637 This plugin converts 32bit IEC958 subframe samples to linear, or linear to
638 32bit IEC958 subframe samples.
639 
640 \code
641 pcm.name {
642         type iec958             # IEC958 subframe conversion PCM
643         slave STR               # Slave name
644         # or
645         slave {                 # Slave definition
646                 pcm STR         # Slave PCM name
647                 # or
648                 pcm { }         # Slave PCM definition
649         }
650 	[status status-bytes]	# IEC958 status bits (given in byte array)
651 	# IEC958 preamble bits definitions
652 	# B/M/W or Z/X/Y, B = block start, M = even subframe, W = odd subframe
653 	# As default, Z = 0x08, Y = 0x04, X = 0x02
654 	[preamble.z or preamble.b val]
655 	[preamble.x or preamble.m val]
656 	[preamble.y or preamble.w val]
657 	[hdmi_mode true]
658 }
659 \endcode
660 
661 When <code>hdmi_mode</code> is true, 8-channel compressed data is
662 formatted as 4 contiguous frames of a single IEC958 stream as required
663 by the HDMI HBR specification.
664 
665 \subsection pcm_plugins_iec958_funcref Function reference
666 
667 <UL>
668   <LI>snd_pcm_iec958_open()
669   <LI>_snd_pcm_iec958_open()
670 </UL>
671 
672 */
673 
674 /**
675  * \brief Creates a new IEC958 subframe conversion PCM
676  * \param pcmp Returns created PCM handle
677  * \param name Name of PCM
678  * \param root Root configuration node
679  * \param conf Configuration node with copy PCM description
680  * \param stream Stream type
681  * \param mode Stream mode
682  * \retval zero on success otherwise a negative error code
683  * \warning Using of this function might be dangerous in the sense
684  *          of compatibility reasons. The prototype might be freely
685  *          changed in future.
686  */
_snd_pcm_iec958_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)687 int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name,
688 			 snd_config_t *root, snd_config_t *conf,
689 			 snd_pcm_stream_t stream, int mode)
690 {
691 	snd_config_iterator_t i, next;
692 	int err;
693 	snd_pcm_t *spcm;
694 	snd_config_t *slave = NULL, *sconf;
695 	snd_config_t *status = NULL, *preamble = NULL;
696 	snd_pcm_format_t sformat;
697 	unsigned char status_bits[24];
698 	unsigned char preamble_vals[3] = {
699 		0x08, 0x02, 0x04 /* Z, X, Y */
700 	};
701 	int hdmi_mode = 0;
702 
703 	snd_config_for_each(i, next, conf) {
704 		snd_config_t *n = snd_config_iterator_entry(i);
705 		const char *id;
706 		if (snd_config_get_id(n, &id) < 0)
707 			continue;
708 		if (snd_pcm_conf_generic_id(id))
709 			continue;
710 		if (strcmp(id, "slave") == 0) {
711 			slave = n;
712 			continue;
713 		}
714 		if (strcmp(id, "status") == 0) {
715 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
716 				SNDERR("Invalid type for %s", id);
717 				return -EINVAL;
718 			}
719 			status = n;
720 			continue;
721 		}
722 		if (strcmp(id, "preamble") == 0) {
723 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
724 				SNDERR("Invalid type for %s", id);
725 				return -EINVAL;
726 			}
727 			preamble = n;
728 			continue;
729 		}
730 		if (strcmp(id, "hdmi_mode") == 0) {
731 			err = snd_config_get_bool(n);
732 			if (err < 0)
733 				continue;
734 			hdmi_mode = err;
735 			continue;
736 		}
737 		SNDERR("Unknown field %s", id);
738 		return -EINVAL;
739 	}
740 	memset(status_bits, 0, sizeof(status_bits));
741 	if (status) {
742 		snd_config_iterator_t i, inext;
743 		int bytes = 0;
744 		snd_config_for_each(i, inext, status) {
745 			long val;
746 			snd_config_t *n = snd_config_iterator_entry(i);
747 			if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
748 				SNDERR("invalid IEC958 status bits");
749 				return -EINVAL;
750 			}
751 			err = snd_config_get_integer(n, &val);
752 			if (err < 0) {
753 				SNDERR("invalid IEC958 status bits");
754 				return err;
755 			}
756 			status_bits[bytes] = val;
757 			bytes++;
758 			if (bytes >= (int)sizeof(status_bits))
759 				break;
760 		}
761 		// fprintf(stderr, "STATUS bits: %02x %02x %02x %02x\n", status_bits[0], status_bits[1], status_bits[2], status_bits[3]);
762 	}
763 	if (preamble) {
764 		snd_config_iterator_t i, inext;
765 		snd_config_for_each(i, inext, preamble) {
766 			long val;
767 			snd_config_t *n = snd_config_iterator_entry(i);
768 			const char *id;
769 			int idx;
770 			if (snd_config_get_id(n, &id) < 0)
771 				continue;
772 			if (strcmp(id, "b") == 0 || strcmp(id, "z") == 0)
773 				idx = PREAMBLE_Z;
774 			else if (strcmp(id, "m") == 0 || strcmp(id, "x") == 0)
775 				idx = PREAMBLE_X;
776 			else if (strcmp(id, "w") == 0 || strcmp(id, "y") == 0)
777 				idx = PREAMBLE_Y;
778 			else {
779 				SNDERR("invalid IEC958 preamble type %s", id);
780 				return -EINVAL;
781 			}
782 			err = snd_config_get_integer(n, &val);
783 			if (err < 0) {
784 				SNDERR("invalid IEC958 preamble value");
785 				return err;
786 			}
787 			preamble_vals[idx] = val;
788 		}
789 	}
790 	if (!slave) {
791 		SNDERR("slave is not defined");
792 		return -EINVAL;
793 	}
794 	err = snd_pcm_slave_conf(root, slave, &sconf, 1,
795 				 SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
796 	if (err < 0)
797 		return err;
798 	if (snd_pcm_format_linear(sformat) != 1 &&
799 	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_LE &&
800 	    sformat != SND_PCM_FORMAT_IEC958_SUBFRAME_BE) {
801 	    	snd_config_delete(sconf);
802 		SNDERR("invalid slave format");
803 		return -EINVAL;
804 	}
805 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
806 	snd_config_delete(sconf);
807 	if (err < 0)
808 		return err;
809 	err = snd_pcm_iec958_open(pcmp, name, sformat, spcm, 1,
810 				  status ? status_bits : NULL,
811 				  preamble_vals, hdmi_mode);
812 	if (err < 0)
813 		snd_pcm_close(spcm);
814 	return err;
815 }
816 #ifndef DOC_HIDDEN
817 SND_DLSYM_BUILD_VERSION(_snd_pcm_iec958_open, SND_PCM_DLSYM_VERSION);
818 #endif
819