Blame view

sound/pci/hda/hda_eld.c 17.4 KB
7f4a9f434   Wu Fengguang   ALSA: create hda_...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * Generic routines and proc interface for ELD(EDID Like Data) information
   *
   * Copyright(c) 2008 Intel Corporation.
   *
   * Authors:
   * 		Wu Fengguang <wfg@linux.intel.com>
   *
   *  This driver is free software; you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation; either version 2 of the License, or
   *  (at your option) any later version.
   *
   *  This driver is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with this program; if not, write to the Free Software
   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
7f4a9f434   Wu Fengguang   ALSA: create hda_...
26
27
28
29
30
31
32
33
34
  #include <sound/core.h>
  #include <asm/unaligned.h>
  #include "hda_codec.h"
  #include "hda_local.h"
  
  enum eld_versions {
  	ELD_VER_CEA_861D	= 2,
  	ELD_VER_PARTIAL		= 31,
  };
7f4a9f434   Wu Fengguang   ALSA: create hda_...
35
36
37
38
39
40
41
  enum cea_edid_versions {
  	CEA_EDID_VER_NONE	= 0,
  	CEA_EDID_VER_CEA861	= 1,
  	CEA_EDID_VER_CEA861A	= 2,
  	CEA_EDID_VER_CEA861BCD	= 3,
  	CEA_EDID_VER_RESERVED	= 4,
  };
7f4a9f434   Wu Fengguang   ALSA: create hda_...
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  static char *cea_speaker_allocation_names[] = {
  	/*  0 */ "FL/FR",
  	/*  1 */ "LFE",
  	/*  2 */ "FC",
  	/*  3 */ "RL/RR",
  	/*  4 */ "RC",
  	/*  5 */ "FLC/FRC",
  	/*  6 */ "RLC/RRC",
  	/*  7 */ "FLW/FRW",
  	/*  8 */ "FLH/FRH",
  	/*  9 */ "TC",
  	/* 10 */ "FCH",
  };
  
  static char *eld_connection_type_names[4] = {
  	"HDMI",
9415e1c41   Wu Fengguang   ALSA: hda - fix D...
58
  	"DisplayPort",
7f4a9f434   Wu Fengguang   ALSA: create hda_...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  	"2-reserved",
  	"3-reserved"
  };
  
  enum cea_audio_coding_types {
  	AUDIO_CODING_TYPE_REF_STREAM_HEADER	=  0,
  	AUDIO_CODING_TYPE_LPCM			=  1,
  	AUDIO_CODING_TYPE_AC3			=  2,
  	AUDIO_CODING_TYPE_MPEG1			=  3,
  	AUDIO_CODING_TYPE_MP3			=  4,
  	AUDIO_CODING_TYPE_MPEG2			=  5,
  	AUDIO_CODING_TYPE_AACLC			=  6,
  	AUDIO_CODING_TYPE_DTS			=  7,
  	AUDIO_CODING_TYPE_ATRAC			=  8,
  	AUDIO_CODING_TYPE_SACD			=  9,
  	AUDIO_CODING_TYPE_EAC3			= 10,
  	AUDIO_CODING_TYPE_DTS_HD		= 11,
  	AUDIO_CODING_TYPE_MLP			= 12,
  	AUDIO_CODING_TYPE_DST			= 13,
  	AUDIO_CODING_TYPE_WMAPRO		= 14,
  	AUDIO_CODING_TYPE_REF_CXT		= 15,
  	/* also include valid xtypes below */
  	AUDIO_CODING_TYPE_HE_AAC		= 15,
  	AUDIO_CODING_TYPE_HE_AAC2		= 16,
  	AUDIO_CODING_TYPE_MPEG_SURROUND		= 17,
  };
  
  enum cea_audio_coding_xtypes {
  	AUDIO_CODING_XTYPE_HE_REF_CT		= 0,
  	AUDIO_CODING_XTYPE_HE_AAC		= 1,
  	AUDIO_CODING_XTYPE_HE_AAC2		= 2,
  	AUDIO_CODING_XTYPE_MPEG_SURROUND	= 3,
  	AUDIO_CODING_XTYPE_FIRST_RESERVED	= 4,
  };
  
  static char *cea_audio_coding_type_names[] = {
  	/*  0 */ "undefined",
  	/*  1 */ "LPCM",
  	/*  2 */ "AC-3",
  	/*  3 */ "MPEG1",
  	/*  4 */ "MP3",
  	/*  5 */ "MPEG2",
  	/*  6 */ "AAC-LC",
  	/*  7 */ "DTS",
  	/*  8 */ "ATRAC",
06f69d17a   Wu Fengguang   ALSA: hda: minor ...
104
  	/*  9 */ "DSD (One Bit Audio)",
7f4a9f434   Wu Fengguang   ALSA: create hda_...
105
106
107
108
109
110
111
112
113
114
115
116
117
  	/* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
  	/* 11 */ "DTS-HD",
  	/* 12 */ "MLP (Dolby TrueHD)",
  	/* 13 */ "DST",
  	/* 14 */ "WMAPro",
  	/* 15 */ "HE-AAC",
  	/* 16 */ "HE-AACv2",
  	/* 17 */ "MPEG Surround",
  };
  
  /*
   * The following two lists are shared between
   * 	- HDMI audio InfoFrame (source to sink)
4e19c58f2   Wu Fengguang   ALSA: hda: minor ...
118
   * 	- CEA E-EDID Extension (sink to source)
7f4a9f434   Wu Fengguang   ALSA: create hda_...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
   */
  
  /*
   * SS1:SS0 index => sample size
   */
  static int cea_sample_sizes[4] = {
  	0,	 		/* 0: Refer to Stream Header */
  	AC_SUPPCM_BITS_16,	/* 1: 16 bits */
  	AC_SUPPCM_BITS_20,	/* 2: 20 bits */
  	AC_SUPPCM_BITS_24,	/* 3: 24 bits */
  };
  
  /*
   * SF2:SF1:SF0 index => sampling frequency
   */
  static int cea_sampling_frequencies[8] = {
  	0,			/* 0: Refer to Stream Header */
  	SNDRV_PCM_RATE_32000,	/* 1:  32000Hz */
  	SNDRV_PCM_RATE_44100,	/* 2:  44100Hz */
  	SNDRV_PCM_RATE_48000,	/* 3:  48000Hz */
  	SNDRV_PCM_RATE_88200,	/* 4:  88200Hz */
  	SNDRV_PCM_RATE_96000,	/* 5:  96000Hz */
  	SNDRV_PCM_RATE_176400,	/* 6: 176400Hz */
  	SNDRV_PCM_RATE_192000,	/* 7: 192000Hz */
  };
b6acf013b   Takashi Iwai   ALSA: hda - Don't...
144
  static unsigned int hdmi_get_eld_data(struct hda_codec *codec, hda_nid_t nid,
7f4a9f434   Wu Fengguang   ALSA: create hda_...
145
146
147
148
149
150
  					int byte_index)
  {
  	unsigned int val;
  
  	val = snd_hda_codec_read(codec, nid, 0,
  					AC_VERB_GET_HDMI_ELDD, byte_index);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
151
  #ifdef BE_PARANOID
03284c8f2   Wu Fengguang   ALSA: hda - make ...
152
153
  	printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x
  ", byte_index, val);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
154
  #endif
b6acf013b   Takashi Iwai   ALSA: hda - Don't...
155
  	return val;
7f4a9f434   Wu Fengguang   ALSA: create hda_...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  }
  
  #define GRAB_BITS(buf, byte, lowbit, bits) 		\
  ({							\
  	BUILD_BUG_ON(lowbit > 7);			\
  	BUILD_BUG_ON(bits > 8);				\
  	BUILD_BUG_ON(bits <= 0);			\
  							\
  	(buf[byte] >> (lowbit)) & ((1 << (bits)) - 1);	\
  })
  
  static void hdmi_update_short_audio_desc(struct cea_sad *a,
  					 const unsigned char *buf)
  {
  	int i;
  	int val;
  
  	val = GRAB_BITS(buf, 1, 0, 7);
  	a->rates = 0;
  	for (i = 0; i < 7; i++)
  		if (val & (1 << i))
  			a->rates |= cea_sampling_frequencies[i + 1];
  
  	a->channels = GRAB_BITS(buf, 0, 0, 3);
  	a->channels++;
0bbaee3a5   Anssi Hannula   ALSA: hda - Reset...
181
182
  	a->sample_bits = 0;
  	a->max_bitrate = 0;
7f4a9f434   Wu Fengguang   ALSA: create hda_...
183
184
185
186
  	a->format = GRAB_BITS(buf, 0, 3, 4);
  	switch (a->format) {
  	case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
  		snd_printd(KERN_INFO
03284c8f2   Wu Fengguang   ALSA: hda - make ...
187
188
  				"HDMI: audio coding type 0 not expected
  ");
7f4a9f434   Wu Fengguang   ALSA: create hda_...
189
190
191
192
  		break;
  
  	case AUDIO_CODING_TYPE_LPCM:
  		val = GRAB_BITS(buf, 2, 0, 3);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  		for (i = 0; i < 3; i++)
  			if (val & (1 << i))
  				a->sample_bits |= cea_sample_sizes[i + 1];
  		break;
  
  	case AUDIO_CODING_TYPE_AC3:
  	case AUDIO_CODING_TYPE_MPEG1:
  	case AUDIO_CODING_TYPE_MP3:
  	case AUDIO_CODING_TYPE_MPEG2:
  	case AUDIO_CODING_TYPE_AACLC:
  	case AUDIO_CODING_TYPE_DTS:
  	case AUDIO_CODING_TYPE_ATRAC:
  		a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
  		a->max_bitrate *= 8000;
  		break;
  
  	case AUDIO_CODING_TYPE_SACD:
  		break;
  
  	case AUDIO_CODING_TYPE_EAC3:
  		break;
  
  	case AUDIO_CODING_TYPE_DTS_HD:
  		break;
  
  	case AUDIO_CODING_TYPE_MLP:
  		break;
  
  	case AUDIO_CODING_TYPE_DST:
  		break;
  
  	case AUDIO_CODING_TYPE_WMAPRO:
  		a->profile = GRAB_BITS(buf, 2, 0, 3);
  		break;
  
  	case AUDIO_CODING_TYPE_REF_CXT:
  		a->format = GRAB_BITS(buf, 2, 3, 5);
  		if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
  		    a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
  			snd_printd(KERN_INFO
03284c8f2   Wu Fengguang   ALSA: hda - make ...
233
234
  				"HDMI: audio coding xtype %d not expected
  ",
7f4a9f434   Wu Fengguang   ALSA: create hda_...
235
236
237
238
239
240
241
242
243
244
245
246
  				a->format);
  			a->format = 0;
  		} else
  			a->format += AUDIO_CODING_TYPE_HE_AAC -
  				     AUDIO_CODING_XTYPE_HE_AAC;
  		break;
  	}
  }
  
  /*
   * Be careful, ELD buf could be totally rubbish!
   */
5b87ebb7a   Wu Fengguang   ALSA: hda: rename...
247
248
  static int hdmi_update_eld(struct hdmi_eld *e,
  			   const unsigned char *buf, int size)
7f4a9f434   Wu Fengguang   ALSA: create hda_...
249
250
251
252
253
254
255
  {
  	int mnl;
  	int i;
  
  	e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
  	if (e->eld_ver != ELD_VER_CEA_861D &&
  	    e->eld_ver != ELD_VER_PARTIAL) {
03284c8f2   Wu Fengguang   ALSA: hda - make ...
256
257
258
  		snd_printd(KERN_INFO "HDMI: Unknown ELD version %d
  ",
  								e->eld_ver);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
  		goto out_fail;
  	}
  
  	e->eld_size = size;
  	e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
  	mnl		= GRAB_BITS(buf, 4, 0, 5);
  	e->cea_edid_ver	= GRAB_BITS(buf, 4, 5, 3);
  
  	e->support_hdcp	= GRAB_BITS(buf, 5, 0, 1);
  	e->support_ai	= GRAB_BITS(buf, 5, 1, 1);
  	e->conn_type	= GRAB_BITS(buf, 5, 2, 2);
  	e->sad_count	= GRAB_BITS(buf, 5, 4, 4);
  
  	e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
  	e->spk_alloc	= GRAB_BITS(buf, 7, 0, 7);
  
  	e->port_id	  = get_unaligned_le64(buf + 8);
  
  	/* not specified, but the spec's tendency is little endian */
  	e->manufacture_id = get_unaligned_le16(buf + 16);
  	e->product_id	  = get_unaligned_le16(buf + 18);
  
  	if (mnl > ELD_MAX_MNL) {
03284c8f2   Wu Fengguang   ALSA: hda - make ...
282
283
  		snd_printd(KERN_INFO "HDMI: MNL is reserved value %d
  ", mnl);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
284
285
  		goto out_fail;
  	} else if (ELD_FIXED_BYTES + mnl > size) {
03284c8f2   Wu Fengguang   ALSA: hda - make ...
286
287
  		snd_printd(KERN_INFO "HDMI: out of range MNL %d
  ", mnl);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
288
289
  		goto out_fail;
  	} else
f5b2d0ef6   Wu Fengguang   ALSA: HDMI - fix ...
290
  		strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl + 1);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
291
292
293
  
  	for (i = 0; i < e->sad_count; i++) {
  		if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
03284c8f2   Wu Fengguang   ALSA: hda - make ...
294
295
  			snd_printd(KERN_INFO "HDMI: out of range SAD %d
  ", i);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
296
297
298
299
300
  			goto out_fail;
  		}
  		hdmi_update_short_audio_desc(e->sad + i,
  					buf + ELD_FIXED_BYTES + mnl + 3 * i);
  	}
2d1b439bd   Wu Fengguang   ALSA: hda - move ...
301
302
303
304
305
306
307
  	/*
  	 * HDMI sink's ELD info cannot always be retrieved for now, e.g.
  	 * in console or for audio devices. Assume the highest speakers
  	 * configuration, to _not_ prohibit multi-channel audio playback.
  	 */
  	if (!e->spk_alloc)
  		e->spk_alloc = 0xffff;
b95d68b81   Wu Fengguang   ALSA: hda - fix E...
308
  	e->eld_valid = true;
7f4a9f434   Wu Fengguang   ALSA: create hda_...
309
310
311
  	return 0;
  
  out_fail:
7f4a9f434   Wu Fengguang   ALSA: create hda_...
312
313
  	return -EINVAL;
  }
7f4a9f434   Wu Fengguang   ALSA: create hda_...
314
315
316
317
318
  int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
  {
  	return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
  						 AC_DIPSIZE_ELD_BUF);
  }
5b87ebb7a   Wu Fengguang   ALSA: hda: rename...
319
  int snd_hdmi_get_eld(struct hdmi_eld *eld,
7f4a9f434   Wu Fengguang   ALSA: create hda_...
320
321
322
323
324
325
  		     struct hda_codec *codec, hda_nid_t nid)
  {
  	int i;
  	int ret;
  	int size;
  	unsigned char *buf;
14bc52b8f   Pierre-Louis Bossart   ALSA: hda/hdmi: e...
326
327
328
329
  	/*
  	 * ELD size is initialized to zero in caller function. If no errors and
  	 * ELD is valid, actual eld_size is assigned in hdmi_update_eld()
  	 */
7f4a9f434   Wu Fengguang   ALSA: create hda_...
330
331
332
  	size = snd_hdmi_get_eld_size(codec, nid);
  	if (size == 0) {
  		/* wfg: workaround for ASUS P5E-VM HDMI board */
03284c8f2   Wu Fengguang   ALSA: hda - make ...
333
334
  		snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128
  ");
7f4a9f434   Wu Fengguang   ALSA: create hda_...
335
336
  		size = 128;
  	}
14bc52b8f   Pierre-Louis Bossart   ALSA: hda/hdmi: e...
337
  	if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
03284c8f2   Wu Fengguang   ALSA: hda - make ...
338
339
  		snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d
  ", size);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
340
341
  		return -ERANGE;
  	}
14bc52b8f   Pierre-Louis Bossart   ALSA: hda/hdmi: e...
342
343
  	/* set ELD buffer */
  	buf = eld->eld_buffer;
7f4a9f434   Wu Fengguang   ALSA: create hda_...
344

b6acf013b   Takashi Iwai   ALSA: hda - Don't...
345
346
  	for (i = 0; i < size; i++) {
  		unsigned int val = hdmi_get_eld_data(codec, nid, i);
a370fc62b   Wu Fengguang   ALSA: hda - fail ...
347
348
349
350
  		/*
  		 * Graphics driver might be writing to ELD buffer right now.
  		 * Just abort. The caller will repoll after a while.
  		 */
b6acf013b   Takashi Iwai   ALSA: hda - Don't...
351
  		if (!(val & AC_ELDD_ELD_VALID)) {
b6acf013b   Takashi Iwai   ALSA: hda - Don't...
352
353
354
  			snd_printd(KERN_INFO
  				  "HDMI: invalid ELD data byte %d
  ", i);
a370fc62b   Wu Fengguang   ALSA: hda - fail ...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
  			ret = -EINVAL;
  			goto error;
  		}
  		val &= AC_ELDD_ELD_DATA;
  		/*
  		 * The first byte cannot be zero. This can happen on some DVI
  		 * connections. Some Intel chips may also need some 250ms delay
  		 * to return non-zero ELD data, even when the graphics driver
  		 * correctly writes ELD content before setting ELD_valid bit.
  		 */
  		if (!val && !i) {
  			snd_printdd(KERN_INFO "HDMI: 0 ELD data
  ");
  			ret = -EINVAL;
  			goto error;
  		}
b6acf013b   Takashi Iwai   ALSA: hda - Don't...
371
372
  		buf[i] = val;
  	}
7f4a9f434   Wu Fengguang   ALSA: create hda_...
373

5b87ebb7a   Wu Fengguang   ALSA: hda: rename...
374
  	ret = hdmi_update_eld(eld, buf, size);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
375

b6acf013b   Takashi Iwai   ALSA: hda - Don't...
376
  error:
7f4a9f434   Wu Fengguang   ALSA: create hda_...
377
378
  	return ret;
  }
af65cbf29   Pierre-Louis Bossart   ALSA: hdmi: fix p...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
  /**
   * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with
   * hdmi-specific routine.
   */
  static void hdmi_print_pcm_rates(int pcm, char *buf, int buflen)
  {
  	static unsigned int alsa_rates[] = {
  		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
  		96000, 176400, 192000, 384000
  	};
  	int i, j;
  
  	for (i = 0, j = 0; i < ARRAY_SIZE(alsa_rates); i++)
  		if (pcm & (1 << i))
  			j += snprintf(buf + j, buflen - j,  " %d",
  				alsa_rates[i]);
  
  	buf[j] = '\0'; /* necessary when j == 0 */
  }
f71ff0d71   Takashi Iwai   ALSA: hda - Moved...
398
  #define SND_PRINT_RATES_ADVISED_BUFSIZE	80
7f4a9f434   Wu Fengguang   ALSA: create hda_...
399
400
401
  static void hdmi_show_short_audio_desc(struct cea_sad *a)
  {
  	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
ae8cb4caa   Wu Fengguang   ALSA: hda: compac...
402
  	char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
7f4a9f434   Wu Fengguang   ALSA: create hda_...
403

ae8cb4caa   Wu Fengguang   ALSA: hda: compac...
404
405
  	if (!a->format)
  		return;
7f4a9f434   Wu Fengguang   ALSA: create hda_...
406

af65cbf29   Pierre-Louis Bossart   ALSA: hdmi: fix p...
407
  	hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
7f4a9f434   Wu Fengguang   ALSA: create hda_...
408
409
  
  	if (a->format == AUDIO_CODING_TYPE_LPCM)
d757534ed   David Henningsson   ALSA: HDA: Fix dm...
410
  		snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2) - 8);
ae8cb4caa   Wu Fengguang   ALSA: hda: compac...
411
412
413
414
415
  	else if (a->max_bitrate)
  		snprintf(buf2, sizeof(buf2),
  				", max bitrate = %d", a->max_bitrate);
  	else
  		buf2[0] = '\0';
03284c8f2   Wu Fengguang   ALSA: hda - make ...
416
  	printk(KERN_INFO "HDMI: supports coding type %s:"
ae8cb4caa   Wu Fengguang   ALSA: hda: compac...
417
418
419
420
421
422
  			" channels = %d, rates =%s%s
  ",
  			cea_audio_coding_type_names[a->format],
  			a->channels,
  			buf,
  			buf2);
7f4a9f434   Wu Fengguang   ALSA: create hda_...
423
  }
903b21d8b   Wu Fengguang   ALSA: hda: make g...
424
  void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
7f4a9f434   Wu Fengguang   ALSA: create hda_...
425
426
427
428
429
  {
  	int i, j;
  
  	for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
  		if (spk_alloc & (1 << i))
06f69d17a   Wu Fengguang   ALSA: hda: minor ...
430
  			j += snprintf(buf + j, buflen - j,  " %s",
7f4a9f434   Wu Fengguang   ALSA: create hda_...
431
432
  					cea_speaker_allocation_names[i]);
  	}
7f4a9f434   Wu Fengguang   ALSA: create hda_...
433
434
  	buf[j] = '\0';	/* necessary when j == 0 */
  }
5b87ebb7a   Wu Fengguang   ALSA: hda: rename...
435
  void snd_hdmi_show_eld(struct hdmi_eld *e)
7f4a9f434   Wu Fengguang   ALSA: create hda_...
436
437
  {
  	int i;
ae8cb4caa   Wu Fengguang   ALSA: hda: compac...
438

03284c8f2   Wu Fengguang   ALSA: hda - make ...
439
440
  	printk(KERN_INFO "HDMI: detected monitor %s at connection type %s
  ",
ae8cb4caa   Wu Fengguang   ALSA: hda: compac...
441
442
443
444
445
446
  			e->monitor_name,
  			eld_connection_type_names[e->conn_type]);
  
  	if (e->spk_alloc) {
  		char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
  		snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
03284c8f2   Wu Fengguang   ALSA: hda - make ...
447
448
  		printk(KERN_INFO "HDMI: available speakers:%s
  ", buf);
ae8cb4caa   Wu Fengguang   ALSA: hda: compac...
449
  	}
7f4a9f434   Wu Fengguang   ALSA: create hda_...
450
451
452
453
  
  	for (i = 0; i < e->sad_count; i++)
  		hdmi_show_short_audio_desc(e->sad + i);
  }
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
454
455
456
457
458
459
  
  #ifdef CONFIG_PROC_FS
  
  static void hdmi_print_sad_info(int i, struct cea_sad *a,
  				struct snd_info_buffer *buffer)
  {
d39b4352f   Wu Fengguang   ALSA: hda: make g...
460
  	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
461
462
463
464
465
466
  
  	snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s
  ",
  			i, a->format, cea_audio_coding_type_names[a->format]);
  	snd_iprintf(buffer, "sad%d_channels\t\t%d
  ", i, a->channels);
af65cbf29   Pierre-Louis Bossart   ALSA: hdmi: fix p...
467
  	hdmi_print_pcm_rates(a->rates, buf, sizeof(buf));
06f69d17a   Wu Fengguang   ALSA: hda: minor ...
468
469
  	snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s
  ", i, a->rates, buf);
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
470

d39b4352f   Wu Fengguang   ALSA: hda: make g...
471
472
473
474
475
476
  	if (a->format == AUDIO_CODING_TYPE_LPCM) {
  		snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
  		snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s
  ",
  							i, a->sample_bits, buf);
  	}
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  
  	if (a->max_bitrate)
  		snd_iprintf(buffer, "sad%d_max_bitrate\t%d
  ",
  							i, a->max_bitrate);
  
  	if (a->profile)
  		snd_iprintf(buffer, "sad%d_profile\t\t%d
  ", i, a->profile);
  }
  
  static void hdmi_print_eld_info(struct snd_info_entry *entry,
  				struct snd_info_buffer *buffer)
  {
5b87ebb7a   Wu Fengguang   ALSA: hda: rename...
491
  	struct hdmi_eld *e = entry->private_data;
903b21d8b   Wu Fengguang   ALSA: hda: make g...
492
  	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
493
  	int i;
4805286bf   Wu Fengguang   ALSA: hda - fix b...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
  	static char *eld_versoin_names[32] = {
  		"reserved",
  		"reserved",
  		"CEA-861D or below",
  		[3 ... 30] = "reserved",
  		[31] = "partial"
  	};
  	static char *cea_edid_version_names[8] = {
  		"no CEA EDID Timing Extension block present",
  		"CEA-861",
  		"CEA-861-A",
  		"CEA-861-B, C or D",
  		[4 ... 7] = "reserved"
  	};
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
508

23ccc2bd2   Wu Fengguang   ALSA: intelhdmi -...
509
510
511
512
  	snd_iprintf(buffer, "monitor_present\t\t%d
  ", e->monitor_present);
  	snd_iprintf(buffer, "eld_valid\t\t%d
  ", e->eld_valid);
5d44f927a   Stephen Warren   ALSA: HDA: Unify ...
513
514
  	if (!e->eld_valid)
  		return;
db7421047   Wu Fengguang   ALSA: hda: modify...
515
516
  	snd_iprintf(buffer, "monitor_name\t\t%s
  ", e->monitor_name);
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
  	snd_iprintf(buffer, "connection_type\t\t%s
  ",
  				eld_connection_type_names[e->conn_type]);
  	snd_iprintf(buffer, "eld_version\t\t[0x%x] %s
  ", e->eld_ver,
  					eld_versoin_names[e->eld_ver]);
  	snd_iprintf(buffer, "edid_version\t\t[0x%x] %s
  ", e->cea_edid_ver,
  				cea_edid_version_names[e->cea_edid_ver]);
  	snd_iprintf(buffer, "manufacture_id\t\t0x%x
  ", e->manufacture_id);
  	snd_iprintf(buffer, "product_id\t\t0x%x
  ", e->product_id);
  	snd_iprintf(buffer, "port_id\t\t\t0x%llx
  ", (long long)e->port_id);
  	snd_iprintf(buffer, "support_hdcp\t\t%d
  ", e->support_hdcp);
  	snd_iprintf(buffer, "support_ai\t\t%d
  ", e->support_ai);
  	snd_iprintf(buffer, "audio_sync_delay\t%d
  ", e->aud_synch_delay);
903b21d8b   Wu Fengguang   ALSA: hda: make g...
538
  	snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
06f69d17a   Wu Fengguang   ALSA: hda: minor ...
539
540
  	snd_iprintf(buffer, "speakers\t\t[0x%x]%s
  ", e->spk_alloc, buf);
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
541
542
543
544
545
546
547
  
  	snd_iprintf(buffer, "sad_count\t\t%d
  ", e->sad_count);
  
  	for (i = 0; i < e->sad_count; i++)
  		hdmi_print_sad_info(i, e->sad + i, buffer);
  }
acb059938   Wu Fengguang   ALSA: hda - ELD p...
548
  static void hdmi_write_eld_info(struct snd_info_entry *entry,
acdda7915   Wu Fengguang   ALSA: hda - suppo...
549
550
551
552
553
554
555
  				struct snd_info_buffer *buffer)
  {
  	struct hdmi_eld *e = entry->private_data;
  	char line[64];
  	char name[64];
  	char *sname;
  	long long val;
78735cffc   Roel Kluin   ALSA: hda: fix ou...
556
  	unsigned int n;
acdda7915   Wu Fengguang   ALSA: hda - suppo...
557
558
559
560
  
  	while (!snd_info_get_line(buffer, line, sizeof(line))) {
  		if (sscanf(line, "%s %llx", name, &val) != 2)
  			continue;
acb059938   Wu Fengguang   ALSA: hda - ELD p...
561
562
563
564
565
  		/*
  		 * We don't allow modification to these fields:
  		 * 	monitor_name manufacture_id product_id
  		 * 	eld_version edid_version
  		 */
23ccc2bd2   Wu Fengguang   ALSA: intelhdmi -...
566
567
568
569
570
  		if (!strcmp(name, "monitor_present"))
  			e->monitor_present = val;
  		else if (!strcmp(name, "eld_valid"))
  			e->eld_valid = val;
  		else if (!strcmp(name, "connection_type"))
acdda7915   Wu Fengguang   ALSA: hda - suppo...
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  			e->conn_type = val;
  		else if (!strcmp(name, "port_id"))
  			e->port_id = val;
  		else if (!strcmp(name, "support_hdcp"))
  			e->support_hdcp = val;
  		else if (!strcmp(name, "support_ai"))
  			e->support_ai = val;
  		else if (!strcmp(name, "audio_sync_delay"))
  			e->aud_synch_delay = val;
  		else if (!strcmp(name, "speakers"))
  			e->spk_alloc = val;
  		else if (!strcmp(name, "sad_count"))
  			e->sad_count = val;
  		else if (!strncmp(name, "sad", 3)) {
  			sname = name + 4;
  			n = name[3] - '0';
  			if (name[4] >= '0' && name[4] <= '9') {
  				sname++;
  				n = 10 * n + name[4] - '0';
  			}
78735cffc   Roel Kluin   ALSA: hda: fix ou...
591
  			if (n >= ELD_MAX_SAD)
acdda7915   Wu Fengguang   ALSA: hda - suppo...
592
593
594
595
596
597
598
599
600
601
602
  				continue;
  			if (!strcmp(sname, "_coding_type"))
  				e->sad[n].format = val;
  			else if (!strcmp(sname, "_channels"))
  				e->sad[n].channels = val;
  			else if (!strcmp(sname, "_rates"))
  				e->sad[n].rates = val;
  			else if (!strcmp(sname, "_bits"))
  				e->sad[n].sample_bits = val;
  			else if (!strcmp(sname, "_max_bitrate"))
  				e->sad[n].max_bitrate = val;
acb059938   Wu Fengguang   ALSA: hda - ELD p...
603
604
  			else if (!strcmp(sname, "_profile"))
  				e->sad[n].profile = val;
acdda7915   Wu Fengguang   ALSA: hda - suppo...
605
606
607
608
609
  			if (n >= e->sad_count)
  				e->sad_count = n + 1;
  		}
  	}
  }
54a25f87e   Wu Fengguang   ALSA: hda - vecto...
610
611
  int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
  			 int index)
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
612
613
614
615
  {
  	char name[32];
  	struct snd_info_entry *entry;
  	int err;
54a25f87e   Wu Fengguang   ALSA: hda - vecto...
616
  	snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
617
618
619
620
621
  	err = snd_card_proc_new(codec->bus->card, name, &entry);
  	if (err < 0)
  		return err;
  
  	snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
acb059938   Wu Fengguang   ALSA: hda - ELD p...
622
  	entry->c.text.write = hdmi_write_eld_info;
acdda7915   Wu Fengguang   ALSA: hda - suppo...
623
  	entry->mode |= S_IWUSR;
f208dba97   Takashi Iwai   ALSA: hda - Relea...
624
  	eld->proc_entry = entry;
acdda7915   Wu Fengguang   ALSA: hda - suppo...
625

5f1e71b1c   Wu Fengguang   ALSA: ELD proc in...
626
627
  	return 0;
  }
f208dba97   Takashi Iwai   ALSA: hda - Relea...
628
629
630
631
632
633
634
  void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
  {
  	if (!codec->bus->shutdown && eld->proc_entry) {
  		snd_device_free(codec->bus->card, eld->proc_entry);
  		eld->proc_entry = NULL;
  	}
  }
274714f55   Takashi Iwai   ALSA: hda - Fix b...
635
  #endif /* CONFIG_PROC_FS */
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
636
  /* update PCM info based on ELD */
2def8172c   Stephen Warren   ALSA: hda: hdmi_e...
637
638
  void snd_hdmi_eld_update_pcm_info(struct hdmi_eld *eld,
  			      struct hda_pcm_stream *hinfo)
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
639
  {
2def8172c   Stephen Warren   ALSA: hda: hdmi_e...
640
641
642
643
  	u32 rates;
  	u64 formats;
  	unsigned int maxbps;
  	unsigned int channels_max;
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
644
  	int i;
3dc864290   Anssi Hannula   ALSA: hda - Alway...
645
646
647
  	/* assume basic audio support (the basic audio flag is not in ELD;
  	 * however, all audio capable sinks are required to support basic
  	 * audio) */
2def8172c   Stephen Warren   ALSA: hda: hdmi_e...
648
649
650
651
652
  	rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
  		SNDRV_PCM_RATE_48000;
  	formats = SNDRV_PCM_FMTBIT_S16_LE;
  	maxbps = 16;
  	channels_max = 2;
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
653
654
  	for (i = 0; i < eld->sad_count; i++) {
  		struct cea_sad *a = &eld->sad[i];
2def8172c   Stephen Warren   ALSA: hda: hdmi_e...
655
656
657
  		rates |= a->rates;
  		if (a->channels > channels_max)
  			channels_max = a->channels;
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
658
  		if (a->format == AUDIO_CODING_TYPE_LPCM) {
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
659
  			if (a->sample_bits & AC_SUPPCM_BITS_20) {
2def8172c   Stephen Warren   ALSA: hda: hdmi_e...
660
661
662
  				formats |= SNDRV_PCM_FMTBIT_S32_LE;
  				if (maxbps < 20)
  					maxbps = 20;
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
663
664
  			}
  			if (a->sample_bits & AC_SUPPCM_BITS_24) {
2def8172c   Stephen Warren   ALSA: hda: hdmi_e...
665
666
667
  				formats |= SNDRV_PCM_FMTBIT_S32_LE;
  				if (maxbps < 24)
  					maxbps = 24;
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
668
669
670
  			}
  		}
  	}
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
671
  	/* restrict the parameters by the values the codec provides */
2def8172c   Stephen Warren   ALSA: hda: hdmi_e...
672
673
674
675
  	hinfo->rates &= rates;
  	hinfo->formats &= formats;
  	hinfo->maxbps = min(hinfo->maxbps, maxbps);
  	hinfo->channels_max = min(hinfo->channels_max, channels_max);
bbbe33900   Takashi Iwai   ALSA: hda - Restr...
676
  }