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