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