Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  tegrahdmi.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Nvidia Tegra HDMI codec support
 */


#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/hdaudio.h>
#include <sound/hda_codec.h>
#include "hda_local.h"
#include "hdmi_local.h"

enum {
 MODEL_TEGRA,
 MODEL_TEGRA234,
};

/*
 * The HDA codec on NVIDIA Tegra contains two scratch registers that are
 * accessed using vendor-defined verbs. These registers can be used for
 * interoperability between the HDA and HDMI drivers.
 */


/* Audio Function Group node */
#define NVIDIA_AFG_NID 0x01

/*
 * The SCRATCH0 register is used to notify the HDMI codec of changes in audio
 * format. On Tegra, bit 31 is used as a trigger that causes an interrupt to
 * be raised in the HDMI codec. The remainder of the bits is arbitrary. This
 * implementation stores the HDA format (see AC_FMT_*) in bits [15:0] and an
 * additional bit (at position 30) to signal the validity of the format.
 *
 * | 31      | 30    | 29  16 | 15   0 |
 * +---------+-------+--------+--------+
 * | TRIGGER | VALID | UNUSED | FORMAT |
 * +-----------------------------------|
 *
 * Note that for the trigger bit to take effect it needs to change value
 * (i.e. it needs to be toggled). The trigger bit is not applicable from
 * TEGRA234 chip onwards, as new verb id 0xf80 will be used for interrupt
 * trigger to hdmi.
 */

#define NVIDIA_SET_HOST_INTR  0xf80
#define NVIDIA_GET_SCRATCH0  0xfa6
#define NVIDIA_SET_SCRATCH0_BYTE0 0xfa7
#define NVIDIA_SET_SCRATCH0_BYTE1 0xfa8
#define NVIDIA_SET_SCRATCH0_BYTE2 0xfa9
#define NVIDIA_SET_SCRATCH0_BYTE3 0xfaa
#define NVIDIA_SCRATCH_TRIGGER (1 << 7)
#define NVIDIA_SCRATCH_VALID   (1 << 6)

#define NVIDIA_GET_SCRATCH1  0xfab
#define NVIDIA_SET_SCRATCH1_BYTE0 0xfac
#define NVIDIA_SET_SCRATCH1_BYTE1 0xfad
#define NVIDIA_SET_SCRATCH1_BYTE2 0xfae
#define NVIDIA_SET_SCRATCH1_BYTE3 0xfaf

/*
 * The format parameter is the HDA audio format (see AC_FMT_*). If set to 0,
 * the format is invalidated so that the HDMI codec can be disabled.
 */

static void tegra_hdmi_set_format(struct hda_codec *codec,
      hda_nid_t cvt_nid,
      unsigned int format)
{
 unsigned int value;
 unsigned int nid = NVIDIA_AFG_NID;
 struct hdmi_spec *spec = codec->spec;

 /*
 * Tegra HDA codec design from TEGRA234 chip onwards support DP MST.
 * This resulted in moving scratch registers from audio function
 * group to converter widget context. So CVT NID should be used for
 * scratch register read/write for DP MST supported Tegra HDA codec.
 */

 if (codec->dp_mst)
  nid = cvt_nid;

 /* bits [31:30] contain the trigger and valid bits */
 value = snd_hda_codec_read(codec, nid, 0,
       NVIDIA_GET_SCRATCH0, 0);
 value = (value >> 24) & 0xff;

 /* bits [15:0] are used to store the HDA format */
 snd_hda_codec_write(codec, nid, 0,
       NVIDIA_SET_SCRATCH0_BYTE0,
       (format >> 0) & 0xff);
 snd_hda_codec_write(codec, nid, 0,
       NVIDIA_SET_SCRATCH0_BYTE1,
       (format >> 8) & 0xff);

 /* bits [16:24] are unused */
 snd_hda_codec_write(codec, nid, 0,
       NVIDIA_SET_SCRATCH0_BYTE2, 0);

 /*
 * Bit 30 signals that the data is valid and hence that HDMI audio can
 * be enabled.
 */

 if (format == 0)
  value &= ~NVIDIA_SCRATCH_VALID;
 else
  value |= NVIDIA_SCRATCH_VALID;

 if (spec->hdmi_intr_trig_ctrl) {
  /*
 * For Tegra HDA Codec design from TEGRA234 onwards, the
 * Interrupt to hdmi driver is triggered by writing
 * non-zero values to verb 0xF80 instead of 31st bit of
 * scratch register.
 */

  snd_hda_codec_write(codec, nid, 0,
    NVIDIA_SET_SCRATCH0_BYTE3, value);
  snd_hda_codec_write(codec, nid, 0,
    NVIDIA_SET_HOST_INTR, 0x1);
 } else {
  /*
 * Whenever the 31st trigger bit is toggled, an interrupt is raised
 * in the HDMI codec. The HDMI driver will use that as trigger
 * to update its configuration.
 */

  value ^= NVIDIA_SCRATCH_TRIGGER;

  snd_hda_codec_write(codec, nid, 0,
    NVIDIA_SET_SCRATCH0_BYTE3, value);
 }
}

static int tegra_hdmi_pcm_prepare(struct hda_pcm_stream *hinfo,
      struct hda_codec *codec,
      unsigned int stream_tag,
      unsigned int format,
      struct snd_pcm_substream *substream)
{
 int err;

 err = snd_hda_hdmi_generic_pcm_prepare(hinfo, codec, stream_tag,
            format, substream);
 if (err < 0)
  return err;

 /* notify the HDMI codec of the format change */
 tegra_hdmi_set_format(codec, hinfo->nid, format);

 return 0;
}

static int tegra_hdmi_pcm_cleanup(struct hda_pcm_stream *hinfo,
      struct hda_codec *codec,
      struct snd_pcm_substream *substream)
{
 /* invalidate the format in the HDMI codec */
 tegra_hdmi_set_format(codec, hinfo->nid, 0);

 return snd_hda_hdmi_generic_pcm_cleanup(hinfo, codec, substream);
}

static struct hda_pcm *hda_find_pcm_by_type(struct hda_codec *codec, int type)
{
 struct hdmi_spec *spec = codec->spec;
 unsigned int i;

 for (i = 0; i < spec->num_pins; i++) {
  struct hda_pcm *pcm = get_pcm_rec(spec, i);

  if (pcm->pcm_type == type)
   return pcm;
 }

 return NULL;
}

static int tegra_hdmi_build_pcms(struct hda_codec *codec)
{
 struct hda_pcm_stream *stream;
 struct hda_pcm *pcm;
 int err;

 err = snd_hda_hdmi_generic_build_pcms(codec);
 if (err < 0)
  return err;

 pcm = hda_find_pcm_by_type(codec, HDA_PCM_TYPE_HDMI);
 if (!pcm)
  return -ENODEV;

 /*
 * Override ->prepare() and ->cleanup() operations to notify the HDMI
 * codec about format changes.
 */

 stream = &pcm->stream[SNDRV_PCM_STREAM_PLAYBACK];
 stream->ops.prepare = tegra_hdmi_pcm_prepare;
 stream->ops.cleanup = tegra_hdmi_pcm_cleanup;

 return 0;
}

/*
 * NVIDIA codecs ignore ASP mapping for 2ch - confirmed on:
 * - 0x10de0015
 * - 0x10de0040
 */

static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
  struct hdac_cea_channel_speaker_allocation *cap, int channels)
{
 if (cap->ca_index == 0x00 && channels == 2)
  return SNDRV_CTL_TLVT_CHMAP_FIXED;

 /* If the speaker allocation matches the channel count, it is OK. */
 if (cap->channels != channels)
  return -1;

 /* all channels are remappable freely */
 return SNDRV_CTL_TLVT_CHMAP_VAR;
}

static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
  int ca, int chs, unsigned char *map)
{
 if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
  return -EINVAL;

 return 0;
}

static int tegra_hdmi_init(struct hda_codec *codec)
{
 struct hdmi_spec *spec = codec->spec;
 int i, err;

 err = snd_hda_hdmi_parse_codec(codec);
 if (err < 0) {
  snd_hda_hdmi_generic_spec_free(codec);
  return err;
 }

 for (i = 0; i < spec->num_cvts; i++)
  snd_hda_codec_write(codec, spec->cvt_nids[i], 0,
     AC_VERB_SET_DIGI_CONVERT_1,
     AC_DIG1_ENABLE);

 snd_hda_hdmi_generic_init_per_pins(codec);

 codec->depop_delay = 10;
 spec->chmap.ops.chmap_cea_alloc_validate_get_type =
  nvhdmi_chmap_cea_alloc_validate_get_type;
 spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;

 spec->chmap.ops.chmap_cea_alloc_validate_get_type =
  nvhdmi_chmap_cea_alloc_validate_get_type;
 spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
 spec->nv_dp_workaround = true;

 return 0;
}

static int tegrahdmi_probe(struct hda_codec *codec,
      const struct hda_device_id *id)
{
 struct hdmi_spec *spec;
 int err;

 err = snd_hda_hdmi_generic_alloc(codec);
 if (err < 0)
  return err;

 if (id->driver_data == MODEL_TEGRA234) {
  codec->dp_mst = true;
  spec = codec->spec;
  spec->dyn_pin_out = true;
  spec->hdmi_intr_trig_ctrl = true;
 }

 return tegra_hdmi_init(codec);
}

static const struct hda_codec_ops tegrahdmi_codec_ops = {
 .probe = tegrahdmi_probe,
 .remove = snd_hda_hdmi_generic_remove,
 .init = snd_hda_hdmi_generic_init,
 .build_pcms = tegra_hdmi_build_pcms,
 .build_controls = snd_hda_hdmi_generic_build_controls,
 .unsol_event = snd_hda_hdmi_generic_unsol_event,
 .suspend = snd_hda_hdmi_generic_suspend,
 .resume = snd_hda_hdmi_generic_resume,
};

static const struct hda_device_id snd_hda_id_tegrahdmi[] = {
 HDA_CODEC_ID_MODEL(0x10de0020, "Tegra30 HDMI",  MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de0022, "Tegra114 HDMI",  MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de0028, "Tegra124 HDMI",  MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de0029, "Tegra210 HDMI/DP", MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de002d, "Tegra186 HDMI/DP0", MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de002e, "Tegra186 HDMI/DP1", MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de002f, "Tegra194 HDMI/DP2", MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de0030, "Tegra194 HDMI/DP3", MODEL_TEGRA),
 HDA_CODEC_ID_MODEL(0x10de0031, "Tegra234 HDMI/DP", MODEL_TEGRA234),
 HDA_CODEC_ID_MODEL(0x10de0033, "SoC 33 HDMI/DP", MODEL_TEGRA234),
 HDA_CODEC_ID_MODEL(0x10de0034, "Tegra264 HDMI/DP", MODEL_TEGRA234),
 HDA_CODEC_ID_MODEL(0x10de0035, "SoC 35 HDMI/DP", MODEL_TEGRA234),
 {} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_tegrahdmi);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Nvidia Tegra HDMI HD-audio codec");
MODULE_IMPORT_NS("SND_HDA_CODEC_HDMI");

static struct hda_codec_driver tegrahdmi_driver = {
 .id = snd_hda_id_tegrahdmi,
 .ops = &tegrahdmi_codec_ops,
};

module_hda_codec_driver(tegrahdmi_driver);

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge