File Index
Files and directories
Sign in
FILE

nvhdmi-mcp.c

File preview with syntax highlighting.

linux-6.19.14/sound/hda/codecs/hdmi/nvhdmi-mcp.c C
  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
 15enum { 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
 25static const hda_nid_t nvhdmi_con_nids_7x[4] = {
 26	/*front, rear, clfe, rear_surr */
 27	0x6, 0x8, 0xa, 0xc,
 28};
 29
 30static 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
 38static 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
 50static 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
 61static 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
 95static 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
121static 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	guard(mutex)(&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	return 0;
219}
220
221static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
222	.substreams = 1,
223	.channels_min = 2,
224	.channels_max = 8,
225	.nid = nvhdmi_master_con_nid_7x,
226	.rates = SUPPORTED_RATES,
227	.maxbps = SUPPORTED_MAXBPS,
228	.formats = SUPPORTED_FORMATS,
229	.ops = {
230		.open = snd_hda_hdmi_simple_pcm_open,
231		.close = nvhdmi_8ch_7x_pcm_close,
232		.prepare = nvhdmi_8ch_7x_pcm_prepare
233	},
234};
235
236static int nvhdmi_mcp_build_pcms(struct hda_codec *codec)
237{
238	struct hdmi_spec *spec = codec->spec;
239	int err;
240
241	err = snd_hda_hdmi_simple_build_pcms(codec);
242	if (!err && spec->multiout.max_channels == 8) {
243		struct hda_pcm *info = get_pcm_rec(spec, 0);
244
245		info->own_chmap = true;
246	}
247	return err;
248}
249
250static int nvhdmi_mcp_build_controls(struct hda_codec *codec)
251{
252	struct hdmi_spec *spec = codec->spec;
253	struct hda_pcm *info;
254	struct snd_pcm_chmap *chmap;
255	int err;
256
257	err = snd_hda_hdmi_simple_build_controls(codec);
258	if (err < 0)
259		return err;
260
261	if (spec->multiout.max_channels != 8)
262		return 0;
263
264	/* add channel maps */
265	info = get_pcm_rec(spec, 0);
266	err = snd_pcm_add_chmap_ctls(info->pcm,
267				     SNDRV_PCM_STREAM_PLAYBACK,
268				     snd_pcm_alt_chmaps, 8, 0, &chmap);
269	if (err < 0)
270		return err;
271	switch (codec->preset->vendor_id) {
272	case 0x10de0002:
273	case 0x10de0003:
274	case 0x10de0005:
275	case 0x10de0006:
276		chmap->channel_mask = (1U << 2) | (1U << 8);
277		break;
278	case 0x10de0007:
279		chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8);
280	}
281	return 0;
282}
283
284static const unsigned int channels_2_6_8[] = {
285	2, 6, 8
286};
287
288static const unsigned int channels_2_8[] = {
289	2, 8
290};
291
292static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = {
293	.count = ARRAY_SIZE(channels_2_6_8),
294	.list = channels_2_6_8,
295	.mask = 0,
296};
297
298static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = {
299	.count = ARRAY_SIZE(channels_2_8),
300	.list = channels_2_8,
301	.mask = 0,
302};
303
304static int nvhdmi_mcp_probe(struct hda_codec *codec,
305			    const struct hda_device_id *id)
306{
307	struct hdmi_spec *spec;
308	int err;
309
310	err = snd_hda_hdmi_simple_probe(codec, nvhdmi_master_con_nid_7x,
311					nvhdmi_master_pin_nid_7x);
312	if (err < 0)
313		return err;
314
315	/* override the PCM rates, etc, as the codec doesn't give full list */
316	spec = codec->spec;
317	spec->pcm_playback.rates = SUPPORTED_RATES;
318	spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
319	spec->pcm_playback.formats = SUPPORTED_FORMATS;
320	spec->nv_dp_workaround = true;
321
322	if (id->driver_data == MODEL_2CH)
323		return 0;
324
325	spec->multiout.max_channels = 8;
326	spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;
327
328	switch (codec->preset->vendor_id) {
329	case 0x10de0002:
330	case 0x10de0003:
331	case 0x10de0005:
332	case 0x10de0006:
333		spec->hw_constraints_channels = &hw_constraints_2_8_channels;
334		break;
335	case 0x10de0007:
336		spec->hw_constraints_channels = &hw_constraints_2_6_8_channels;
337		break;
338	default:
339		break;
340	}
341
342	/* Initialize the audio infoframe channel mask and checksum to something
343	 * valid
344	 */
345	nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);
346
347	return 0;
348}
349
350static const struct hda_codec_ops nvhdmi_mcp_codec_ops = {
351	.probe = nvhdmi_mcp_probe,
352	.remove = snd_hda_hdmi_simple_remove,
353	.build_pcms = nvhdmi_mcp_build_pcms,
354	.build_controls = nvhdmi_mcp_build_controls,
355	.init = nvhdmi_mcp_init,
356	.unsol_event = snd_hda_hdmi_simple_unsol_event,
357};
358
359static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = {
360	HDA_CODEC_ID_MODEL(0x10de0001, "MCP73 HDMI",	MODEL_2CH),
361	HDA_CODEC_ID_MODEL(0x10de0002, "MCP77/78 HDMI",	MODEL_8CH),
362	HDA_CODEC_ID_MODEL(0x10de0003, "MCP77/78 HDMI",	MODEL_8CH),
363	HDA_CODEC_ID_MODEL(0x10de0004, "GPU 04 HDMI",	MODEL_8CH),
364	HDA_CODEC_ID_MODEL(0x10de0005, "MCP77/78 HDMI",	MODEL_8CH),
365	HDA_CODEC_ID_MODEL(0x10de0006, "MCP77/78 HDMI",	MODEL_8CH),
366	HDA_CODEC_ID_MODEL(0x10de0007, "MCP79/7A HDMI",	MODEL_8CH),
367	HDA_CODEC_ID_MODEL(0x10de0067, "MCP67 HDMI",	MODEL_2CH),
368	HDA_CODEC_ID_MODEL(0x10de8001, "MCP73 HDMI",	MODEL_2CH),
369	HDA_CODEC_ID_MODEL(0x10de8067, "MCP67/68 HDMI",	MODEL_2CH),
370	{} /* terminator */
371};
372MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp);
373
374MODULE_LICENSE("GPL");
375MODULE_DESCRIPTION("Legacy Nvidia HDMI HD-audio codec");
376MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
377
378static struct hda_codec_driver nvhdmi_mcp_driver = {
379	.id = snd_hda_id_nvhdmi_mcp,
380	.ops = &nvhdmi_mcp_codec_ops,
381};
382
383module_hda_codec_driver(nvhdmi_mcp_driver);
~/ Read-only file preview.