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


Quelle  nvhdmi-mcp.c   Sprache: C

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


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

enum { MODEL_2CH, MODEL_8CH };

#define Nv_VERB_SET_Channel_Allocation          0xF79
#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
#define Nv_VERB_SET_Audio_Protection_On         0xF98
#define Nv_VERB_SET_Audio_Protection_Off        0xF99

#define nvhdmi_master_con_nid_7x 0x04
#define nvhdmi_master_pin_nid_7x 0x05

static const hda_nid_t nvhdmi_con_nids_7x[4] = {
 /*front, rear, clfe, rear_surr */
 0x6, 0x8, 0xa, 0xc,
};

static const struct hda_verb nvhdmi_basic_init_7x_2ch[] = {
 /* set audio protect on */
 { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
 /* enable digital output on pin widget */
 { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 {} /* terminator */
};

static const struct hda_verb nvhdmi_basic_init_7x_8ch[] = {
 /* set audio protect on */
 { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
 /* enable digital output on pin widget */
 { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 {} /* terminator */
};

static int nvhdmi_mcp_init(struct hda_codec *codec)
{
 struct hdmi_spec *spec = codec->spec;

 if (spec->multiout.max_channels == 2)
  snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_2ch);
 else
  snd_hda_sequence_write(codec, nvhdmi_basic_init_7x_8ch);
 return 0;
}

static void nvhdmi_8ch_7x_set_info_frame_parameters(struct hda_codec *codec,
          int channels)
{
 unsigned int chanmask;
 int chan = channels ? (channels - 1) : 1;

 switch (channels) {
 default:
 case 0:
 case 2:
  chanmask = 0x00;
  break;
 case 4:
  chanmask = 0x08;
  break;
 case 6:
  chanmask = 0x0b;
  break;
 case 8:
  chanmask = 0x13;
  break;
 }

 /* Set the audio infoframe channel allocation and checksum fields.  The
 * channel count is computed implicitly by the hardware.
 */

 snd_hda_codec_write(codec, 0x1, 0,
   Nv_VERB_SET_Channel_Allocation, chanmask);

 snd_hda_codec_write(codec, 0x1, 0,
   Nv_VERB_SET_Info_Frame_Checksum,
   (0x71 - chan - chanmask));
}

static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
       struct hda_codec *codec,
       struct snd_pcm_substream *substream)
{
 struct hdmi_spec *spec = codec->spec;
 int i;

 snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
   0, AC_VERB_SET_CHANNEL_STREAMID, 0);
 for (i = 0; i < 4; i++) {
  /* set the stream id */
  snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
    AC_VERB_SET_CHANNEL_STREAMID, 0);
  /* set the stream format */
  snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
    AC_VERB_SET_STREAM_FORMAT, 0);
 }

 /* The audio hardware sends a channel count of 0x7 (8ch) when all the
 * streams are disabled.
 */

 nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);

 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}

static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
         struct hda_codec *codec,
         unsigned int stream_tag,
         unsigned int format,
         struct snd_pcm_substream *substream)
{
 int chs;
 unsigned int dataDCC2, channel_id;
 int i;
 struct hdmi_spec *spec = codec->spec;
 struct hda_spdif_out *spdif;
 struct hdmi_spec_per_cvt *per_cvt;

 mutex_lock(&codec->spdif_mutex);
 per_cvt = get_cvt(spec, 0);
 spdif = snd_hda_spdif_out_of_nid(codec, per_cvt->cvt_nid);

 chs = substream->runtime->channels;

 dataDCC2 = 0x2;

 /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
 if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE))
  snd_hda_codec_write(codec,
    nvhdmi_master_con_nid_7x,
    0,
    AC_VERB_SET_DIGI_CONVERT_1,
    spdif->ctls & ~AC_DIG1_ENABLE & 0xff);

 /* set the stream id */
 snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
   AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);

 /* set the stream format */
 snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
   AC_VERB_SET_STREAM_FORMAT, format);

 /* turn on again (if needed) */
 /* enable and set the channel status audio/data flag */
 if (codec->spdif_status_reset && (spdif->ctls & AC_DIG1_ENABLE)) {
  snd_hda_codec_write(codec,
    nvhdmi_master_con_nid_7x,
    0,
    AC_VERB_SET_DIGI_CONVERT_1,
    spdif->ctls & 0xff);
  snd_hda_codec_write(codec,
    nvhdmi_master_con_nid_7x,
    0,
    AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
 }

 for (i = 0; i < 4; i++) {
  if (chs == 2)
   channel_id = 0;
  else
   channel_id = i * 2;

  /* turn off SPDIF once;
 *otherwise the IEC958 bits won't be updated
 */

  if (codec->spdif_status_reset &&
  (spdif->ctls & AC_DIG1_ENABLE))
   snd_hda_codec_write(codec,
    nvhdmi_con_nids_7x[i],
    0,
    AC_VERB_SET_DIGI_CONVERT_1,
    spdif->ctls & ~AC_DIG1_ENABLE & 0xff);
  /* set the stream id */
  snd_hda_codec_write(codec,
    nvhdmi_con_nids_7x[i],
    0,
    AC_VERB_SET_CHANNEL_STREAMID,
    (stream_tag << 4) | channel_id);
  /* set the stream format */
  snd_hda_codec_write(codec,
    nvhdmi_con_nids_7x[i],
    0,
    AC_VERB_SET_STREAM_FORMAT,
    format);
  /* turn on again (if needed) */
  /* enable and set the channel status audio/data flag */
  if (codec->spdif_status_reset &&
  (spdif->ctls & AC_DIG1_ENABLE)) {
   snd_hda_codec_write(codec,
     nvhdmi_con_nids_7x[i],
     0,
     AC_VERB_SET_DIGI_CONVERT_1,
     spdif->ctls & 0xff);
   snd_hda_codec_write(codec,
     nvhdmi_con_nids_7x[i],
     0,
     AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
  }
 }

 nvhdmi_8ch_7x_set_info_frame_parameters(codec, chs);

 mutex_unlock(&codec->spdif_mutex);
 return 0;
}

static const struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
 .substreams = 1,
 .channels_min = 2,
 .channels_max = 8,
 .nid = nvhdmi_master_con_nid_7x,
 .rates = SUPPORTED_RATES,
 .maxbps = SUPPORTED_MAXBPS,
 .formats = SUPPORTED_FORMATS,
 .ops = {
  .open = snd_hda_hdmi_simple_pcm_open,
  .close = nvhdmi_8ch_7x_pcm_close,
  .prepare = nvhdmi_8ch_7x_pcm_prepare
 },
};

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

 err = snd_hda_hdmi_simple_build_pcms(codec);
 if (!err && spec->multiout.max_channels == 8) {
  struct hda_pcm *info = get_pcm_rec(spec, 0);

  info->own_chmap = true;
 }
 return err;
}

static int nvhdmi_mcp_build_controls(struct hda_codec *codec)
{
 struct hdmi_spec *spec = codec->spec;
 struct hda_pcm *info;
 struct snd_pcm_chmap *chmap;
 int err;

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

 if (spec->multiout.max_channels != 8)
  return 0;

 /* add channel maps */
 info = get_pcm_rec(spec, 0);
 err = snd_pcm_add_chmap_ctls(info->pcm,
         SNDRV_PCM_STREAM_PLAYBACK,
         snd_pcm_alt_chmaps, 8, 0, &chmap);
 if (err < 0)
  return err;
 switch (codec->preset->vendor_id) {
 case 0x10de0002:
 case 0x10de0003:
 case 0x10de0005:
 case 0x10de0006:
  chmap->channel_mask = (1U << 2) | (1U << 8);
  break;
 case 0x10de0007:
  chmap->channel_mask = (1U << 2) | (1U << 6) | (1U << 8);
 }
 return 0;
}

static const unsigned int channels_2_6_8[] = {
 2, 6, 8
};

static const unsigned int channels_2_8[] = {
 2, 8
};

static const struct snd_pcm_hw_constraint_list hw_constraints_2_6_8_channels = {
 .count = ARRAY_SIZE(channels_2_6_8),
 .list = channels_2_6_8,
 .mask = 0,
};

static const struct snd_pcm_hw_constraint_list hw_constraints_2_8_channels = {
 .count = ARRAY_SIZE(channels_2_8),
 .list = channels_2_8,
 .mask = 0,
};

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

 err = snd_hda_hdmi_simple_probe(codec, nvhdmi_master_con_nid_7x,
     nvhdmi_master_pin_nid_7x);
 if (err < 0)
  return err;

 /* override the PCM rates, etc, as the codec doesn't give full list */
 spec = codec->spec;
 spec->pcm_playback.rates = SUPPORTED_RATES;
 spec->pcm_playback.maxbps = SUPPORTED_MAXBPS;
 spec->pcm_playback.formats = SUPPORTED_FORMATS;
 spec->nv_dp_workaround = true;

 if (id->driver_data == MODEL_2CH)
  return 0;

 spec->multiout.max_channels = 8;
 spec->pcm_playback = nvhdmi_pcm_playback_8ch_7x;

 switch (codec->preset->vendor_id) {
 case 0x10de0002:
 case 0x10de0003:
 case 0x10de0005:
 case 0x10de0006:
  spec->hw_constraints_channels = &hw_constraints_2_8_channels;
  break;
 case 0x10de0007:
  spec->hw_constraints_channels = &hw_constraints_2_6_8_channels;
  break;
 default:
  break;
 }

 /* Initialize the audio infoframe channel mask and checksum to something
 * valid
 */

 nvhdmi_8ch_7x_set_info_frame_parameters(codec, 8);

 return 0;
}

static const struct hda_codec_ops nvhdmi_mcp_codec_ops = {
 .probe = nvhdmi_mcp_probe,
 .remove = snd_hda_hdmi_simple_remove,
 .build_pcms = nvhdmi_mcp_build_pcms,
 .build_controls = nvhdmi_mcp_build_controls,
 .init = nvhdmi_mcp_init,
 .unsol_event = snd_hda_hdmi_simple_unsol_event,
};

static const struct hda_device_id snd_hda_id_nvhdmi_mcp[] = {
 HDA_CODEC_ID_MODEL(0x10de0001, "MCP73 HDMI", MODEL_2CH),
 HDA_CODEC_ID_MODEL(0x10de0002, "MCP77/78 HDMI", MODEL_8CH),
 HDA_CODEC_ID_MODEL(0x10de0003, "MCP77/78 HDMI", MODEL_8CH),
 HDA_CODEC_ID_MODEL(0x10de0004, "GPU 04 HDMI", MODEL_8CH),
 HDA_CODEC_ID_MODEL(0x10de0005, "MCP77/78 HDMI", MODEL_8CH),
 HDA_CODEC_ID_MODEL(0x10de0006, "MCP77/78 HDMI", MODEL_8CH),
 HDA_CODEC_ID_MODEL(0x10de0007, "MCP79/7A HDMI", MODEL_8CH),
 HDA_CODEC_ID_MODEL(0x10de0067, "MCP67 HDMI", MODEL_2CH),
 HDA_CODEC_ID_MODEL(0x10de8001, "MCP73 HDMI", MODEL_2CH),
 HDA_CODEC_ID_MODEL(0x10de8067, "MCP67/68 HDMI", MODEL_2CH),
 {} /* terminator */
};
MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_nvhdmi_mcp);

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

static struct hda_codec_driver nvhdmi_mcp_driver = {
 .id = snd_hda_id_nvhdmi_mcp,
 .ops = &nvhdmi_mcp_codec_ops,
};

module_hda_codec_driver(nvhdmi_mcp_driver);

Messung V0.5
C=96 H=95 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