Impressum vc4_hdmi.c
Interaktion und PortierbarkeitC
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Broadcom * Copyright (c) 2014 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com>
*/
/** * DOC: VC4 Falcon HDMI module * * The HDMI core has a state machine and a PHY. On BCM2835, most of * the unit operates off of the HSM clock from CPRMAN. It also * internally uses the PLLH_PIX clock for the PHY. * * HDMI infoframes are kept within a small packet ram, where each * packet can be individually enabled for including in a frame. * * HDMI audio is implemented entirely within the HDMI IP block. A * register in the HDMI encoder takes SPDIF frames from the DMA engine * and transfers them over an internal MAI (multi-channel audio * interconnect) bus to the encoder side for insertion into the video * blank regions. * * The driver's HDMI encoder does not yet support power management. * The HDMI encoder's power domain and the HSM/pixel clocks are kept * continuously running, and only the HDMI logic and packet ram are * powered off/on at disable/enable time. * * The driver does not yet support CEC control, though the HDMI * encoder block has CEC support.
*/
/* * We can be called by our bind callback, when the * connector->dev pointer might not be initialised yet.
*/ if (drm && !drm_dev_enter(drm, &idx)) return;
/* * We can be called by our bind callback, when the * connector->dev pointer might not be initialised yet.
*/ if (drm && !drm_dev_enter(drm, &idx)) return;
/* * This function is called by our runtime_resume implementation * and thus at bind time, when we haven't registered our * connector yet and thus don't have a pointer to the DRM * device.
*/ if (drm && !drm_dev_enter(drm, &idx)) return;
cec_rate = clk_get_rate(vc4_hdmi->cec_clock);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
value = HDMI_READ(HDMI_CEC_CNTRL_1);
value &= ~VC4_HDMI_CEC_DIV_CLK_CNT_MASK;
/* * Set the clock divider: the hsm_clock rate and this divider * setting will give a 40 kHz CEC clock.
*/
clk_cnt = cec_rate / CEC_CLOCK_FREQ;
value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT;
HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
/* * HDMI 2.0 says that one should not send scrambled data * prior to configuring the sink scrambling, and that * TMDS clock/data transmission should be suspended when * changing the TMDS clock rate in the sink. So let's * just do a full modeset here, even though some sinks * would be perfectly happy if were to just reconfigure * the SCDC settings on the fly.
*/ return drm_atomic_helper_reset_crtc(crtc, ctx);
}
/* * NOTE: This function should really be called with vc4_hdmi->mutex * held, but doing so results in reentrancy issues since * cec_s_phys_addr() might call .adap_enable, which leads to that * funtion being called with our mutex held. * * A similar situation occurs with vc4_hdmi_reset_link() that * will call into our KMS hooks if the scrambling was enabled. * * Concurrency isn't an issue at the moment since we don't share * any state with any of the other frameworks so we can ignore * the lock for now.
*/
/* * NOTE: This function should really take vc4_hdmi->mutex, but * doing so results in reentrancy issues since * vc4_hdmi_handle_hotplug() can call into other functions that * would take the mutex while it's held here. * * Concurrency isn't an issue at the moment since we don't share * any state with any of the other frameworks so we can ignore * the lock for now.
*/
ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); if (ret) {
drm_err_once(connector->dev, "Failed to retain HDMI power domain: %d\n",
ret); return connector_status_unknown;
}
if (vc4_hdmi->hpd_gpio) { if (gpiod_get_value_cansleep(vc4_hdmi->hpd_gpio))
status = connector_status_connected;
} else { if (vc4_hdmi->variant->hp_detect &&
vc4_hdmi->variant->hp_detect(vc4_hdmi))
status = connector_status_connected;
}
list_for_each_entry(mode, &connector->probed_modes, head) { if (vc4_hdmi_mode_needs_scrambling(mode, 8, HDMI_COLORSPACE_RGB)) {
drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz.");
drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60.");
}
}
}
crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state);
/* * Strictly speaking, we should be calling * drm_atomic_helper_check_planes() after our call to * drm_atomic_add_affected_planes(). However, the * connector atomic_check is called as part of * drm_atomic_helper_check_modeset() that already * happens before a call to * drm_atomic_helper_check_planes() in * drm_atomic_helper_check().
*/
ret = drm_atomic_add_affected_planes(state, crtc); if (ret) return ret;
}
if (old_state->colorspace != new_state->colorspace) { struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state);
/* * Some of the properties below require access to state, like bpc. * Allocate some default initial connector state with our reset helper.
*/ if (connector->funcs->reset)
connector->funcs->reset(connector);
/* Create and attach TV margin props to this connector. */
ret = drm_mode_create_tv_margin_properties(dev); if (ret) return ret;
ret = drm_mode_create_hdmi_colorspace_property(connector, 0); if (ret) return ret;
/* * clear remainder of packet ram as it's included in the * infoframe and triggers a checksum error on hdmi analyser
*/ for (; packet_reg < packet_reg_next; packet_reg += 4)
writel(0, base + packet_reg);
/* * TODO: This should work on BCM2712, but doesn't for some * reason and result in a system lockup.
*/ if (vc4->gen < VC4_GEN_6_C) {
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
HDMI_WRITE(HDMI_VID_CTL,
HDMI_READ(HDMI_VID_CTL) &
~VC4_HD_VID_CTL_ENABLE);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
}
if (state->hdmi.is_limited_range) { /* CEA VICs other than #1 requre limited range RGB * output unless overridden by an AVI infoframe. * Apply a colorspace conversion to squash 0-255 down * to 16-235. The matrix here is: * * [ 0 0 0.8594 16] * [ 0 0.8594 0 16] * [ 0.8594 0 0 16] * [ 0 0 0 1]
*/
csc_ctl |= VC4_HD_CSC_CTL_ENABLE;
csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC;
csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
VC4_HD_CSC_CTL_MODE);
/* YUV444 needs the CSC matrices using the channels in a different order */
HDMI_WRITE(HDMI_CSC_12_11, (coeffs[1][1] << 16) | coeffs[1][0]);
HDMI_WRITE(HDMI_CSC_14_13, (coeffs[1][3] << 16) | coeffs[1][2]);
HDMI_WRITE(HDMI_CSC_22_21, (coeffs[2][1] << 16) | coeffs[2][0]);
HDMI_WRITE(HDMI_CSC_24_23, (coeffs[2][3] << 16) | coeffs[2][2]);
HDMI_WRITE(HDMI_CSC_32_31, (coeffs[0][1] << 16) | coeffs[0][0]);
HDMI_WRITE(HDMI_CSC_34_33, (coeffs[0][3] << 16) | coeffs[0][2]);
}
staticconst u16
(*vc5_hdmi_find_yuv_csc_coeffs(struct vc4_hdmi *vc4_hdmi, u32 colorspace, bool limited))[4]
{ switch (colorspace) { case DRM_MODE_COLORIMETRY_SMPTE_170M_YCC: case DRM_MODE_COLORIMETRY_XVYCC_601: case DRM_MODE_COLORIMETRY_SYCC_601: case DRM_MODE_COLORIMETRY_OPYCC_601: case DRM_MODE_COLORIMETRY_BT601_YCC: return vc5_hdmi_csc_full_rgb_to_yuv_bt601[limited];
default: case DRM_MODE_COLORIMETRY_NO_DATA: case DRM_MODE_COLORIMETRY_BT709_YCC: case DRM_MODE_COLORIMETRY_XVYCC_709: case DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED: case DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT: return vc5_hdmi_csc_full_rgb_to_yuv_bt709[limited];
case DRM_MODE_COLORIMETRY_BT2020_CYCC: case DRM_MODE_COLORIMETRY_BT2020_YCC: case DRM_MODE_COLORIMETRY_BT2020_RGB: case DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65: case DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER: return vc5_hdmi_csc_full_rgb_to_yuv_bt2020[limited];
}
}
switch (state->hdmi.output_bpc) { case 12:
gcp = 6; break; case 10:
gcp = 5; break; case 8: default:
gcp = 0; break;
}
/* * YCC422 is always 36-bit and not considered deep colour so * doesn't signal in GCP.
*/ if (state->hdmi.output_format == HDMI_COLORSPACE_YUV422) {
gcp = 0;
}
ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); if (ret < 0) {
drm_err(drm, "Failed to retain power domain: %d\n", ret); goto err_dev_exit;
}
/* * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must * be faster than pixel clock, infinitesimally faster, tested in * simulation. Otherwise, exact value is unimportant for HDMI * operation." This conflicts with bcm2835's vc4 documentation, which * states HSM's clock has to be at least 108% of the pixel clock. * * Real life tests reveal that vc4's firmware statement holds up, and * users are able to use pixel clocks closer to HSM's, namely for * 1920x1200@60Hz. So it was decided to have leave a 1% margin between * both clocks. Which, for RPi0-3 implies a maximum pixel clock of * 162MHz. * * Additionally, the AXI clock needs to be at least 25% of * pixel clock, but HSM ends up being the limiting factor.
*/
hsm_rate = max_t(unsignedlong,
HSM_MIN_CLOCK_FREQ,
div_u64(tmds_char_rate, 100) * 101);
ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate); if (ret) {
drm_err(drm, "Failed to set HSM clock rate: %d\n", ret); goto err_put_runtime_pm;
}
ret = clk_set_rate(vc4_hdmi->pixel_clock, tmds_char_rate); if (ret) {
drm_err(drm, "Failed to set pixel clock rate: %d\n", ret); goto err_put_runtime_pm;
}
ret = clk_prepare_enable(vc4_hdmi->pixel_clock); if (ret) {
drm_err(drm, "Failed to turn on pixel clock: %d\n", ret); goto err_put_runtime_pm;
}
ret = clk_set_min_rate(vc4_hdmi->pixel_bvb_clock, bvb_rate); if (ret) {
drm_err(drm, "Failed to set pixel bvb clock rate: %d\n", ret); goto err_disable_pixel_clock;
}
ret = clk_prepare_enable(vc4_hdmi->pixel_bvb_clock); if (ret) {
drm_err(drm, "Failed to turn on pixel bvb clock: %d\n", ret); goto err_disable_pixel_clock;
}
if (vc4_hdmi->variant->phy_init)
vc4_hdmi->variant->phy_init(vc4_hdmi, conn_state);
if (vc4_hdmi->variant->unsupported_odd_h_timings) { if (mode->flags & DRM_MODE_FLAG_DBLCLK) { /* Only try to fixup DBLCLK modes to get 480i and 576i * working. * A generic solution for all modes with odd horizontal * timing values seems impossible based on trying to * solve it for 1366x768 monitors.
*/ if ((mode->hsync_start - mode->hdisplay) & 1)
mode->hsync_start--; if ((mode->hsync_end - mode->hsync_start) & 1)
mode->hsync_end--;
}
/* Now check whether we still have odd values remaining */ if ((mode->hdisplay % 2) || (mode->hsync_start % 2) ||
(mode->hsync_end % 2) || (mode->htotal % 2)) return -EINVAL;
}
/* * The 1440p@60 pixel rate is in the same range than the first * WiFi channel (between 2.4GHz and 2.422GHz with 22MHz * bandwidth). Slightly lower the frequency to bring it out of * the WiFi range.
*/
tmds_bit_rate = tmds_char_rate * 10; if (vc4_hdmi->disable_wifi_frequencies &&
(tmds_bit_rate >= WIFI_2_4GHz_CH1_MIN_FREQ &&
tmds_bit_rate <= WIFI_2_4GHz_CH1_MAX_FREQ)) {
mode->clock = 238560;
tmds_char_rate = mode->clock * 1000;
}
/* * We could get slightly more accurate clocks in some cases by * providing a CTS_1 value. The two CTS values are alternated * between based on the period fields
*/
HDMI_WRITE(HDMI_CTS_0, cts);
HDMI_WRITE(HDMI_CTS_1, cts);
}
staticint sample_rate_to_mai_fmt(int samplerate)
{ switch (samplerate) { case 8000: return VC4_HDMI_MAI_SAMPLE_RATE_8000; case 11025: return VC4_HDMI_MAI_SAMPLE_RATE_11025; case 12000: return VC4_HDMI_MAI_SAMPLE_RATE_12000; case 16000: return VC4_HDMI_MAI_SAMPLE_RATE_16000; case 22050: return VC4_HDMI_MAI_SAMPLE_RATE_22050; case 24000: return VC4_HDMI_MAI_SAMPLE_RATE_24000; case 32000: return VC4_HDMI_MAI_SAMPLE_RATE_32000; case 44100: return VC4_HDMI_MAI_SAMPLE_RATE_44100; case 48000: return VC4_HDMI_MAI_SAMPLE_RATE_48000; case 64000: return VC4_HDMI_MAI_SAMPLE_RATE_64000; case 88200: return VC4_HDMI_MAI_SAMPLE_RATE_88200; case 96000: return VC4_HDMI_MAI_SAMPLE_RATE_96000; case 128000: return VC4_HDMI_MAI_SAMPLE_RATE_128000; case 176400: return VC4_HDMI_MAI_SAMPLE_RATE_176400; case 192000: return VC4_HDMI_MAI_SAMPLE_RATE_192000; default: return VC4_HDMI_MAI_SAMPLE_RATE_NOT_INDICATED;
}
}
/* The B frame identifier should match the value used by alsa-lib (8) */
audio_packet_config =
VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT |
VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS |
VC4_SET_FIELD(0x8, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER);
/* * ASoC makes it a bit hard to retrieve a pointer to the * vc4_hdmi structure. Registering the card will overwrite our * device drvdata with a pointer to the snd_soc_card structure, * which can then be used to retrieve whatever drvdata we want * to associate. * * However, that doesn't fly in the case where we wouldn't * register an ASoC card (because of an old DT that is missing * the dmas properties for example), then the card isn't * registered and the device drvdata wouldn't be set. * * We can deal with both cases by making sure a snd_soc_card * pointer and a vc4_hdmi structure are pointing to the same * memory address, so we can treat them indistinctly without any * issue.
*/
BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0);
BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0);
if (!of_find_property(dev->of_node, "dmas", &len) || !len) {
dev_warn(dev, "'dmas' DT property is missing or empty, no HDMI audio\n"); return 0;
}
if (mai_data->reg != VC4_HD) {
WARN_ONCE(true, "MAI isn't in the HD block\n"); return -EINVAL;
}
/* * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve * the bus address specified in the DT, because the physical address * (the one returned by platform_get_resource()) is not appropriate * for DMA transfers. * This VC/MMU should probably be exposed to avoid this kind of hacks.
*/
index = of_property_match_string(dev->of_node, "reg-names", "hd"); /* Before BCM2711, we don't have a named register range */ if (index < 0)
index = 1;
addr = of_get_address(dev->of_node, index, NULL, NULL); if (!addr) return -EINVAL;
/* * NOTE: Strictly speaking, we should probably use a DRM-managed * registration there to avoid removing all the audio components * by the time the driver doesn't have any user anymore. * * However, the ASoC core uses a number of devm_kzalloc calls * when registering, even when using non-device-managed * functions (such as in snd_soc_register_component()). * * If we call snd_soc_unregister_component() in a DRM-managed * action, the device-managed actions have already been executed * and thus we would access memory that has been freed. * * Using device-managed hooks here probably leaves us open to a * bunch of issues if userspace still has a handle on the ALSA * device when the device is removed. However, this is mitigated * by the use of drm_dev_enter()/drm_dev_exit() in the audio * path to prevent the access to the device resources if it * isn't there anymore. * * Then, the vc4_hdmi structure is DRM-managed and thus only * freed whenever the last user has closed the DRM device file. * It should thus outlive ALSA in most situations.
*/
ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0);
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ 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.0.82Bemerkung:
¤
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.