/* first three bytes are just standard SAD */ #define ATI_AUDIODESC_CHANNELS 0x00000007 #define ATI_AUDIODESC_RATES 0x0000ff00 #define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000
/* in standard HDMI VSDB format */ #define ATI_DELAY_VIDEO_LATENCY 0x000000ff #define ATI_DELAY_AUDIO_LATENCY 0x0000ff00
if (sink_desc_len > ELD_MAX_MNL) {
codec_info(codec, "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n",
sink_desc_len);
sink_desc_len = ELD_MAX_MNL;
}
buf[4] |= sink_desc_len;
for (i = 0; i < sink_desc_len; i++) {
snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i);
buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0);
}
}
for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) continue; /* not handled by ATI/AMD */
if (ati_sad & ATI_AUDIODESC_RATES) { /* format is supported, copy SAD as-is */
buf[pos++] = (ati_sad & 0x0000ff) >> 0;
buf[pos++] = (ati_sad & 0x00ff00) >> 8;
buf[pos++] = (ati_sad & 0xff0000) >> 16;
}
if (i == AUDIO_CODING_TYPE_LPCM
&& (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES)
&& (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { /* for PCM there is a separate stereo rate mask */
buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; /* rates from the extra byte */
buf[pos++] = (ati_sad & 0xff000000) >> 24;
buf[pos++] = (ati_sad & 0x00ff0000) >> 16;
}
}
if (pos == ELD_FIXED_BYTES + sink_desc_len) {
codec_info(codec, "HDMI ATI/AMD: no audio descriptors for ELD\n"); return -EINVAL;
}
/* * HDMI VSDB latency format: * separately for both audio and video: * 0 field not valid or unknown latency * [1..251] msecs = (x-1)*2 (max 500ms with x = 251 = 0xfb) * 255 audio/video not supported * * HDA latency format: * single value indicating video latency relative to audio: * 0 unknown or 0ms * [1..250] msecs = x*2 (max 500ms with x = 250 = 0xfa) * [251..255] reserved
*/
aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { int video_latency_hdmi = (aud_synch & ATI_DELAY_VIDEO_LATENCY); int audio_latency_hdmi = (aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8;
if (video_latency_hdmi <= 0xfb && audio_latency_hdmi <= 0xfb &&
video_latency_hdmi > audio_latency_hdmi)
buf[6] = video_latency_hdmi - audio_latency_hdmi; /* else unknown/invalid or 0ms or video ahead of audio, so use zero */
}
/* Baseline ELD length (4-byte header is not counted in) */
buf[2] = (pos - 4) / 4;
*eld_size = pos;
return 0;
}
staticint atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, int dev_id, unsignedchar *buf, int *eld_size)
{
WARN_ON(dev_id != 0); /* call hda_eld.c ATI/AMD-specific function */ return get_eld_ati(codec, nid, buf, eld_size,
is_amdhdmi_rev3_or_later(codec));
}
staticvoid atihdmi_pin_setup_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid, int dev_id, int ca, int active_channels, int conn_type)
{
WARN_ON(dev_id != 0);
snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca);
}
staticint atihdmi_paired_swap_fc_lfe(int pos)
{ /* * ATI/AMD have automatic FC/LFE swap built-in * when in pairwise mapping mode.
*/
switch (pos) { /* see channel_allocations[].speakers[] */ case 2: return 3; case 3: return 2; default: return pos;
}
}
staticint atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, int ca, int chs, unsignedchar *map)
{ struct hdac_cea_channel_speaker_allocation *cap; int i, j;
/* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
cap = snd_hdac_get_ch_alloc_from_ca(ca); for (i = 0; i < chs; ++i) { int mask = snd_hdac_chmap_to_spk_mask(map[i]); bool ok = false; bool companion_ok = false;
if (!mask) continue;
for (j = 0 + i % 2; j < 8; j += 2) { int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j);
if (cap->speakers[chan_idx] == mask) { /* channel is in a supported position */
ok = true;
if (i % 2 == 0 && i + 1 < chs) { /* even channel, check the odd companion */ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); int comp_mask_act = cap->speakers[comp_chan_idx];
if (companion_ok)
i++; /* companion channel already checked */
}
return 0;
}
staticint atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
{ struct hda_codec *codec = hdac_to_hda_codec(hdac); int verb; int ati_channel_setup = 0;
if (hdmi_slot > 7) return -EINVAL;
if (!has_amd_full_remap_support(codec)) {
hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot);
/* In case this is an odd slot but without stream channel, do not * disable the slot since the corresponding even slot could have a * channel. In case neither have a channel, the slot pair will be * disabled when this function is called for the even slot.
*/ if (hdmi_slot % 2 != 0 && stream_channel == 0xf) return 0;
hdmi_slot -= hdmi_slot % 2;
if (stream_channel != 0xf)
stream_channel -= stream_channel % 2;
}
staticint atihdmi_paired_chmap_cea_alloc_validate_get_type( struct hdac_chmap *chmap, struct hdac_cea_channel_speaker_allocation *cap, int channels)
{ int c;
/* * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so * we need to take that into account (a single channel may take 2 * channel slots if we need to carry a silent channel next to it). * On Rev3+ AMD codecs this function is not used.
*/ int chanpairs = 0;
/* We only produce even-numbered channel count TLVs */ if ((channels % 2) != 0) return -1;
for (c = 0; c < 7; c += 2) { if (cap->speakers[c] || cap->speakers[c+1])
chanpairs++;
}
if (chanpairs * 2 != channels) return -1;
return SNDRV_CTL_TLVT_CHMAP_PAIRED;
}
staticvoid atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, struct hdac_cea_channel_speaker_allocation *cap, unsignedint *chmap, int channels)
{ /* produce paired maps for pre-rev3 ATI/AMD codecs */ int count = 0; int c;
for (c = 7; c >= 0; c--) { int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); int spk = cap->speakers[chan];
if (!spk) { /* add N/A channel if the companion channel is occupied */ if (cap->speakers[chan + (chan % 2 ? -1 : 1)])
chmap[count++] = SNDRV_CHMAP_NA;
continue;
}
chmap[count++] = snd_hdac_spk_to_chmap(spk);
}
WARN_ON(count != channels);
}
staticint atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, int dev_id, bool hbr)
{ int hbr_ctl, hbr_ctl_new;
if (hbr_ctl != hbr_ctl_new)
snd_hda_codec_write(codec, pin_nid, 0,
ATI_VERB_SET_HBR_CONTROL,
hbr_ctl_new);
} elseif (hbr) return -EINVAL;
return 0;
}
staticint atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, int dev_id,
u32 stream_tag, int format)
{ if (is_amdhdmi_rev3_or_later(codec)) { int ramp_rate = 180; /* default as per AMD spec */ /* disable ramp-up/down for non-pcm as per AMD spec */ if (format & AC_FMT_TYPE_NON_PCM)
ramp_rate = 0;
/* make sure downmix information in infoframe is zero */
snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0);
/* enable channel-wise remap mode if supported */ if (has_amd_full_remap_support(codec))
snd_hda_codec_write(codec, per_pin->pin_nid, 0,
ATI_VERB_SET_MULTICHANNEL_MODE,
ATI_MULTICHANNEL_MODE_SINGLE);
}
codec->auto_runtime_pm = 1;
return 0;
}
/* map from pin NID to port; port is 0-based */ /* for AMD: assume widget NID starting from 3, with step 2 (3, 5, 7, ...) */ staticint atihdmi_pin2port(void *audio_ptr, int pin_nid)
{ return pin_nid / 2 - 1;
}
/* reverse-map from port to pin NID: see above */ staticint atihdmi_port2pin(struct hda_codec *codec, int port)
{ return port * 2 + 3;
}
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.