Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/hda/codecs/hdmi/eld.c
26490 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Generic routines and proc interface for ELD(EDID Like Data) information
4
*
5
* Copyright(c) 2008 Intel Corporation.
6
* Copyright (c) 2013 Anssi Hannula <[email protected]>
7
*
8
* Authors:
9
* Wu Fengguang <[email protected]>
10
*/
11
12
#include <linux/init.h>
13
#include <linux/slab.h>
14
#include <sound/core.h>
15
#include <sound/hda_chmap.h>
16
#include <sound/hda_codec.h>
17
#include "hda_local.h"
18
19
enum cea_edid_versions {
20
CEA_EDID_VER_NONE = 0,
21
CEA_EDID_VER_CEA861 = 1,
22
CEA_EDID_VER_CEA861A = 2,
23
CEA_EDID_VER_CEA861BCD = 3,
24
CEA_EDID_VER_RESERVED = 4,
25
};
26
27
/*
28
* The following two lists are shared between
29
* - HDMI audio InfoFrame (source to sink)
30
* - CEA E-EDID Extension (sink to source)
31
*/
32
33
static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
34
int byte_index)
35
{
36
unsigned int val;
37
38
val = snd_hda_codec_read(codec, nid, 0,
39
AC_VERB_GET_HDMI_ELDD, byte_index);
40
#ifdef BE_PARANOID
41
codec_info(codec, "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
42
#endif
43
return val;
44
}
45
46
int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
47
{
48
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
49
AC_DIPSIZE_ELD_BUF);
50
}
51
52
int snd_hdmi_get_eld(struct hda_codec *codec, hda_nid_t nid,
53
unsigned char *buf, int *eld_size)
54
{
55
int i;
56
int ret = 0;
57
int size;
58
59
/*
60
* ELD size is initialized to zero in caller function. If no errors and
61
* ELD is valid, actual eld_size is assigned.
62
*/
63
64
size = snd_hdmi_get_eld_size(codec, nid);
65
if (size == 0) {
66
/* wfg: workaround for ASUS P5E-VM HDMI board */
67
codec_info(codec, "HDMI: ELD buf size is 0, force 128\n");
68
size = 128;
69
}
70
if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
71
codec_info(codec, "HDMI: invalid ELD buf size %d\n", size);
72
return -ERANGE;
73
}
74
75
/* set ELD buffer */
76
for (i = 0; i < size; i++) {
77
unsigned int val = hdmi_get_eld_data(codec, nid, i);
78
/*
79
* Graphics driver might be writing to ELD buffer right now.
80
* Just abort. The caller will repoll after a while.
81
*/
82
if (!(val & AC_ELDD_ELD_VALID)) {
83
codec_info(codec, "HDMI: invalid ELD data byte %d\n", i);
84
ret = -EINVAL;
85
goto error;
86
}
87
val &= AC_ELDD_ELD_DATA;
88
/*
89
* The first byte cannot be zero. This can happen on some DVI
90
* connections. Some Intel chips may also need some 250ms delay
91
* to return non-zero ELD data, even when the graphics driver
92
* correctly writes ELD content before setting ELD_valid bit.
93
*/
94
if (!val && !i) {
95
codec_dbg(codec, "HDMI: 0 ELD data\n");
96
ret = -EINVAL;
97
goto error;
98
}
99
buf[i] = val;
100
}
101
102
*eld_size = size;
103
error:
104
return ret;
105
}
106
107
#ifdef CONFIG_SND_PROC_FS
108
void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
109
struct snd_info_buffer *buffer,
110
hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
111
{
112
snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present);
113
snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid);
114
snd_iprintf(buffer, "codec_pin_nid\t\t0x%x\n", pin_nid);
115
snd_iprintf(buffer, "codec_dev_id\t\t0x%x\n", dev_id);
116
snd_iprintf(buffer, "codec_cvt_nid\t\t0x%x\n", cvt_nid);
117
118
if (!eld->eld_valid)
119
return;
120
121
snd_print_eld_info(&eld->info, buffer);
122
}
123
124
void snd_hdmi_write_eld_info(struct hdmi_eld *eld,
125
struct snd_info_buffer *buffer)
126
{
127
struct snd_parsed_hdmi_eld *e = &eld->info;
128
char line[64];
129
char name[64];
130
char *sname;
131
long long val;
132
unsigned int n;
133
134
while (!snd_info_get_line(buffer, line, sizeof(line))) {
135
if (sscanf(line, "%s %llx", name, &val) != 2)
136
continue;
137
/*
138
* We don't allow modification to these fields:
139
* monitor_name manufacture_id product_id
140
* eld_version edid_version
141
*/
142
if (!strcmp(name, "monitor_present"))
143
eld->monitor_present = val;
144
else if (!strcmp(name, "eld_valid"))
145
eld->eld_valid = val;
146
else if (!strcmp(name, "connection_type"))
147
e->conn_type = val;
148
else if (!strcmp(name, "port_id"))
149
e->port_id = val;
150
else if (!strcmp(name, "support_hdcp"))
151
e->support_hdcp = val;
152
else if (!strcmp(name, "support_ai"))
153
e->support_ai = val;
154
else if (!strcmp(name, "audio_sync_delay"))
155
e->aud_synch_delay = val;
156
else if (!strcmp(name, "speakers"))
157
e->spk_alloc = val;
158
else if (!strcmp(name, "sad_count"))
159
e->sad_count = val;
160
else if (!strncmp(name, "sad", 3)) {
161
sname = name + 4;
162
n = name[3] - '0';
163
if (name[4] >= '0' && name[4] <= '9') {
164
sname++;
165
n = 10 * n + name[4] - '0';
166
}
167
if (n >= ELD_MAX_SAD)
168
continue;
169
if (!strcmp(sname, "_coding_type"))
170
e->sad[n].format = val;
171
else if (!strcmp(sname, "_channels"))
172
e->sad[n].channels = val;
173
else if (!strcmp(sname, "_rates"))
174
e->sad[n].rates = val;
175
else if (!strcmp(sname, "_bits"))
176
e->sad[n].sample_bits = val;
177
else if (!strcmp(sname, "_max_bitrate"))
178
e->sad[n].max_bitrate = val;
179
else if (!strcmp(sname, "_profile"))
180
e->sad[n].profile = val;
181
if (n >= e->sad_count)
182
e->sad_count = n + 1;
183
}
184
}
185
}
186
#endif /* CONFIG_SND_PROC_FS */
187
188
/* update PCM info based on ELD */
189
void snd_hdmi_eld_update_pcm_info(struct snd_parsed_hdmi_eld *e,
190
struct hda_pcm_stream *hinfo)
191
{
192
u32 rates;
193
u64 formats;
194
unsigned int maxbps;
195
unsigned int channels_max;
196
int i;
197
198
/* assume basic audio support (the basic audio flag is not in ELD;
199
* however, all audio capable sinks are required to support basic
200
* audio) */
201
rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
202
SNDRV_PCM_RATE_48000;
203
formats = SNDRV_PCM_FMTBIT_S16_LE;
204
maxbps = 16;
205
channels_max = 2;
206
for (i = 0; i < e->sad_count; i++) {
207
struct snd_cea_sad *a = &e->sad[i];
208
rates |= a->rates;
209
if (a->channels > channels_max)
210
channels_max = a->channels;
211
if (a->format == AUDIO_CODING_TYPE_LPCM) {
212
if (a->sample_bits & ELD_PCM_BITS_20) {
213
formats |= SNDRV_PCM_FMTBIT_S32_LE;
214
if (maxbps < 20)
215
maxbps = 20;
216
}
217
if (a->sample_bits & ELD_PCM_BITS_24) {
218
formats |= SNDRV_PCM_FMTBIT_S32_LE;
219
if (maxbps < 24)
220
maxbps = 24;
221
}
222
}
223
}
224
225
/* restrict the parameters by the values the codec provides */
226
hinfo->rates &= rates;
227
hinfo->formats &= formats;
228
hinfo->maxbps = min(hinfo->maxbps, maxbps);
229
hinfo->channels_max = min(hinfo->channels_max, channels_max);
230
}
231
232