// 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); if (ret) {
dev_err(dev, "Could not register PCM component: %d\n", ret); return ret;
}
ret = devm_snd_soc_register_component(dev, &vc4_hdmi_audio_cpu_dai_comp,
&vc4_hdmi_audio_cpu_dai_drv, 1); if (ret) {
dev_err(dev, "Could not register CPU DAI: %d\n", ret); return ret;
}
ret = drm_connector_hdmi_audio_init(&vc4_hdmi->connector, dev,
&vc4_hdmi_audio_funcs, 8, 0, false,
-1); if (ret) return ret;
/* * Be careful, snd_soc_register_card() calls dev_set_drvdata() and * stores a pointer to the snd card object in dev->driver_data. This * means we cannot use it for something else. The hdmi back-pointer is * now stored in card->drvdata and should be retrieved with * snd_soc_card_get_drvdata() if needed.
*/
snd_soc_card_set_drvdata(card, vc4_hdmi);
ret = devm_snd_soc_register_card(dev, card); if (ret)
dev_err_probe(dev, ret, "Could not register sound card\n");
if (vc4_hdmi->cec_tx_ok) {
cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK,
0, 0, 0, 0);
} else { /* * This CEC implementation makes 1 retry, so if we * get a NACK, then that means it made 2 attempts.
*/
cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK,
0, 2, 0, 0);
} return IRQ_HANDLED;
}
/* * We don't need to protect the register access using * drm_dev_enter() there because the interrupt handler lifetime * is tied to the device itself, and not to the DRM device. * * So when the device will be gone, one of the first thing we * will be doing will be to unregister the interrupt handler, * and then unregister the DRM device. drm_dev_enter() would * thus always succeed if we are here.
*/
/* * We don't need to protect the register access using * drm_dev_enter() there because the interrupt handler lifetime * is tied to the device itself, and not to the DRM device. * * So when the device will be gone, one of the first thing we * will be doing will be to unregister the interrupt handler, * and then unregister the DRM device. drm_dev_enter() would * thus always succeed if we are here.
*/
/* * We don't need to protect the register access using * drm_dev_enter() there because the interrupt handler lifetime * is tied to the device itself, and not to the DRM device. * * So when the device will be gone, one of the first thing we * will be doing will be to unregister the interrupt handler, * and then unregister the DRM device. drm_dev_enter() would * thus always succeed if we are here.
*/
if (!(stat & VC4_HDMI_CPU_CEC)) return IRQ_NONE;
spin_lock(&vc4_hdmi->hw_lock);
cntrl5 = HDMI_READ(HDMI_CEC_CNTRL_5);
vc4_hdmi->cec_irq_was_rx = cntrl5 & VC4_HDMI_CEC_RX_CEC_INT; if (vc4_hdmi->cec_irq_was_rx)
ret = vc4_cec_irq_handler_rx_bare_locked(vc4_hdmi); else
ret = vc4_cec_irq_handler_tx_bare_locked(vc4_hdmi);
staticint vc4_hdmi_cec_enable(struct cec_adapter *adap)
{ struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); struct drm_device *drm = vc4_hdmi->connector.dev; /* clock period in microseconds */ const u32 usecs = 1000000 / CEC_CLOCK_FREQ; unsignedlong flags;
u32 val; int ret; int idx;
if (!drm_dev_enter(drm, &idx)) /* * We can't return an error code, because the CEC * framework will emit WARN_ON messages at unbind * otherwise.
*/ return 0;
ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); if (ret) {
drm_dev_exit(idx); return ret;
}
mutex_lock(&vc4_hdmi->mutex);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
val = HDMI_READ(HDMI_CEC_CNTRL_5);
val &= ~(VC4_HDMI_CEC_TX_SW_RESET | VC4_HDMI_CEC_RX_SW_RESET |
VC4_HDMI_CEC_CNT_TO_4700_US_MASK |
VC4_HDMI_CEC_CNT_TO_4500_US_MASK);
val |= ((4700 / usecs) << VC4_HDMI_CEC_CNT_TO_4700_US_SHIFT) |
((4500 / usecs) << VC4_HDMI_CEC_CNT_TO_4500_US_SHIFT);
if (!drm_dev_enter(drm, &idx)) /* * We can't return an error code, because the CEC * framework will emit WARN_ON messages at unbind * otherwise.
*/ return 0;
mutex_lock(&vc4_hdmi->mutex);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
if (!vc4_hdmi->variant->external_irq_controller)
HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, VC4_HDMI_CPU_CEC);
if (!drm_dev_enter(drm, &idx)) /* * We can't return an error code, because the CEC * framework will emit WARN_ON messages at unbind * otherwise.
*/ return 0;
if (msg->len > 16) {
drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len);
drm_dev_exit(idx); return -ENOMEM;
}
mutex_lock(&vc4_hdmi->mutex);
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
for (i = 0; i < msg->len; i += 4)
HDMI_WRITE(HDMI_CEC_TX_DATA_1 + (i >> 2),
(msg->msg[i]) |
(msg->msg[i + 1] << 8) |
(msg->msg[i + 2] << 16) |
(msg->msg[i + 3] << 24));
val = HDMI_READ(HDMI_CEC_CNTRL_1);
val &= ~VC4_HDMI_CEC_START_XMIT_BEGIN;
HDMI_WRITE(HDMI_CEC_CNTRL_1, val);
val &= ~VC4_HDMI_CEC_MESSAGE_LENGTH_MASK;
val |= (msg->len - 1) << VC4_HDMI_CEC_MESSAGE_LENGTH_SHIFT;
val |= VC4_HDMI_CEC_START_XMIT_BEGIN;
if (vc4_hdmi->variant->external_irq_controller) {
ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-rx"),
vc4_cec_irq_handler_rx_bare,
vc4_cec_irq_handler_rx_thread, 0, "vc4 hdmi cec rx", vc4_hdmi); if (ret) goto err_delete_cec_adap;
ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-tx"),
vc4_cec_irq_handler_tx_bare,
vc4_cec_irq_handler_tx_thread, 0, "vc4 hdmi cec tx", vc4_hdmi); if (ret) goto err_delete_cec_adap;
} else {
ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
vc4_cec_irq_handler,
vc4_cec_irq_handler_thread, 0, "vc4 hdmi cec", vc4_hdmi); if (ret) goto err_delete_cec_adap;
}
ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev); if (ret < 0) goto err_delete_cec_adap;
/* * NOTE: Strictly speaking, we should probably use a DRM-managed * registration there to avoid removing the CEC adapter by the * time the DRM driver doesn't have any user anymore. * * However, the CEC framework already cleans up the CEC adapter * only when the last user has closed its file descriptor, so we * don't need to handle it in DRM. * * By the time the device-managed hook is executed, we will give * up our reference to the CEC adapter and therefore don't * really care when it's actually freed. * * There's still a problematic sequence: if we unregister our * CEC adapter, but the userspace keeps a handle on the CEC * adapter but not the DRM device for some reason. In such a * case, our vc4_hdmi structure will be freed, but the * cec_adapter structure will have a dangling pointer to what * used to be our HDMI controller. If we get a CEC call at that * moment, we could end up with a use-after-free. Fortunately, * the CEC framework already handles this too, by calling * cec_is_registered() in cec_ioctl() and cec_poll().
*/
ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi); if (ret) return ret;
vc4_hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(vc4_hdmi->hdmicore_regs)) return PTR_ERR(vc4_hdmi->hdmicore_regs);
vc4_hdmi->hd_regs = vc4_ioremap_regs(pdev, 1); if (IS_ERR(vc4_hdmi->hd_regs)) return PTR_ERR(vc4_hdmi->hd_regs);
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI); if (ret) return ret;
vc4_hdmi->pixel_clock = devm_clk_get(dev, "pixel"); if (IS_ERR(vc4_hdmi->pixel_clock)) {
ret = PTR_ERR(vc4_hdmi->pixel_clock); if (ret != -EPROBE_DEFER)
drm_err(drm, "Failed to get pixel clock\n"); return ret;
}
vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); if (IS_ERR(vc4_hdmi->hsm_clock)) {
drm_err(drm, "Failed to get HDMI state machine clock\n"); return PTR_ERR(vc4_hdmi->hsm_clock);
}
vc4_hdmi->audio_clock = vc4_hdmi->hsm_clock;
vc4_hdmi->cec_clock = vc4_hdmi->hsm_clock;
vc4_hdmi->hdmicore_regs = devm_platform_ioremap_resource_byname(pdev, "hdmi"); if (IS_ERR(vc4_hdmi->hdmicore_regs)) return PTR_ERR(vc4_hdmi->hdmicore_regs);
/* This is shared between both HDMI controllers. Cannot * claim for both instances. Lets not convert to using * devm_platform_ioremap_resource_byname() like * the rest
*/
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hd"); if (!res) return -ENODEV;
vc4_hdmi->hd_regs = devm_ioremap(dev, res->start, resource_size(res)); if (!vc4_hdmi->hd_regs) return -ENOMEM;
vc4_hdmi->cec_regs = devm_platform_ioremap_resource_byname(pdev, "cec"); if (IS_ERR(vc4_hdmi->cec_regs)) return PTR_ERR(vc4_hdmi->cec_regs);
vc4_hdmi->csc_regs = devm_platform_ioremap_resource_byname(pdev, "csc"); if (IS_ERR(vc4_hdmi->csc_regs)) return PTR_ERR(vc4_hdmi->csc_regs);
vc4_hdmi->dvp_regs = devm_platform_ioremap_resource_byname(pdev, "dvp"); if (IS_ERR(vc4_hdmi->dvp_regs)) return PTR_ERR(vc4_hdmi->dvp_regs);
if (IS_ERR(vc4_hdmi->phy_regs)) return PTR_ERR(vc4_hdmi->phy_regs);
vc4_hdmi->ram_regs = devm_platform_ioremap_resource_byname(pdev, "packet"); if (IS_ERR(vc4_hdmi->ram_regs)) return PTR_ERR(vc4_hdmi->ram_regs);
vc4_hdmi->rm_regs = devm_platform_ioremap_resource_byname(pdev, "rm"); if (IS_ERR(vc4_hdmi->rm_regs)) return PTR_ERR(vc4_hdmi->rm_regs);
vc4_hdmi->hsm_clock = devm_clk_get(dev, "hdmi"); if (IS_ERR(vc4_hdmi->hsm_clock)) {
drm_err(drm, "Failed to get HDMI state machine clock\n"); return PTR_ERR(vc4_hdmi->hsm_clock);
}
vc4_hdmi->pixel_bvb_clock = devm_clk_get(dev, "bvb"); if (IS_ERR(vc4_hdmi->pixel_bvb_clock)) {
drm_err(drm, "Failed to get pixel bvb clock\n"); return PTR_ERR(vc4_hdmi->pixel_bvb_clock);
}
vc4_hdmi->audio_clock = devm_clk_get(dev, "audio"); if (IS_ERR(vc4_hdmi->audio_clock)) {
drm_err(drm, "Failed to get audio clock\n"); return PTR_ERR(vc4_hdmi->audio_clock);
}
vc4_hdmi->cec_clock = devm_clk_get(dev, "cec"); if (IS_ERR(vc4_hdmi->cec_clock)) {
drm_err(drm, "Failed to get CEC clock\n"); return PTR_ERR(vc4_hdmi->cec_clock);
}
vc4_hdmi->reset = devm_reset_control_get(dev, NULL); if (IS_ERR(vc4_hdmi->reset)) {
drm_err(drm, "Failed to get HDMI reset line\n"); return PTR_ERR(vc4_hdmi->reset);
}
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->cec_regset, VC5_CEC); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->csc_regset, VC5_CSC); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->dvp_regset, VC5_DVP); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->phy_regset, VC5_PHY); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->ram_regset, VC5_RAM); if (ret) return ret;
ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->rm_regset, VC5_RM); if (ret) return ret;
ret = clk_prepare_enable(vc4_hdmi->hsm_clock); if (ret) return ret;
/* * Whenever the RaspberryPi boots without an HDMI monitor * plugged in, the firmware won't have initialized the HSM clock * rate and it will be reported as 0. * * If we try to access a register of the controller in such a * case, it will lead to a silent CPU stall. Let's make sure we * prevent such a case.
*/
rate = clk_get_rate(vc4_hdmi->hsm_clock); if (!rate) {
ret = -EINVAL; goto err_disable_clk;
}
ret = clk_prepare_enable(vc4_hdmi->audio_clock); if (ret) goto err_disable_clk;
if (vc4_hdmi->variant->reset)
vc4_hdmi->variant->reset(vc4_hdmi);
#ifdef CONFIG_DRM_VC4_HDMI_CEC
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
value = HDMI_READ(HDMI_CEC_CNTRL_1); /* Set the logical address to Unregistered */
value |= VC4_HDMI_CEC_ADDR_MASK;
HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
/* * Since we don't know the state of the controller and its * display (if any), let's assume it's always enabled. * vc4_hdmi_disable_scrambling() will thus run at boot, make * sure it's disabled, and avoid any inconsistency.
*/ if (variant->max_pixel_clock > HDMI_14_MAX_TMDS_CLK)
vc4_hdmi->scdc_enabled = true;
ret = variant->init_resources(drm, vc4_hdmi); if (ret) return ret;
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); if (!ddc_node) {
drm_err(drm, "Failed to find ddc node in device tree\n"); return -ENODEV;
}
vc4_hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node); if (!vc4_hdmi->ddc) {
drm_err(drm, "Failed to get ddc i2c adapter by node\n"); return -EPROBE_DEFER;
}
ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc_device, vc4_hdmi); if (ret) return ret;
/* Only use the GPIO HPD pin if present in the DT, otherwise * we'll use the HDMI core's register.
*/
vc4_hdmi->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); if (IS_ERR(vc4_hdmi->hpd_gpio)) { return PTR_ERR(vc4_hdmi->hpd_gpio);
}
ret = devm_pm_runtime_enable(dev); if (ret) return ret;
/* * We need to have the device powered up at this point to call * our reset hook and for the CEC init.
*/
ret = pm_runtime_resume_and_get(dev); if (ret) return ret;
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.