Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/hda/codecs/hdmi/nvhdmi-mcp.c
26490 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Legacy Nvidia HDMI codec support
4
*/
5
6
#include <linux/init.h>
7
#include <linux/slab.h>
8
#include <linux/module.h>
9
#include <sound/core.h>
10
#include <sound/hdaudio.h>
11
#include <sound/hda_codec.h>
12
#include "hda_local.h"
13
#include "hdmi_local.h"
14
15
enum { MODEL_2CH, MODEL_8CH };
16
17
#define Nv_VERB_SET_Channel_Allocation 0xF79
18
#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
19
#define Nv_VERB_SET_Audio_Protection_On 0xF98
20
#define Nv_VERB_SET_Audio_Protection_Off 0xF99
21
22
#define nvhdmi_master_con_nid_7x 0x04
23
#define nvhdmi_master_pin_nid_7x 0x05
24
25
static const hda_nid_t nvhdmi_con_nids_7x[4] = {
26
/*front, rear, clfe, rear_surr */
27
0x6, 0x8, 0xa, 0xc,
28
};
29
30
static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = {
31
/* set audio protect on */
32
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
33
/* enable digital output on pin widget */
34
{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
35
{} /* terminator */
36
};
37
38
static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = {
39
/* set audio protect on */
40
{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
41
/* enable digital output on pin widget */
42
{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
43
{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
44
{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
45
{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
46
{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
47
{} /* terminator */
48
};
49
50
static int nvhdmi_mcp_init(struct hda_codec *codec)
51
{
52
struct hdmi_spec *spec = codec->spec;
53
54
if (spec->multiout.max_channels == 2)
55
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
56
else
57
snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
58
return 0;
59
}
60
61
static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
62
int channels)
63
{
64
unsigned int chanmask;
65
int chan = channels ? (channels - 1) : 1;
66
67
switch (channels) {
68
default:
69
case 0:
70
case 2:
71
chanmask = 0x00;
72
break;
73
case 4:
74
chanmask = 0x08;
75
break;
76
case 6:
77
chanmask = 0x0b;
78
break;
79
case 8:
80
chanmask = 0x13;
81
break;
82
}
83
84
/* Set the audio infoframe channel allocation and checksum fields. The
85
* channel count is computed implicitly by the hardware.
86
*/
87
snd_hda_codec_write(codec, 0x1, 0,
88
Nv_VERB_SET_Channel_Allocation, chanmask);
89
90
snd_hda_codec_write(codec, 0x1, 0,
91
Nv_VERB_SET_Info_Frame_Checksum,
92
(0x71 - chan - chanmask));
93
}
94
95
static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
96
struct hda_codec *codec,
97
struct snd_pcm_substream *substream)
98
{
99
struct hdmi_spec *spec = codec->spec;
100
int i;
101
102
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
103
0, AC_VERB_SET_CHANNEL_STREAMID, 0);
104
for (i = 0; i < 4; i++) {
105
/* set the stream id */
106
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
107
AC_VERB_SET_CHANNEL_STREAMID, 0);
108
/* set the stream format */
109
snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
110
AC_VERB_SET_STREAM_FORMAT, 0);
111
}
112
113
/* The audio hardware sends a channel count of 0x7 (8ch) when all the
114
* streams are disabled.
115
*/
116
nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
117
118
return snd_hda_multi_out_dig_close(codec, &spec->multiout);
119
}
120
121
static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
122
struct hda_codec *codec,
123
unsigned int stream_tag,
124
unsigned int format,
125
struct snd_pcm_substream *substream)
126
{
127
int chs;
128
unsigned int dataDCC2, channel_id;
129
int i;
130
struct hdmi_spec *spec = codec->spec;
131
struct hda_spdif_out *spdif;
132
struct hdmi_spec_per_cvt *per_cvt;
133
134
mutex_lock(&codec->spdif_mutex);
135
per_cvt = get_cvt(spec, 0);
136
spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);
137
138
chs = substream->runtime->channels;
139
140
dataDCC2 = 0x2;
141
142
/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
143
if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
144
snd_hda_codec_write(codec,
145
nvhdmi_master_con_nid_7x,
146
0,
147
AC_VERB_SET_DIGI_CONVERT_1,
148
spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
149
150
/* set the stream id */
151
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
152
AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
153
154
/* set the stream format */
155
snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
156
AC_VERB_SET_STREAM_FORMAT, format);
157
158
/* turn on again (if needed) */
159
/* enable and set the channel status audio/data flag */
160
if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) {
161
snd_hda_codec_write(codec,
162
nvhdmi_master_con_nid_7x,
163
0,
164
AC_VERB_SET_DIGI_CONVERT_1,
165
spdif->ctls & 0xff);
166
snd_hda_codec_write(codec,
167
nvhdmi_master_con_nid_7x,
168
0,
169
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
170
}
171
172
for (i = 0; i < 4; i++) {
173
if (chs == 2)
174
channel_id = 0;
175
else
176
channel_id = i * 2;
177
178
/* turn off SPDIF once;
179
*otherwise the IEC958 bits won't be updated
180
*/
181
if (codec->spdif_status_reset &&
182
(spdif->ctls & AC_DIG1_ENABLE))
183
snd_hda_codec_write(codec,
184
nvhdmi_con_nids_7x[i],
185
0,
186
AC_VERB_SET_DIGI_CONVERT_1,
187
spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
188
/* set the stream id */
189
snd_hda_codec_write(codec,
190
nvhdmi_con_nids_7x[i],
191
0,
192
AC_VERB_SET_CHANNEL_STREAMID,
193
(stream_tag << 4) | channel_id);
194
/* set the stream format */
195
snd_hda_codec_write(codec,
196
nvhdmi_con_nids_7x[i],
197
0,
198
AC_VERB_SET_STREAM_FORMAT,
199
format);
200
/* turn on again (if needed) */
201
/* enable and set the channel status audio/data flag */
202
if (codec->spdif_status_reset &&
203
(spdif->ctls & AC_DIG1_ENABLE)) {
204
snd_hda_codec_write(codec,
205
nvhdmi_con_nids_7x[i],
206
0,
207
AC_VERB_SET_DIGI_CONVERT_1,
208
spdif->ctls & 0xff);
209
snd_hda_codec_write(codec,
210
nvhdmi_con_nids_7x[i],
211
0,
212
AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
213
}
214
}
215
216
nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs);
217
218
mutex_unlock(&codec->spdif_mutex);
219
return 0;
220
}
221
222
static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
223
.substreams = 1,
224
.channels_min = 2,
225
.channels_max = 8,
226
.nid = nvhdmi_master_con_nid_7x,
227
.rates = SUPPORTED_RATES,
228
.maxbps = SUPPORTED_MAXBPS,
229
.formats = SUPPORTED_FORMATS,
230
.ops = {
231
.open = snd_hda_hdmi_simple_pcm_open,
232
.close = nvhdmi_8ch_7x_pcm_close,
233
.prepare = nvhdmi_8ch_7x_pcm_prepare
234
},
235
};
236
237
static int nvhdmi_mcp_build_pcms(struct hda_codec *codec)
238
{
239
struct hdmi_spec *spec = codec->spec;
240
int err;
241
242
err = snd_hda_hdmi_simple_build_pcms(codec);
243
if (!err && spec->multiout.max_channels == 8) {
244
struct hda_pcm *info = get_pcm_rec(spec, 0);
245
246
info->own_chmap = true;
247
}
248
return err;
249
}
250
251
static int nvhdmi_mcp_build_controls(struct hda_codec *codec)
252
{
253
struct hdmi_spec *spec = codec->spec;
254
struct hda_pcm *info;
255
struct snd_pcm_chmap *chmap;
256
int err;
257
258
err = snd_hda_hdmi_simple_build_controls(codec);
259
if (err < 0)
260
return err;
261
262
if (spec->multiout.max_channels != 8)
263
return 0;
264
265
/* add channel maps */
266
info = get_pcm_rec(spec, 0);
267
err = snd_pcm_add_chmap_ctls(info->pcm,
268
SNDRV_PCM_STREAM_PLAYBACK,
269
snd_pcm_alt_chmaps, 8, 0, &chmap);
270
if (err < 0)
271
return err;
272
switch (codec->preset->vendor_id) {
273
case 0x10de0002:
274
case 0x10de0003:
275
case 0x10de0005:
276
case 0x10de0006:
277
chmap->channel_mask = (1U << 2) | (1U << 8);
278
break;
279
case 0x10de0007:
280
chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8);
281
}
282
return 0;
283
}
284
285
static const unsigned int channels_2_6_8[] = {
286
2, 6, 8
287
};
288
289
static const unsigned int channels_2_8[] = {
290
2, 8
291
};
292
293
static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = {
294
.count = ARRAY_SIZE(channels_2_6_8),
295
.list = channels_2_6_8,
296
.mask = 0,
297
};
298
299
static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = {
300
.count = ARRAY_SIZE(channels_2_8),
301
.list = channels_2_8,
302
.mask = 0,
303
};
304
305
static int nvhdmi_mcp_probe(struct hda_codec *codec,
306
const struct hda_device_id *id)
307
{
308
struct hdmi_spec *spec;
309
int err;
310
311
err = snd_hda_hdmi_simple_probe(codec, nvhdmi_master_con_nid_7x,
312
nvhdmi_master_pin_nid_7x);
313
if (err < 0)
314
return err;
315
316
/* override the PCM rates, etc, as the codec doesn't give full list */
317
spec = codec->spec;
318
spec->pcm_playback.rates = SUPPORTED_RATES;
319
spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
320
spec->pcm_playback.formats = SUPPORTED_FORMATS;
321
spec->nv_dp_workaround = true;
322
323
if (id->driver_data == MODEL_2CH)
324
return 0;
325
326
spec->multiout.max_channels = 8;
327
spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
328
329
switch (codec->preset->vendor_id) {
330
case 0x10de0002:
331
case 0x10de0003:
332
case 0x10de0005:
333
case 0x10de0006:
334
spec->hw_constraints_channels = &hw_constraints_2_8_channels;
335
break;
336
case 0x10de0007:
337
spec->hw_constraints_channels = &hw_constraints_2_6_8_channels;
338
break;
339
default:
340
break;
341
}
342
343
/* Initialize the audio infoframe channel mask and checksum to something
344
* valid
345
*/
346
nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
347
348
return 0;
349
}
350
351
static const struct hda_codec_ops nvhdmi_mcp_codec_ops = {
352
.probe = nvhdmi_mcp_probe,
353
.remove = snd_hda_hdmi_simple_remove,
354
.build_controls = nvhdmi_mcp_build_pcms,
355
.build_pcms = nvhdmi_mcp_build_controls,
356
.init = nvhdmi_mcp_init,
357
.unsol_event = snd_hda_hdmi_simple_unsol_event,
358
};
359
360
static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = {
361
HDA_CODEC_ID_MODEL(0x10de0001, "MCP73 HDMI", MODEL_2CH),
362
HDA_CODEC_ID_MODEL(0x10de0002, "MCP77/78 HDMI", MODEL_8CH),
363
HDA_CODEC_ID_MODEL(0x10de0003, "MCP77/78 HDMI", MODEL_8CH),
364
HDA_CODEC_ID_MODEL(0x10de0004, "GPU 04 HDMI", MODEL_8CH),
365
HDA_CODEC_ID_MODEL(0x10de0005, "MCP77/78 HDMI", MODEL_8CH),
366
HDA_CODEC_ID_MODEL(0x10de0006, "MCP77/78 HDMI", MODEL_8CH),
367
HDA_CODEC_ID_MODEL(0x10de0007, "MCP79/7A HDMI", MODEL_8CH),
368
HDA_CODEC_ID_MODEL(0x10de0067, "MCP67 HDMI", MODEL_2CH),
369
HDA_CODEC_ID_MODEL(0x10de8001, "MCP73 HDMI", MODEL_2CH),
370
HDA_CODEC_ID_MODEL(0x10de8067, "MCP67/68 HDMI", MODEL_2CH),
371
{} /* terminator */
372
};
373
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp);
374
375
MODULE_LICENSE("GPL");
376
MODULE_DESCRIPTION("Legacy Nvidia HDMI HD-audio codec");
377
MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
378
379
static struct hda_codec_driver nvhdmi_mcp_driver = {
380
.id = snd_hda_id_nvhdmi_mcp,
381
.ops = &nvhdmi_mcp_codec_ops,
382
};
383
384
module_hda_codec_driver(nvhdmi_mcp_driver);
385
386