File Index
Files and directories
Sign in
FILE

tegrahdmi.c

File preview with syntax highlighting.

linux-6.19.14/sound/hda/codecs/hdmi/tegrahdmi.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Nvidia Tegra 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/tlv.h>
 11#include <sound/hdaudio.h>
 12#include <sound/hda_codec.h>
 13#include "hda_local.h"
 14#include "hdmi_local.h"
 15
 16enum {
 17	MODEL_TEGRA,
 18	MODEL_TEGRA234,
 19};
 20
 21/*
 22 * The HDA codec on NVIDIA Tegra contains two scratch registers that are
 23 * accessed using vendor-defined verbs. These registers can be used for
 24 * interoperability between the HDA and HDMI drivers.
 25 */
 26
 27/* Audio Function Group node */
 28#define NVIDIA_AFG_NID 0x01
 29
 30/*
 31 * The SCRATCH0 register is used to notify the HDMI codec of changes in audio
 32 * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to
 33 * be raised in the HDMI codec. The remainder of the bits is arbitrary. This
 34 * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an
 35 * additional bit (at position 30) to signal the validity of the format.
 36 *
 37 * | 31      | 30    | 29  16 | 15   0 |
 38 * +---------+-------+--------+--------+
 39 * | TRIGGER | VALID | UNUSED | FORMAT |
 40 * +-----------------------------------|
 41 *
 42 * Note that for the trigger bit to take effect it needs to change value
 43 * (i.e. it needs to be toggled). The trigger bit is not applicable from
 44 * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt
 45 * trigger to hdmi.
 46 */
 47#define NVIDIA_SET_HOST_INTR		0xf80
 48#define NVIDIA_GET_SCRATCH0		0xfa6
 49#define NVIDIA_SET_SCRATCH0_BYTE0	0xfa7
 50#define NVIDIA_SET_SCRATCH0_BYTE1	0xfa8
 51#define NVIDIA_SET_SCRATCH0_BYTE2	0xfa9
 52#define NVIDIA_SET_SCRATCH0_BYTE3	0xfaa
 53#define NVIDIA_SCRATCH_TRIGGER (1 << 7)
 54#define NVIDIA_SCRATCH_VALID   (1 << 6)
 55
 56#define NVIDIA_GET_SCRATCH1		0xfab
 57#define NVIDIA_SET_SCRATCH1_BYTE0	0xfac
 58#define NVIDIA_SET_SCRATCH1_BYTE1	0xfad
 59#define NVIDIA_SET_SCRATCH1_BYTE2	0xfae
 60#define NVIDIA_SET_SCRATCH1_BYTE3	0xfaf
 61
 62/*
 63 * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
 64 * the format is invalidated so that the HDMI codec can be disabled.
 65 */
 66static void tegra_hdmi_set_format(struct hda_codec *codec,
 67				  hda_nid_t cvt_nid,
 68				  unsigned int format)
 69{
 70	unsigned int value;
 71	unsigned int nid = NVIDIA_AFG_NID;
 72	struct hdmi_spec *spec = codec->spec;
 73
 74	/*
 75	 * Tegra HDA codec design from TEGRA234 chip onwards support DP MST.
 76	 * This resulted in moving scratch registers from audio function
 77	 * group to converter widget context. So CVT NID should be used for
 78	 * scratch register read/write for DP MST supported Tegra HDA codec.
 79	 */
 80	if (codec->dp_mst)
 81		nid = cvt_nid;
 82
 83	/* bits [31:30] contain the trigger and valid bits */
 84	value = snd_hda_codec_read(codec, nid, 0,
 85				   NVIDIA_GET_SCRATCH0, 0);
 86	value = (value >> 24) & 0xff;
 87
 88	/* bits [15:0] are used to store the HDA format */
 89	snd_hda_codec_write(codec, nid, 0,
 90			    NVIDIA_SET_SCRATCH0_BYTE0,
 91			    (format >> 0) & 0xff);
 92	snd_hda_codec_write(codec, nid, 0,
 93			    NVIDIA_SET_SCRATCH0_BYTE1,
 94			    (format >> 8) & 0xff);
 95
 96	/* bits [16:24] are unused */
 97	snd_hda_codec_write(codec, nid, 0,
 98			    NVIDIA_SET_SCRATCH0_BYTE2, 0);
 99
100	/*
101	 * Bit 30 signals that the data is valid and hence that HDMI audio can
102	 * be enabled.
103	 */
104	if (format == 0)
105		value &= ~NVIDIA_SCRATCH_VALID;
106	else
107		value |= NVIDIA_SCRATCH_VALID;
108
109	if (spec->hdmi_intr_trig_ctrl) {
110		/*
111		 * For Tegra HDA Codec design from TEGRA234 onwards, the
112		 * Interrupt to hdmi driver is triggered by writing
113		 * non-zero values to verb 0xF80 instead of 31st bit of
114		 * scratch register.
115		 */
116		snd_hda_codec_write(codec, nid, 0,
117				NVIDIA_SET_SCRATCH0_BYTE3, value);
118		snd_hda_codec_write(codec, nid, 0,
119				NVIDIA_SET_HOST_INTR, 0x1);
120	} else {
121		/*
122		 * Whenever the 31st trigger bit is toggled, an interrupt is raised
123		 * in the HDMI codec. The HDMI driver will use that as trigger
124		 * to update its configuration.
125		 */
126		value ^= NVIDIA_SCRATCH_TRIGGER;
127
128		snd_hda_codec_write(codec, nid, 0,
129				NVIDIA_SET_SCRATCH0_BYTE3, value);
130	}
131}
132
133static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
134				  struct hda_codec *codec,
135				  unsigned int stream_tag,
136				  unsigned int format,
137				  struct snd_pcm_substream *substream)
138{
139	int err;
140
141	err = snd_hda_hdmi_generic_pcm_prepare(hinfo, codec, stream_tag,
142					       format, substream);
143	if (err < 0)
144		return err;
145
146	/* notify the HDMI codec of the format change */
147	tegra_hdmi_set_format(codec, hinfo->nid, format);
148
149	return 0;
150}
151
152static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
153				  struct hda_codec *codec,
154				  struct snd_pcm_substream *substream)
155{
156	/* invalidate the format in the HDMI codec */
157	tegra_hdmi_set_format(codec, hinfo->nid, 0);
158
159	return snd_hda_hdmi_generic_pcm_cleanup(hinfo, codec, substream);
160}
161
162static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type)
163{
164	struct hdmi_spec *spec = codec->spec;
165	unsigned int i;
166
167	for (i = 0; i < spec->num_pins; i++) {
168		struct hda_pcm *pcm = get_pcm_rec(spec, i);
169
170		if (pcm->pcm_type == type)
171			return pcm;
172	}
173
174	return NULL;
175}
176
177static int tegra_hdmi_build_pcms(struct hda_codec *codec)
178{
179	struct hda_pcm_stream *stream;
180	struct hda_pcm *pcm;
181	int err;
182
183	err = snd_hda_hdmi_generic_build_pcms(codec);
184	if (err < 0)
185		return err;
186
187	pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI);
188	if (!pcm)
189		return -ENODEV;
190
191	/*
192	 * Override ->prepare() and ->cleanup() operations to notify the HDMI
193	 * codec about format changes.
194	 */
195	stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
196	stream->ops.prepare = tegra_hdmi_pcm_prepare;
197	stream->ops.cleanup = tegra_hdmi_pcm_cleanup;
198
199	return 0;
200}
201
202/*
203 * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
204 * - 0x10de0015
205 * - 0x10de0040
206 */
207static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
208		struct hdac_cea_channel_speaker_allocation *cap, int channels)
209{
210	if (cap->ca_index == 0x00 && channels == 2)
211		return SNDRV_CTL_TLVT_CHMAP_FIXED;
212
213	/* If the speaker allocation matches the channel count, it is OK. */
214	if (cap->channels != channels)
215		return -1;
216
217	/* all channels are remappable freely */
218	return SNDRV_CTL_TLVT_CHMAP_VAR;
219}
220
221static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
222		int ca, int chs, unsigned char *map)
223{
224	if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
225		return -EINVAL;
226
227	return 0;
228}
229
230static int tegra_hdmi_init(struct hda_codec *codec)
231{
232	struct hdmi_spec *spec = codec->spec;
233	int i, err;
234
235	err = snd_hda_hdmi_parse_codec(codec);
236	if (err < 0) {
237		snd_hda_hdmi_generic_spec_free(codec);
238		return err;
239	}
240
241	for (i = 0; i < spec->num_cvts; i++)
242		snd_hda_codec_write(codec, spec->cvt_nids[i], 0,
243					AC_VERB_SET_DIGI_CONVERT_1,
244					AC_DIG1_ENABLE);
245
246	snd_hda_hdmi_generic_init_per_pins(codec);
247
248	codec->depop_delay = 10;
249	spec->chmap.ops.chmap_cea_alloc_validate_get_type =
250		nvhdmi_chmap_cea_alloc_validate_get_type;
251	spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
252
253	spec->chmap.ops.chmap_cea_alloc_validate_get_type =
254		nvhdmi_chmap_cea_alloc_validate_get_type;
255	spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
256	spec->nv_dp_workaround = true;
257
258	return 0;
259}
260
261static int tegrahdmi_probe(struct hda_codec *codec,
262			   const struct hda_device_id *id)
263{
264	struct hdmi_spec *spec;
265	int err;
266
267	err = snd_hda_hdmi_generic_alloc(codec);
268	if (err < 0)
269		return err;
270
271	if (id->driver_data == MODEL_TEGRA234) {
272		codec->dp_mst = true;
273		spec = codec->spec;
274		spec->dyn_pin_out = true;
275		spec->hdmi_intr_trig_ctrl = true;
276	}
277
278	return tegra_hdmi_init(codec);
279}
280
281static const struct hda_codec_ops tegrahdmi_codec_ops = {
282	.probe = tegrahdmi_probe,
283	.remove = snd_hda_hdmi_generic_remove,
284	.init = snd_hda_hdmi_generic_init,
285	.build_pcms = tegra_hdmi_build_pcms,
286	.build_controls = snd_hda_hdmi_generic_build_controls,
287	.unsol_event = snd_hda_hdmi_generic_unsol_event,
288	.suspend = snd_hda_hdmi_generic_suspend,
289	.resume = snd_hda_hdmi_generic_resume,
290};
291
292static const struct hda_device_id snd_hda_id_tegrahdmi[] = {
293	HDA_CODEC_ID_MODEL(0x10de0020, "Tegra30 HDMI",		MODEL_TEGRA),
294	HDA_CODEC_ID_MODEL(0x10de0022, "Tegra114 HDMI",		MODEL_TEGRA),
295	HDA_CODEC_ID_MODEL(0x10de0028, "Tegra124 HDMI",		MODEL_TEGRA),
296	HDA_CODEC_ID_MODEL(0x10de0029, "Tegra210 HDMI/DP",	MODEL_TEGRA),
297	HDA_CODEC_ID_MODEL(0x10de002d, "Tegra186 HDMI/DP0",	MODEL_TEGRA),
298	HDA_CODEC_ID_MODEL(0x10de002e, "Tegra186 HDMI/DP1",	MODEL_TEGRA),
299	HDA_CODEC_ID_MODEL(0x10de002f, "Tegra194 HDMI/DP2",	MODEL_TEGRA),
300	HDA_CODEC_ID_MODEL(0x10de0030, "Tegra194 HDMI/DP3",	MODEL_TEGRA),
301	HDA_CODEC_ID_MODEL(0x10de0031, "Tegra234 HDMI/DP",	MODEL_TEGRA234),
302	HDA_CODEC_ID_MODEL(0x10de0032, "Tegra238 HDMI/DP",	MODEL_TEGRA234),
303	HDA_CODEC_ID_MODEL(0x10de0033, "SoC 33 HDMI/DP",	MODEL_TEGRA234),
304	HDA_CODEC_ID_MODEL(0x10de0034, "Tegra264 HDMI/DP",	MODEL_TEGRA234),
305	HDA_CODEC_ID_MODEL(0x10de0035, "SoC 35 HDMI/DP",	MODEL_TEGRA234),
306	{} /* terminator */
307};
308MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_tegrahdmi);
309
310MODULE_LICENSE("GPL");
311MODULE_DESCRIPTION("Nvidia Tegra HDMI HD-audio codec");
312MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");
313
314static struct hda_codec_driver tegrahdmi_driver = {
315	.id = snd_hda_id_tegrahdmi,
316	.ops = &tegrahdmi_codec_ops,
317};
318
319module_hda_codec_driver(tegrahdmi_driver);
~/ Read-only file preview.