summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-05-22 10:04:45 -0400
committerTakashi Iwai <tiwai@suse.de>2015-05-22 10:04:45 -0400
commit181a152a09ccbf977ea69405cde4d87b5585de6c (patch)
tree7da9527a962994fb2c1202c7e63954c1f0e950f3
parent6ab97f25ad1c3883f0dc20d2d5b7dae30acade74 (diff)
parent9203dd016a5d8ffb2eb6acdca60cd0b5dfe38c2b (diff)
Merge branch 'topic/hdmi' into for-next
-rw-r--r--include/sound/pcm_drm_eld.h6
-rw-r--r--include/sound/pcm_iec958.h9
-rw-r--r--sound/core/Kconfig6
-rw-r--r--sound/core/Makefile2
-rw-r--r--sound/core/pcm_drm_eld.c99
-rw-r--r--sound/core/pcm_iec958.c95
6 files changed, 217 insertions, 0 deletions
diff --git a/include/sound/pcm_drm_eld.h b/include/sound/pcm_drm_eld.h
new file mode 100644
index 000000000000..93357b25d2e2
--- /dev/null
+++ b/include/sound/pcm_drm_eld.h
@@ -0,0 +1,6 @@
1#ifndef __SOUND_PCM_DRM_ELD_H
2#define __SOUND_PCM_DRM_ELD_H
3
4int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld);
5
6#endif
diff --git a/include/sound/pcm_iec958.h b/include/sound/pcm_iec958.h
new file mode 100644
index 000000000000..0eed397aca8e
--- /dev/null
+++ b/include/sound/pcm_iec958.h
@@ -0,0 +1,9 @@
1#ifndef __SOUND_PCM_IEC958_H
2#define __SOUND_PCM_IEC958_H
3
4#include <linux/types.h>
5
6int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
7 size_t len);
8
9#endif
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 63cc2e967099..937f5c763899 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -6,6 +6,12 @@ config SND_PCM
6 tristate 6 tristate
7 select SND_TIMER 7 select SND_TIMER
8 8
9config SND_PCM_ELD
10 bool
11
12config SND_PCM_IEC958
13 bool
14
9config SND_DMAENGINE_PCM 15config SND_DMAENGINE_PCM
10 tristate 16 tristate
11 17
diff --git a/sound/core/Makefile b/sound/core/Makefile
index 7dd17a365269..73871b63a092 100644
--- a/sound/core/Makefile
+++ b/sound/core/Makefile
@@ -16,6 +16,8 @@ snd-$(CONFIG_SND_JACK) += ctljack.o jack.o
16snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ 16snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
17 pcm_memory.o memalloc.o 17 pcm_memory.o memalloc.o
18snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o 18snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
19snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
20snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
19 21
20# for trace-points 22# for trace-points
21CFLAGS_pcm_lib.o := -I$(src) 23CFLAGS_pcm_lib.o := -I$(src)
diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c
new file mode 100644
index 000000000000..e70379fb63d0
--- /dev/null
+++ b/sound/core/pcm_drm_eld.c
@@ -0,0 +1,99 @@
1/*
2 * PCM DRM helpers
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/export.h>
9#include <drm/drm_edid.h>
10#include <sound/pcm.h>
11#include <sound/pcm_drm_eld.h>
12
13static const unsigned int eld_rates[] = {
14 32000,
15 44100,
16 48000,
17 88200,
18 96000,
19 176400,
20 192000,
21};
22
23static unsigned int sad_max_channels(const u8 *sad)
24{
25 return 1 + (sad[0] & 7);
26}
27
28static int eld_limit_rates(struct snd_pcm_hw_params *params,
29 struct snd_pcm_hw_rule *rule)
30{
31 struct snd_interval *r = hw_param_interval(params, rule->var);
32 struct snd_interval *c;
33 unsigned int rate_mask = 7, i;
34 const u8 *sad, *eld = rule->private;
35
36 sad = drm_eld_sad(eld);
37 if (sad) {
38 c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
39
40 for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) {
41 unsigned max_channels = sad_max_channels(sad);
42
43 /*
44 * Exclude SADs which do not include the
45 * requested number of channels.
46 */
47 if (c->min <= max_channels)
48 rate_mask |= sad[1];
49 }
50 }
51
52 return snd_interval_list(r, ARRAY_SIZE(eld_rates), eld_rates,
53 rate_mask);
54}
55
56static int eld_limit_channels(struct snd_pcm_hw_params *params,
57 struct snd_pcm_hw_rule *rule)
58{
59 struct snd_interval *c = hw_param_interval(params, rule->var);
60 struct snd_interval *r;
61 struct snd_interval t = { .min = 1, .max = 2, .integer = 1, };
62 unsigned int i;
63 const u8 *sad, *eld = rule->private;
64
65 sad = drm_eld_sad(eld);
66 if (sad) {
67 unsigned int rate_mask = 0;
68
69 /* Convert the rate interval to a mask */
70 r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
71 for (i = 0; i < ARRAY_SIZE(eld_rates); i++)
72 if (r->min <= eld_rates[i] && r->max >= eld_rates[i])
73 rate_mask |= BIT(i);
74
75 for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
76 if (rate_mask & sad[1])
77 t.max = max(t.max, sad_max_channels(sad));
78 }
79
80 return snd_interval_refine(c, &t);
81}
82
83int snd_pcm_hw_constraint_eld(struct snd_pcm_runtime *runtime, void *eld)
84{
85 int ret;
86
87 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
88 eld_limit_rates, eld,
89 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
90 if (ret < 0)
91 return ret;
92
93 ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
94 eld_limit_channels, eld,
95 SNDRV_PCM_HW_PARAM_RATE, -1);
96
97 return ret;
98}
99EXPORT_SYMBOL_GPL(snd_pcm_hw_constraint_eld);
diff --git a/sound/core/pcm_iec958.c b/sound/core/pcm_iec958.c
new file mode 100644
index 000000000000..36b2d7aca1bd
--- /dev/null
+++ b/sound/core/pcm_iec958.c
@@ -0,0 +1,95 @@
1/*
2 * PCM DRM helpers
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8#include <linux/export.h>
9#include <linux/types.h>
10#include <sound/asoundef.h>
11#include <sound/pcm.h>
12#include <sound/pcm_iec958.h>
13
14/**
15 * snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
16 * @runtime: pcm runtime structure with ->rate filled in
17 * @cs: channel status buffer, at least four bytes
18 * @len: length of channel status buffer
19 *
20 * Create the consumer format channel status data in @cs of maximum size
21 * @len corresponding to the parameters of the PCM runtime @runtime.
22 *
23 * Drivers may wish to tweak the contents of the buffer after creation.
24 *
25 * Returns: length of buffer, or negative error code if something failed.
26 */
27int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
28 size_t len)
29{
30 unsigned int fs, ws;
31
32 if (len < 4)
33 return -EINVAL;
34
35 switch (runtime->rate) {
36 case 32000:
37 fs = IEC958_AES3_CON_FS_32000;
38 break;
39 case 44100:
40 fs = IEC958_AES3_CON_FS_44100;
41 break;
42 case 48000:
43 fs = IEC958_AES3_CON_FS_48000;
44 break;
45 case 88200:
46 fs = IEC958_AES3_CON_FS_88200;
47 break;
48 case 96000:
49 fs = IEC958_AES3_CON_FS_96000;
50 break;
51 case 176400:
52 fs = IEC958_AES3_CON_FS_176400;
53 break;
54 case 192000:
55 fs = IEC958_AES3_CON_FS_192000;
56 break;
57 default:
58 return -EINVAL;
59 }
60
61 if (len > 4) {
62 switch (snd_pcm_format_width(runtime->format)) {
63 case 16:
64 ws = IEC958_AES4_CON_WORDLEN_20_16;
65 break;
66 case 18:
67 ws = IEC958_AES4_CON_WORDLEN_22_18;
68 break;
69 case 20:
70 ws = IEC958_AES4_CON_WORDLEN_20_16 |
71 IEC958_AES4_CON_MAX_WORDLEN_24;
72 break;
73 case 24:
74 ws = IEC958_AES4_CON_WORDLEN_24_20 |
75 IEC958_AES4_CON_MAX_WORDLEN_24;
76 break;
77
78 default:
79 return -EINVAL;
80 }
81 }
82
83 memset(cs, 0, len);
84
85 cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
86 cs[1] = IEC958_AES1_CON_GENERAL;
87 cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
88 cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs;
89
90 if (len > 4)
91 cs[4] = ws;
92
93 return len;
94}
95EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);