Message ID | cover.1667463263.git.Sandor.yu@nxp.com |
---|---|
Headers | show |
Series | Initial support for Cadence MHDP(HDMI/DP) for i.MX8MQ | expand |
Hello, thanks for working on this and the updated version. Am Freitag, 4. November 2022, 07:44:56 CET schrieb Sandor Yu: > Add a new DRM HDMI bridge driver for Candence MHDP used in i.MX8MQ > SOC. MHDP IP could support HDMI or DisplayPort standards according > embedded Firmware running in the uCPU. > > For iMX8MQ SOC, the HDMI FW was loaded and activated by SOC ROM code. > Bootload binary included HDMI FW was required for the driver. > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > --- > drivers/gpu/drm/bridge/cadence/Kconfig | 12 + > .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 1038 +++++++++++++++++ > 2 files changed, 1050 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > b/drivers/gpu/drm/bridge/cadence/Kconfig index e79ae1af3765..377452d09992 > 100644 > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig > @@ -26,6 +26,18 @@ config DRM_CDNS_MHDP8546_J721E > clock and data muxes. > endif > > +config DRM_CDNS_HDMI > + tristate "Cadence HDMI DRM driver" > + select DRM_KMS_HELPER > + select DRM_PANEL_BRIDGE > + select DRM_DISPLAY_HELPER > + select DRM_CDNS_AUDIO > + depends on OF > + help > + Support Cadence MHDP HDMI driver. > + Cadence MHDP Controller support one or more protocols, > + HDMI firmware is required for this driver. > + > config DRM_CDNS_DP > tristate "Cadence DP DRM driver" > select DRM_KMS_HELPER > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c new file mode 100644 > index 000000000000..b392aac015bd > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > @@ -0,0 +1,1038 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Cadence High-Definition Multimedia Interface (HDMI) driver > + * > + * Copyright (C) 2019-2022 NXP Semiconductor, Inc. > + * > + */ > +#include <linux/delay.h> > +#include <linux/err.h> > +#include <linux/hdmi.h> > +#include <linux/irq.h> > +#include <linux/module.h> > +#include <linux/mfd/syscon.h> > +#include <linux/mutex.h> > +#include <linux/of_device.h> > +#include <linux/phy/phy.h> > +#include <linux/phy/phy-hdmi.h> > + > +#include <drm/bridge/cdns-mhdp-mailbox.h> > +#include <drm/display/drm_hdmi_helper.h> > +#include <drm/display/drm_scdc_helper.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_edid.h> > +#include <drm/drm_encoder_slave.h> > +#include <drm/drm_of.h> > +#include <drm/drm_probe_helper.h> > +#include <drm/drm_print.h> > +#include <drm/drm_vblank.h> > + > +#include "cdns-mhdp-common.h" > + > +void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp, > + u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type) > +{ > + u32 *packet32, len32; > + u32 val, i; > + > + /* invalidate entry */ > + val = F_ACTIVE_IDLE_TYPE(1) | F_PKT_ALLOC_ADDRESS(entry_id); > + writel(val, mhdp->regs + SOURCE_PIF_PKT_ALLOC_REG); > + writel(F_PKT_ALLOC_WR_EN(1), mhdp->regs + SOURCE_PIF_PKT_ALLOC_WR_EN); > + > + /* flush fifo 1 */ > + writel(F_FIFO1_FLUSH(1), mhdp->regs + SOURCE_PIF_FIFO1_FLUSH); > + > + /* write packet into memory */ > + packet32 = (u32 *)packet; This only works on little-endian machines, no? > + len32 = packet_len / 4; > + for (i = 0; i < len32; i++) > + writel(F_DATA_WR(packet32[i]), mhdp->regs + SOURCE_PIF_DATA_WR); > + > + /* write entry id */ > + writel(F_WR_ADDR(entry_id), mhdp->regs + SOURCE_PIF_WR_ADDR); > + > + /* write request */ > + writel(F_HOST_WR(1), mhdp->regs + SOURCE_PIF_WR_REQ); > + > + /* update entry */ > + val = F_ACTIVE_IDLE_TYPE(1) | F_TYPE_VALID(1) | > + F_PACKET_TYPE(packet_type) | F_PKT_ALLOC_ADDRESS(entry_id); > + writel(val, mhdp->regs + SOURCE_PIF_PKT_ALLOC_REG); > + > + writel(F_PKT_ALLOC_WR_EN(1), mhdp->regs + SOURCE_PIF_PKT_ALLOC_WR_EN); > +} > + > +static int cdns_hdmi_get_edid_block(void *data, u8 *edid, > + u32 block, size_t length) > +{ > + struct cdns_mhdp_device *mhdp = data; > + u8 msg[2], reg[5], i; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + for (i = 0; i < 4; i++) { What is i? It is not used inside the loop. > + msg[0] = block / 2; > + msg[1] = block % 2; > + > + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_EDID, > + sizeof(msg), msg); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_HDMI_TX, > + HDMI_TX_EDID, sizeof(reg) + length); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_data(mhdp, edid, length); > + if (ret) > + continue; > + > + if ((reg[3] << 8 | reg[4]) == length) > + break; > + } > + > + mutex_unlock(&mhdp->mbox_mutex); > + > + if (ret) > + DRM_ERROR("get block[%d] edid failed: %d\n", block, ret); > + return ret; > +} > + > +int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data) > +{ > + u8 msg[4], reg[6]; > + int ret; > + > + msg[0] = 0x54; > + msg[1] = addr; > + msg[2] = 0; > + msg[3] = 1; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_READ, > + sizeof(msg), msg); > + if (ret) > + goto err_scdc_read; > + > + ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_HDMI_TX, > + HDMI_TX_READ, sizeof(reg)); > + if (ret) > + goto err_scdc_read; > + > + ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > + if (ret) > + goto err_scdc_read; > + > + *data = reg[5]; > + > +err_scdc_read: > + > + mutex_unlock(&mhdp->mbox_mutex); > + > + if (ret) > + DRM_ERROR("scdc read failed: %d\n", ret); > + return ret; > +} > + > +int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value) > +{ > + u8 msg[5], reg[5]; > + int ret; > + > + msg[0] = 0x54; > + msg[1] = addr; > + msg[2] = 0; > + msg[3] = 1; > + msg[4] = value; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_WRITE, > + sizeof(msg), msg); > + if (ret) > + goto err_scdc_write; > + > + ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_HDMI_TX, > + HDMI_TX_WRITE, sizeof(reg)); > + if (ret) > + goto err_scdc_write; > + > + ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > + if (ret) > + goto err_scdc_write; > + > + if (reg[0] != 0) > + ret = -EINVAL; > + > +err_scdc_write: > + > + mutex_unlock(&mhdp->mbox_mutex); > + > + if (ret) > + DRM_ERROR("scdc write failed: %d\n", ret); > + return ret; > +} > + > +int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, > + int protocol, u32 char_rate) > +{ > + u32 reg0; > + u32 reg1; > + u32 val; > + int ret; > + > + /* Set PHY to HDMI data */ > + ret = cdns_mhdp_reg_write(mhdp, PHY_DATA_SEL, F_SOURCE_PHY_MHDP_SEL(1)); > + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(mhdp, HDTX_HPD, > + F_HPD_VALID_WIDTH(4) | F_HPD_GLITCH_WIDTH(0)); > + if (ret < 0) > + return ret; > + > + /* open CARS */ > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_PHY_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, 0xFF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_PKT_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_AIF_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CIPHER_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CRYPTO_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CEC_CAR, 3); > + if (ret < 0) > + return ret; > + > + reg0 = reg1 = 0x7c1f; > + if (protocol == MODE_HDMI_2_0 && char_rate >= 340000) { > + reg0 = 0; > + reg1 = 0xFFFFF; > + } > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_0, reg0); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_1, reg1); > + if (ret < 0) > + return ret; > + > + /* set hdmi mode and preemble mode data enable */ > + val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | F_DATA_EN(1) | > + F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF); > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > + > + return ret; > +} > + > +int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp, > + struct drm_display_mode *mode, > + struct video_info *video_info) > +{ > + int ret; > + u32 val; > + u32 vsync_lines = mode->vsync_end - mode->vsync_start; > + u32 eof_lines = mode->vsync_start - mode->vdisplay; > + u32 sof_lines = mode->vtotal - mode->vsync_end; > + u32 hblank = mode->htotal - mode->hdisplay; > + u32 hactive = mode->hdisplay; > + u32 vblank = mode->vtotal - mode->vdisplay; > + u32 vactive = mode->vdisplay; > + u32 hfront = mode->hsync_start - mode->hdisplay; > + u32 hback = mode->htotal - mode->hsync_end; > + u32 vfront = eof_lines; > + u32 hsync = hblank - hfront - hback; > + u32 vsync = vsync_lines; > + u32 vback = sof_lines; > + u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1) + > + ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : 2); Please fix the alignment. > + > + ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_H_SIZE, (hactive << 16) + > hblank); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_V_SIZE, (vactive << 16) + > vblank); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_FRONT_WIDTH, (vfront << 16) + > hfront); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_SYNC_WIDTH, (vsync << 16) + > hsync); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_BACK_WIDTH, (vback << 16) + > hback); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, v_h_polarity); > + if (ret < 0) > + return ret; > + > + /* Reset Data Enable */ > + cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER, &val); > + val &= ~F_DATA_EN(1); > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > + if (ret < 0) > + return ret; > + > + /* Set bpc */ > + val &= ~F_VIF_DATA_WIDTH(3); > + switch (video_info->bpc) { > + case 10: > + val |= F_VIF_DATA_WIDTH(1); > + break; > + case 12: > + val |= F_VIF_DATA_WIDTH(2); > + break; > + case 16: > + val |= F_VIF_DATA_WIDTH(3); > + break; > + case 8: > + default: > + val |= F_VIF_DATA_WIDTH(0); > + break; > + } > + > + /* select color encoding */ > + val &= ~F_HDMI_ENCODING(3); > + switch (video_info->color_fmt) { > + case DRM_COLOR_FORMAT_YCBCR444: > + val |= F_HDMI_ENCODING(2); > + break; > + case DRM_COLOR_FORMAT_YCBCR422: > + val |= F_HDMI_ENCODING(1); > + break; > + case DRM_COLOR_FORMAT_YCBCR420: > + val |= F_HDMI_ENCODING(3); > + break; > + case DRM_COLOR_FORMAT_RGB444: > + default: > + val |= F_HDMI_ENCODING(0); > + break; > + } > + > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > + if (ret < 0) > + return ret; > + > + /* set data enable */ > + val |= F_DATA_EN(1); > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > + > + return ret; > +} > + > +int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp) > +{ > + u32 val; > + > + cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER, &val); > + val &= ~F_GCP_EN(1); > + > + return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > +} > + > +int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp) > +{ > + u32 val; > + > + cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER, &val); > + val |= F_GCP_EN(1); > + > + return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > +} > + > +static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) > +{ > + struct drm_scdc *scdc = &mhdp->curr_conn->display_info.hdmi.scdc; > + u8 buff = 0; > + > + /* Default work in HDMI1.4 */ > + mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; > + > + /* check sink support SCDC or not */ > + if (scdc->supported != true) { > + DRM_INFO("Sink Not Support SCDC\n"); > + return; > + } > + > + if (mhdp->hdmi.char_rate > 340000) { > + /* > + * TMDS Character Rate above 340MHz should working in HDMI2.0 > + * Enable scrambling and TMDS_Bit_Clock_Ratio > + */ > + buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE; > + mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; > + } else if (scdc->scrambling.low_rates) { > + /* > + * Enable scrambling and HDMI2.0 when scrambling capability of sink > + * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit > + */ > + buff = SCDC_SCRAMBLING_ENABLE; > + mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; > + } > + > + /* TMDS config */ > + cdns_hdmi_scdc_write(mhdp, SCDC_TMDS_CONFIG, buff); > +} > + > +static void hdmi_lanes_config(struct cdns_mhdp_device *mhdp) > +{ > + u32 lane_mapping = mhdp->plat_data->lane_mapping; > + /* Line swapping */ > + cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | lane_mapping); > +} > + > +int cdns_mhdp_hdmi_read_hpd(struct cdns_mhdp_device *mhdp) > +{ > + u8 status; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, > HDMI_TX_HPD_STATUS, + 0, NULL); > + if (ret) > + goto err_get_hpd; > + > + ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_HDMI_TX, > + HDMI_TX_HPD_STATUS, sizeof(status)); > + if (ret) > + goto err_get_hpd; > + > + ret = cdns_mhdp_mailbox_recv_data(mhdp, &status, sizeof(status)); > + if (ret) > + goto err_get_hpd; > + > + mutex_unlock(&mhdp->mbox_mutex); > + return status; > + > +err_get_hpd: > + mutex_unlock(&mhdp->mbox_mutex); > + DRM_ERROR("read hpd failed: %d\n", ret); > + return ret; > +} > + > +static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp, > + struct drm_display_mode *mode) > +{ > + struct hdmi_avi_infoframe frame; > + int format = mhdp->video_info.color_fmt; > + struct drm_connector_state *conn_state = mhdp->curr_conn->state; > + struct drm_display_mode *adj_mode; > + enum hdmi_quantization_range qr; > + u8 buf[32]; > + int ret; > + > + /* Initialise info frame from DRM mode */ > + drm_hdmi_avi_infoframe_from_display_mode(&frame, > + mhdp->curr_conn, mode); > + > + switch (format) { > + case DRM_COLOR_FORMAT_YCBCR444: > + frame.colorspace = HDMI_COLORSPACE_YUV444; > + break; > + case DRM_COLOR_FORMAT_YCBCR422: > + frame.colorspace = HDMI_COLORSPACE_YUV422; > + break; > + case DRM_COLOR_FORMAT_YCBCR420: > + frame.colorspace = HDMI_COLORSPACE_YUV420; > + break; > + default: > + frame.colorspace = HDMI_COLORSPACE_RGB; > + break; > + } > + > + drm_hdmi_avi_infoframe_colorimetry(&frame, conn_state); > + > + adj_mode = &mhdp->bridge.encoder->crtc->state->adjusted_mode; > + > + qr = drm_default_rgb_quant_range(adj_mode); > + > + drm_hdmi_avi_infoframe_quant_range(&frame, mhdp->curr_conn, > + adj_mode, qr); > + > + ret = hdmi_avi_infoframe_check(&frame); > + if (WARN_ON(ret)) > + return -EINVAL; > + > + ret = hdmi_avi_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); > + if (ret < 0) { > + DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); > + return -1; > + } > + > + buf[0] = 0; > + cdns_mhdp_infoframe_set(mhdp, 0, sizeof(buf), buf, > HDMI_INFOFRAME_TYPE_AVI); + > + return 0; > +} > + > +static void hdmi_vendor_info_set(struct cdns_mhdp_device *mhdp, > + struct drm_display_mode *mode) > +{ > + struct hdmi_vendor_infoframe frame; > + u8 buf[32]; > + int ret; > + > + /* Initialise vendor frame from DRM mode */ > + ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mhdp- >curr_conn, > mode); + if (ret < 0) { > + DRM_INFO("No vendor infoframe\n"); > + return; > + } > + > + ret = hdmi_vendor_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); > + if (ret < 0) { > + DRM_WARN("Unable to pack vendor infoframe: %d\n", ret); > + return; > + } > + > + buf[0] = 0; > + cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, > HDMI_INFOFRAME_TYPE_VENDOR); +} > + > +static void hdmi_drm_info_set(struct cdns_mhdp_device *mhdp) > +{ > + struct drm_connector_state *conn_state; > + struct hdmi_drm_infoframe frame; > + u8 buf[32]; > + int ret; > + > + conn_state = mhdp->curr_conn->state; > + > + if (!conn_state->hdr_output_metadata) > + return; > + > + ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); > + if (ret < 0) { > + DRM_DEBUG_KMS("couldn't set HDR metadata in infoframe\n"); > + return; > + } > + > + ret = hdmi_drm_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); > + if (ret < 0) { > + DRM_DEBUG_KMS("couldn't pack HDR infoframe\n"); > + return; > + } > + > + buf[0] = 0; > + cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, > HDMI_INFOFRAME_TYPE_DRM); +} > + > +static int hdmi_phy_colorspace(int color_fmt) > +{ > + int color_space; > + > + switch (color_fmt) { > + case DRM_COLOR_FORMAT_YCBCR444: > + color_space = HDMI_PHY_COLORSPACE_YUV444; > + break; > + case DRM_COLOR_FORMAT_YCBCR422: > + color_space = HDMI_PHY_COLORSPACE_YUV422; > + break; > + case DRM_COLOR_FORMAT_YCBCR420: > + color_space = HDMI_PHY_COLORSPACE_YUV420; > + break; > + case DRM_COLOR_FORMAT_RGB444: > + default: > + color_space = HDMI_PHY_COLORSPACE_RGB; > + break; > + } > + > + return color_space; > +} > + > +void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp) > +{ > + struct drm_display_mode *mode = &mhdp->mode; > + union phy_configure_opts phy_cfg; > + int ret; > + > + /* video mode check */ > + if (mode->clock == 0 || mode->hdisplay == 0 || mode->vdisplay == 0) > + return; > + > + hdmi_lanes_config(mhdp); > + > + phy_cfg.hdmi.pixel_clk_rate = mode->clock; > + phy_cfg.hdmi.bpc = mhdp->video_info.bpc; > + phy_cfg.hdmi.color_space = > hdmi_phy_colorspace(mhdp->video_info.color_fmt); + ret = > phy_configure(mhdp->phy, &phy_cfg); > + if (ret) { > + dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n", > + __func__, ret); > + return; > + } > + > + hdmi_sink_config(mhdp); > + > + ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type, > mhdp->hdmi.char_rate); + if (ret < 0) { > + DRM_ERROR("%s, ret = %d\n", __func__, ret); > + return; > + } > + > + /* Config GCP */ > + if (mhdp->video_info.bpc == 8) > + cdns_hdmi_disable_gcp(mhdp); > + else > + cdns_hdmi_enable_gcp(mhdp); > + > + ret = hdmi_avi_info_set(mhdp, mode); > + if (ret < 0) { > + DRM_ERROR("%s ret = %d\n", __func__, ret); > + return; > + } > + > + /* vendor info frame is enabled only for HDMI1.4 4K mode */ > + hdmi_vendor_info_set(mhdp, mode); > + > + hdmi_drm_info_set(mhdp); > + > + ret = cdns_hdmi_mode_config(mhdp, mode, &mhdp->video_info); > + if (ret < 0) { > + DRM_ERROR("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret); > + return; > + } > +} > +static enum drm_connector_status > +cdns_hdmi_detect(struct cdns_mhdp_device *mhdp) > +{ > + u8 hpd = 0xf; > + > + hpd = cdns_mhdp_hdmi_read_hpd(mhdp); > + if (hpd == 1) > + return connector_status_connected; > + else if (hpd == 0) > + return connector_status_disconnected; > + > + DRM_INFO("Unknown cable status, hdp=%u\n", hpd); > + return connector_status_unknown; > +} > + > +static enum drm_connector_status > +cdns_hdmi_bridge_detect(struct drm_bridge *bridge) > +{ > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > + > + return cdns_hdmi_detect(mhdp); > +} > + > +static int cdns_hdmi_connector_get_modes(struct drm_connector *connector) > +{ > + struct cdns_mhdp_device *mhdp = > + container_of(connector, struct cdns_mhdp_device, connector); > + int num_modes = 0; > + struct edid *edid; > + > + edid = drm_do_get_edid(connector, > + cdns_hdmi_get_edid_block, mhdp); > + if (edid) { > + dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n", > + edid->header[0], edid->header[1], > + edid->header[2], edid->header[3], > + edid->header[4], edid->header[5], > + edid->header[6], edid->header[7]); > + drm_connector_update_edid_property(connector, edid); > + num_modes = drm_add_edid_modes(connector, edid); > + kfree(edid); > + } > + > + if (num_modes == 0) > + DRM_ERROR("Invalid edid\n"); > + return num_modes; > +} > + > +static bool blob_equal(const struct drm_property_blob *a, > + const struct drm_property_blob *b) > +{ > + if (a && b) > + return a->length == b->length && > + !memcmp(a->data, b->data, a->length); > + > + return !a == !b; > +} > + > +static int cdns_hdmi_connector_atomic_check(struct drm_connector > *connector, + struct drm_atomic_state *state) > +{ > + struct drm_connector_state *new_con_state = > + drm_atomic_get_new_connector_state(state, connector); > + struct drm_connector_state *old_con_state = > + drm_atomic_get_old_connector_state(state, connector); > + struct drm_crtc *crtc = new_con_state->crtc; > + struct drm_crtc_state *new_crtc_state; > + > + if (!blob_equal(new_con_state->hdr_output_metadata, > + old_con_state->hdr_output_metadata) || > + new_con_state->colorspace != old_con_state->colorspace) { > + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); > + if (IS_ERR(new_crtc_state)) > + return PTR_ERR(new_crtc_state); > + > + new_crtc_state->mode_changed = > + !new_con_state->hdr_output_metadata || > + !old_con_state->hdr_output_metadata || > + new_con_state->colorspace != old_con_state- >colorspace; > + } > + > + return 0; > +} > + > +static const struct drm_connector_funcs cdns_hdmi_connector_funcs = { > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = drm_connector_cleanup, > + .reset = drm_atomic_helper_connector_reset, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +static const struct drm_connector_helper_funcs > cdns_hdmi_connector_helper_funcs = { + .get_modes = > cdns_hdmi_connector_get_modes, > + .atomic_check = cdns_hdmi_connector_atomic_check, > +}; > + > +static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, > + enum drm_bridge_attach_flags flags) > +{ > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > + struct drm_mode_config *config = &bridge->dev->mode_config; > + struct drm_encoder *encoder = bridge->encoder; > + struct drm_connector *connector = &mhdp->connector; > + > + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { > + connector->interlace_allowed = 0; > + connector->polled = DRM_CONNECTOR_POLL_HPD; > + > + drm_connector_helper_add(connector, &cdns_hdmi_connector_helper_funcs); > + > + drm_connector_init(bridge->dev, connector, &cdns_hdmi_connector_funcs, > + DRM_MODE_CONNECTOR_HDMIA); > + > + drm_object_attach_property(&connector->base, > + config- >hdr_output_metadata_property, 0); > + > + if (!drm_mode_create_hdmi_colorspace_property(connector)) > + drm_object_attach_property(&connector->base, > + connector- >colorspace_property, 0); > + > + drm_connector_attach_encoder(connector, encoder); > + } > + > + return 0; > +} > + > +static enum drm_mode_status > +cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, > + const struct drm_display_info *info, > + const struct drm_display_mode *mode) > +{ > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > + enum drm_mode_status mode_status = MODE_OK; > + union phy_configure_opts phy_cfg; > + int ret; > + > + /* We don't support double-clocked and Interlaced modes */ > + if (mode->flags & DRM_MODE_FLAG_DBLCLK || > + mode->flags & DRM_MODE_FLAG_INTERLACE) > + return MODE_BAD; > + > + /* MAX support pixel clock rate 594MHz */ > + if (mode->clock > 594000) > + return MODE_CLOCK_HIGH; > + > + /* 4096x2160 is not supported */ > + if (mode->hdisplay > 3840 || mode->vdisplay > 2160) > + return MODE_BAD_HVALUE; > + > + /* Check modes supported by PHY */ > + phy_cfg.hdmi.pixel_clk_rate = mode->clock; > + ret = phy_validate(mhdp->phy, PHY_MODE_HDMI, 0, &phy_cfg); > + if (ret < 0) > + return MODE_CLOCK_RANGE; > + > + return mode_status; > +} > + > +bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, > + const struct drm_display_mode *mode, > + struct drm_display_mode *adjusted_mode) > +{ > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > + struct video_info *video = &mhdp->video_info; > + > + video->bpc = 8; > + video->color_fmt = DRM_COLOR_FORMAT_RGB444; > + > + return true; > +} > + > +static struct edid *cdns_hdmi_bridge_get_edid(struct drm_bridge *bridge, > + struct drm_connector *connector) > +{ > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > + > + return drm_do_get_edid(connector, cdns_hdmi_get_edid_block, mhdp); > +} > + > +static void cdns_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, > + struct drm_bridge_state *old_state) > +{ > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > + > + mhdp->curr_conn = NULL; > + > + mutex_lock(&mhdp->lock); > + phy_power_off(mhdp->phy); > + mutex_unlock(&mhdp->lock); > +} > + > +static void cdns_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, > + struct drm_bridge_state *old_state) > +{ > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > + struct drm_atomic_state *state = old_state->base.state; > + struct drm_connector *connector; > + struct video_info *video = &mhdp->video_info; > + struct drm_crtc_state *crtc_state; > + struct drm_connector_state *conn_state; > + const struct drm_display_mode *mode; > + > + connector = drm_atomic_get_new_connector_for_encoder(state, > + bridge->encoder); > + if (WARN_ON(!connector)) > + return; > + > + mhdp->curr_conn = connector; > + > + conn_state = drm_atomic_get_new_connector_state(state, connector); > + if (WARN_ON(!conn_state)) > + return; > + > + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); > + if (WARN_ON(!crtc_state)) > + return; > + > + mode = &crtc_state->adjusted_mode; > + DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode- >clock); > + memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); > + > + video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); > + video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); > + > + mutex_lock(&mhdp->lock); > + cdns_hdmi_mode_set(mhdp); > + mutex_unlock(&mhdp->lock); > +} > + > +static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = { > + .attach = cdns_hdmi_bridge_attach, > + .detect = cdns_hdmi_bridge_detect, > + .get_edid = cdns_hdmi_bridge_get_edid, > + .mode_valid = cdns_hdmi_bridge_mode_valid, > + .mode_fixup = cdns_hdmi_bridge_mode_fixup, > + .atomic_enable = cdns_hdmi_bridge_atomic_enable, > + .atomic_disable = cdns_hdmi_bridge_atomic_disable, > + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, > + .atomic_reset = drm_atomic_helper_bridge_reset, > +}; > + > +static void hotplug_work_func(struct work_struct *work) > +{ > + struct cdns_mhdp_device *mhdp = container_of(work, > + struct cdns_mhdp_device, hotplug_work.work); > + enum drm_connector_status status = cdns_hdmi_detect(mhdp); > + > + drm_bridge_hpd_notify(&mhdp->bridge, status); > + > + if (status == connector_status_connected) { > + DRM_INFO("HDMI Cable Plug In\n"); > + enable_irq(mhdp->irq[IRQ_OUT]); > + } else if (status == connector_status_disconnected) { > + /* Cable Disconnedted */ > + DRM_INFO("HDMI Cable Plug Out\n"); > + enable_irq(mhdp->irq[IRQ_IN]); > + } > +} > + > +static irqreturn_t cdns_hdmi_irq_thread(int irq, void *data) > +{ > + struct cdns_mhdp_device *mhdp = data; > + > + disable_irq_nosync(irq); > + > + mod_delayed_work(system_wq, &mhdp->hotplug_work, > + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); > + > + return IRQ_HANDLED; > +} > + > +static int cdns_mhdp_imx_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct cdns_mhdp_device *mhdp; > + struct platform_device_info pdevinfo; > + struct resource *res; > + u32 reg; > + int ret; > + > + mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); > + if (!mhdp) > + return -ENOMEM; > + > + mutex_init(&mhdp->lock); > + mutex_init(&mhdp->mbox_mutex); > + > + INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENODEV; > + mhdp->regs = devm_ioremap(dev, res->start, resource_size(res)); > + if (IS_ERR(mhdp->regs)) > + return PTR_ERR(mhdp->regs); Please use devm_platform_get_and_ioremap_resource. > + mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, 0); > + if (IS_ERR(mhdp->phy)) { > + dev_err(dev, "no PHY configured\n"); > + return PTR_ERR(mhdp->phy); > + } Please use dev_err_probe(). > + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); > + if (mhdp->irq[IRQ_IN] < 0) { > + dev_info(dev, "No plug_in irq number\n"); > + return -EPROBE_DEFER; > + } Please use dev_err_probe(). > + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out"); > + if (mhdp->irq[IRQ_OUT] < 0) { > + dev_info(dev, "No plug_out irq number\n"); > + return -EPROBE_DEFER; > + } Please use dev_err_probe(). > + /* > + * Wait for the KEEP_ALIVE "message" on the first 8 bits. > + * Updated each sched "tick" (~2ms) > + */ > + ret = readl_poll_timeout(mhdp->regs + KEEP_ALIVE, reg, > + reg & CDNS_KEEP_ALIVE_MASK, 500, > + CDNS_KEEP_ALIVE_TIMEOUT); This freezes my board TQMa8MQ (arch/arm64/boot/dts/freescale/imx8mq-tqma8mq- mba8mx.dts) completly if this and the PHY driver are compiled in. I have "pd_ignore_unused clk_ignore_unused" passed to kernel command line, so I have no idea what's wrong here. Best regards, Alexander > + if (ret) { > + dev_err(mhdp->dev, > + "device didn't give any life sign: reg %d\n", reg); > + return ret; > + } > + > + ret = phy_init(mhdp->phy); > + if (ret) { > + dev_err(mhdp->dev, "Failed to initialize PHY: %d\n", ret); > + return -ENODEV; > + } > + > + /* Enable Hotplug Detect thread */ > + irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], > + NULL, cdns_hdmi_irq_thread, > + IRQF_ONESHOT, dev_name(dev), > + mhdp); > + if (ret < 0) { > + dev_err(dev, "can't claim irq %d\n", > + mhdp- >irq[IRQ_IN]); > + return -EINVAL; > + } > + > + irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN); > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], > + NULL, cdns_hdmi_irq_thread, > + IRQF_ONESHOT, dev_name(dev), > + mhdp); > + if (ret < 0) { > + dev_err(dev, "can't claim irq %d\n", > + mhdp- >irq[IRQ_OUT]); > + return -EINVAL; > + } > + > + mhdp->dev = dev; > + > + if (cdns_mhdp_hdmi_read_hpd(mhdp)) > + enable_irq(mhdp->irq[IRQ_OUT]); > + else > + enable_irq(mhdp->irq[IRQ_IN]); > + > + mhdp->bridge.driver_private = mhdp; > + mhdp->bridge.funcs = &cdns_hdmi_bridge_funcs; > + mhdp->bridge.of_node = dev->of_node; > + mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | > + DRM_BRIDGE_OP_HPD; > + mhdp->bridge.type = DRM_MODE_CONNECTOR_HDMIA; > + drm_bridge_add(&mhdp->bridge); > + > + memset(&pdevinfo, 0, sizeof(pdevinfo)); > + pdevinfo.parent = dev; > + pdevinfo.id = PLATFORM_DEVID_AUTO; > + > + dev_set_drvdata(dev, mhdp); > + mhdp->plat_data = of_device_get_match_data(dev); > + > + return 0; > +} > + > +static int cdns_mhdp_imx_remove(struct platform_device *pdev) > +{ > + struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev); > + int ret = 0; > + > + drm_bridge_remove(&mhdp->bridge); > + > + return ret; > +} > + > +static struct cdns_plat_data imx8mq_hdmi_drv_data = { > + .lane_mapping = 0xe4, > +}; > + > +static const struct of_device_id cdns_mhdp_imx_dt_ids[] = { > + { .compatible = "cdns,mhdp-imx8mq-hdmi", > + .data = &imx8mq_hdmi_drv_data > + }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, cdns_mhdp_imx_dt_ids); > + > +static struct platform_driver cdns_mhdp_imx_platform_driver = { > + .probe = cdns_mhdp_imx_probe, > + .remove = cdns_mhdp_imx_remove, > + .driver = { > + .name = "cdns-mhdp-imx8mq-hdmi", > + .of_match_table = cdns_mhdp_imx_dt_ids, > + }, > +}; > + > +module_platform_driver(cdns_mhdp_imx_platform_driver); > + > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > +MODULE_DESCRIPTION("Cadence HDMI transmitter driver"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:cdns-hdmi");
Thanks for your comments. > -----Original Message----- > From: Alexander Stein <alexander.stein@ew.tq-group.com> > Sent: 2022年11月8日 21:17 > To: jonas@kwiboo.se; Sandor Yu <sandor.yu@nxp.com> > Cc: dri-devel@lists.freedesktop.org; devicetree@vger.kernel.org; > linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; > linux-phy@lists.infradead.org; andrzej.hajda@intel.com; > neil.armstrong@linaro.org; robert.foss@linaro.org; > Laurent.pinchart@ideasonboard.com; jernej.skrabec@gmail.com; > kishon@ti.com; vkoul@kernel.org; Oliver Brown <oliver.brown@nxp.com>; > krzysztof.kozlowski+dt@linaro.org; sam@ravnborg.org; jani.nikula@intel.com; > tzimmermann@suse.de; s.hauer@pengutronix.de; javierm@redhat.com; > penguin-kernel@i-love.sakura.ne.jp; robh+dt@kernel.org; dl-linux-imx > <linux-imx@nxp.com>; kernel@pengutronix.de; Sandor Yu > <sandor.yu@nxp.com>; shawnguo@kernel.org; p.yadav@ti.com; > maxime@cerno.tech > Subject: [EXT] Re: [v2 06/10] drm: bridge: cadence: Add MHDP HDMI driver > for i.MX8MQ > > Caution: EXT Email > > Hello, > > thanks for working on this and the updated version. > > Am Freitag, 4. November 2022, 07:44:56 CET schrieb Sandor Yu: > > Add a new DRM HDMI bridge driver for Candence MHDP used in i.MX8MQ > > SOC. MHDP IP could support HDMI or DisplayPort standards according > > embedded Firmware running in the uCPU. > > > > For iMX8MQ SOC, the HDMI FW was loaded and activated by SOC ROM > code. > > Bootload binary included HDMI FW was required for the driver. > > > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > > --- > > drivers/gpu/drm/bridge/cadence/Kconfig | 12 + > > .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 1038 > +++++++++++++++++ > > 2 files changed, 1050 insertions(+) > > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > > > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > > b/drivers/gpu/drm/bridge/cadence/Kconfig index > > e79ae1af3765..377452d09992 > > 100644 > > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig > > @@ -26,6 +26,18 @@ config DRM_CDNS_MHDP8546_J721E > > clock and data muxes. > > endif > > > > +config DRM_CDNS_HDMI > > + tristate "Cadence HDMI DRM driver" > > + select DRM_KMS_HELPER > > + select DRM_PANEL_BRIDGE > > + select DRM_DISPLAY_HELPER > > + select DRM_CDNS_AUDIO > > + depends on OF > > + help > > + Support Cadence MHDP HDMI driver. > > + Cadence MHDP Controller support one or more protocols, > > + HDMI firmware is required for this driver. > > + > > config DRM_CDNS_DP > > tristate "Cadence DP DRM driver" > > select DRM_KMS_HELPER > > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > > b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c new file mode > 100644 > > index 000000000000..b392aac015bd > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > > @@ -0,0 +1,1038 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Cadence High-Definition Multimedia Interface (HDMI) driver > > + * > > + * Copyright (C) 2019-2022 NXP Semiconductor, Inc. > > + * > > + */ > > +#include <linux/delay.h> > > +#include <linux/err.h> > > +#include <linux/hdmi.h> > > +#include <linux/irq.h> > > +#include <linux/module.h> > > +#include <linux/mfd/syscon.h> > > +#include <linux/mutex.h> > > +#include <linux/of_device.h> > > +#include <linux/phy/phy.h> > > +#include <linux/phy/phy-hdmi.h> > > + > > +#include <drm/bridge/cdns-mhdp-mailbox.h> #include > > +<drm/display/drm_hdmi_helper.h> #include > > +<drm/display/drm_scdc_helper.h> #include <drm/drm_atomic_helper.h> > > +#include <drm/drm_edid.h> #include <drm/drm_encoder_slave.h> > #include > > +<drm/drm_of.h> #include <drm/drm_probe_helper.h> #include > > +<drm/drm_print.h> #include <drm/drm_vblank.h> > > + > > +#include "cdns-mhdp-common.h" > > + > > +void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp, > > + u8 entry_id, u8 packet_len, > u8 *packet, u8 packet_type) > > +{ > > + u32 *packet32, len32; > > + u32 val, i; > > + > > + /* invalidate entry */ > > + val = F_ACTIVE_IDLE_TYPE(1) | F_PKT_ALLOC_ADDRESS(entry_id); > > + writel(val, mhdp->regs + SOURCE_PIF_PKT_ALLOC_REG); > > + writel(F_PKT_ALLOC_WR_EN(1), mhdp->regs + > SOURCE_PIF_PKT_ALLOC_WR_EN); > > + > > + /* flush fifo 1 */ > > + writel(F_FIFO1_FLUSH(1), mhdp->regs + > SOURCE_PIF_FIFO1_FLUSH); > > + > > + /* write packet into memory */ > > + packet32 = (u32 *)packet; > > This only works on little-endian machines, no? Register SOURCE_PIF_DATA_WR require little-endian data, I will use get_unaligned_le32() to replace it in the next version. > > > + len32 = packet_len / 4; > > + for (i = 0; i < len32; i++) > > + writel(F_DATA_WR(packet32[i]), mhdp->regs + > SOURCE_PIF_DATA_WR); > > + > > + /* write entry id */ > > + writel(F_WR_ADDR(entry_id), mhdp->regs + > SOURCE_PIF_WR_ADDR); > > + > > + /* write request */ > > + writel(F_HOST_WR(1), mhdp->regs + SOURCE_PIF_WR_REQ); > > + > > + /* update entry */ > > + val = F_ACTIVE_IDLE_TYPE(1) | F_TYPE_VALID(1) | > > + F_PACKET_TYPE(packet_type) | > F_PKT_ALLOC_ADDRESS(entry_id); > > + writel(val, mhdp->regs + SOURCE_PIF_PKT_ALLOC_REG); > > + > > + writel(F_PKT_ALLOC_WR_EN(1), mhdp->regs + > SOURCE_PIF_PKT_ALLOC_WR_EN); > > +} > > + > > +static int cdns_hdmi_get_edid_block(void *data, u8 *edid, > > + u32 block, size_t length) { > > + struct cdns_mhdp_device *mhdp = data; > > + u8 msg[2], reg[5], i; > > + int ret; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + for (i = 0; i < 4; i++) { > > What is i? It is not used inside the loop. EDID data read by HDMI firmware are not guarantee 100% successful. Base on experiments, try 4 times if EDID read failed. > > > + msg[0] = block / 2; > > + msg[1] = block % 2; > > + > > + ret = cdns_mhdp_mailbox_send(mhdp, > MB_MODULE_ID_HDMI_TX, > HDMI_TX_EDID, > > + sizeof(msg), msg); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_HDMI_TX, > > + > HDMI_TX_EDID, sizeof(reg) + length); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, > sizeof(reg)); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_data(mhdp, edid, length); > > + if (ret) > > + continue; > > + > > + if ((reg[3] << 8 | reg[4]) == length) > > + break; > > + } > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + if (ret) > > + DRM_ERROR("get block[%d] edid failed: %d\n", block, > ret); > > + return ret; > > +} > > + > > +int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 > > +*data) { > > + u8 msg[4], reg[6]; > > + int ret; > > + > > + msg[0] = 0x54; > > + msg[1] = addr; > > + msg[2] = 0; > > + msg[3] = 1; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, > HDMI_TX_READ, > > + sizeof(msg), msg); > > + if (ret) > > + goto err_scdc_read; > > + > > + ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_HDMI_TX, > > + HDMI_TX_READ, > sizeof(reg)); > > + if (ret) > > + goto err_scdc_read; > > + > > + ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > > + if (ret) > > + goto err_scdc_read; > > + > > + *data = reg[5]; > > + > > +err_scdc_read: > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + if (ret) > > + DRM_ERROR("scdc read failed: %d\n", ret); > > + return ret; > > +} > > + > > +int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 > > +value) { > > + u8 msg[5], reg[5]; > > + int ret; > > + > > + msg[0] = 0x54; > > + msg[1] = addr; > > + msg[2] = 0; > > + msg[3] = 1; > > + msg[4] = value; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, > HDMI_TX_WRITE, > > + sizeof(msg), msg); > > + if (ret) > > + goto err_scdc_write; > > + > > + ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_HDMI_TX, > > + HDMI_TX_WRITE, > sizeof(reg)); > > + if (ret) > > + goto err_scdc_write; > > + > > + ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > > + if (ret) > > + goto err_scdc_write; > > + > > + if (reg[0] != 0) > > + ret = -EINVAL; > > + > > +err_scdc_write: > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + if (ret) > > + DRM_ERROR("scdc write failed: %d\n", ret); > > + return ret; > > +} > > + > > +int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp, > > + int protocol, u32 char_rate) { > > + u32 reg0; > > + u32 reg1; > > + u32 val; > > + int ret; > > + > > + /* Set PHY to HDMI data */ > > + ret = cdns_mhdp_reg_write(mhdp, PHY_DATA_SEL, > F_SOURCE_PHY_MHDP_SEL(1)); > > + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_HPD, > > + F_HPD_VALID_WIDTH(4) | > F_HPD_GLITCH_WIDTH(0)); > > + if (ret < 0) > > + return ret; > > + > > + /* open CARS */ > > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_PHY_CAR, 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, 0xFF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_PKT_CAR, 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_AIF_CAR, 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CIPHER_CAR, 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CRYPTO_CAR, 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(mhdp, SOURCE_CEC_CAR, 3); > > + if (ret < 0) > > + return ret; > > + > > + reg0 = reg1 = 0x7c1f; > > + if (protocol == MODE_HDMI_2_0 && char_rate >= 340000) { > > + reg0 = 0; > > + reg1 = 0xFFFFF; > > + } > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_0, reg0); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_1, reg1); > > + if (ret < 0) > > + return ret; > > + > > + /* set hdmi mode and preemble mode data enable */ > > + val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | > > + F_DATA_EN(1) > | > > + F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | > F_PIC_3D(0XF); > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > > + > > + return ret; > > +} > > + > > +int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp, > > + struct > drm_display_mode *mode, > > + struct > video_info *video_info) > > +{ > > + int ret; > > + u32 val; > > + u32 vsync_lines = mode->vsync_end - mode->vsync_start; > > + u32 eof_lines = mode->vsync_start - mode->vdisplay; > > + u32 sof_lines = mode->vtotal - mode->vsync_end; > > + u32 hblank = mode->htotal - mode->hdisplay; > > + u32 hactive = mode->hdisplay; > > + u32 vblank = mode->vtotal - mode->vdisplay; > > + u32 vactive = mode->vdisplay; > > + u32 hfront = mode->hsync_start - mode->hdisplay; > > + u32 hback = mode->htotal - mode->hsync_end; > > + u32 vfront = eof_lines; > > + u32 hsync = hblank - hfront - hback; > > + u32 vsync = vsync_lines; > > + u32 vback = sof_lines; > > + u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? > 0 : 1) + > > + ((mode->flags & > DRM_MODE_FLAG_NVSYNC) ? 0 : 2); > > Please fix the alignment. OK. > > > + > > + ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_H_SIZE, (hactive << > > + 16) + > > hblank); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_V_SIZE, (vactive << > > + 16) + > > vblank); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_FRONT_WIDTH, > (vfront > > + << > 16) + > > hfront); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_SYNC_WIDTH, > (vsync > > + << > 16) + > > hsync); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_BACK_WIDTH, > (vback > > + << > 16) + > > hback); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, > v_h_polarity); > > + if (ret < 0) > > + return ret; > > + > > + /* Reset Data Enable */ > > + cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER, &val); > > + val &= ~F_DATA_EN(1); > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > > + if (ret < 0) > > + return ret; > > + > > + /* Set bpc */ > > + val &= ~F_VIF_DATA_WIDTH(3); > > + switch (video_info->bpc) { > > + case 10: > > + val |= F_VIF_DATA_WIDTH(1); > > + break; > > + case 12: > > + val |= F_VIF_DATA_WIDTH(2); > > + break; > > + case 16: > > + val |= F_VIF_DATA_WIDTH(3); > > + break; > > + case 8: > > + default: > > + val |= F_VIF_DATA_WIDTH(0); > > + break; > > + } > > + > > + /* select color encoding */ > > + val &= ~F_HDMI_ENCODING(3); > > + switch (video_info->color_fmt) { > > + case DRM_COLOR_FORMAT_YCBCR444: > > + val |= F_HDMI_ENCODING(2); > > + break; > > + case DRM_COLOR_FORMAT_YCBCR422: > > + val |= F_HDMI_ENCODING(1); > > + break; > > + case DRM_COLOR_FORMAT_YCBCR420: > > + val |= F_HDMI_ENCODING(3); > > + break; > > + case DRM_COLOR_FORMAT_RGB444: > > + default: > > + val |= F_HDMI_ENCODING(0); > > + break; > > + } > > + > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > > + if (ret < 0) > > + return ret; > > + > > + /* set data enable */ > > + val |= F_DATA_EN(1); > > + ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); > > + > > + return ret; > > +} > > + > > +int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp) { > > + u32 val; > > + > > + cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER, &val); > > + val &= ~F_GCP_EN(1); > > + > > + return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); } > > + > > +int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp) { > > + u32 val; > > + > > + cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER, &val); > > + val |= F_GCP_EN(1); > > + > > + return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val); } > > + > > +static void hdmi_sink_config(struct cdns_mhdp_device *mhdp) { > > + struct drm_scdc *scdc = > &mhdp->curr_conn->display_info.hdmi.scdc; > > + u8 buff = 0; > > + > > + /* Default work in HDMI1.4 */ > > + mhdp->hdmi.hdmi_type = MODE_HDMI_1_4; > > + > > + /* check sink support SCDC or not */ > > + if (scdc->supported != true) { > > + DRM_INFO("Sink Not Support SCDC\n"); > > + return; > > + } > > + > > + if (mhdp->hdmi.char_rate > 340000) { > > + /* > > + * TMDS Character Rate above 340MHz should working in > HDMI2.0 > > + * Enable scrambling and TMDS_Bit_Clock_Ratio > > + */ > > + buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | > SCDC_SCRAMBLING_ENABLE; > > + mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; > > + } else if (scdc->scrambling.low_rates) { > > + /* > > + * Enable scrambling and HDMI2.0 when scrambling > capability of sink > > + * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit > > + */ > > + buff = SCDC_SCRAMBLING_ENABLE; > > + mhdp->hdmi.hdmi_type = MODE_HDMI_2_0; > > + } > > + > > + /* TMDS config */ > > + cdns_hdmi_scdc_write(mhdp, SCDC_TMDS_CONFIG, buff); } > > + > > +static void hdmi_lanes_config(struct cdns_mhdp_device *mhdp) { > > + u32 lane_mapping = mhdp->plat_data->lane_mapping; > > + /* Line swapping */ > > + cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | > > +lane_mapping); } > > + > > +int cdns_mhdp_hdmi_read_hpd(struct cdns_mhdp_device *mhdp) { > > + u8 status; > > + int ret; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, > > HDMI_TX_HPD_STATUS, + 0, NULL); > > + if (ret) > > + goto err_get_hpd; > > + > > + ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_HDMI_TX, > > + > HDMI_TX_HPD_STATUS, sizeof(status)); > > + if (ret) > > + goto err_get_hpd; > > + > > + ret = cdns_mhdp_mailbox_recv_data(mhdp, &status, sizeof(status)); > > + if (ret) > > + goto err_get_hpd; > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + return status; > > + > > +err_get_hpd: > > + mutex_unlock(&mhdp->mbox_mutex); > > + DRM_ERROR("read hpd failed: %d\n", ret); > > + return ret; > > +} > > + > > +static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp, > > + struct drm_display_mode *mode) { > > + struct hdmi_avi_infoframe frame; > > + int format = mhdp->video_info.color_fmt; > > + struct drm_connector_state *conn_state = mhdp->curr_conn->state; > > + struct drm_display_mode *adj_mode; > > + enum hdmi_quantization_range qr; > > + u8 buf[32]; > > + int ret; > > + > > + /* Initialise info frame from DRM mode */ > > + drm_hdmi_avi_infoframe_from_display_mode(&frame, > > + mhdp->curr_conn, > mode); > > + > > + switch (format) { > > + case DRM_COLOR_FORMAT_YCBCR444: > > + frame.colorspace = HDMI_COLORSPACE_YUV444; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR422: > > + frame.colorspace = HDMI_COLORSPACE_YUV422; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR420: > > + frame.colorspace = HDMI_COLORSPACE_YUV420; > > + break; > > + default: > > + frame.colorspace = HDMI_COLORSPACE_RGB; > > + break; > > + } > > + > > + drm_hdmi_avi_infoframe_colorimetry(&frame, conn_state); > > + > > + adj_mode = &mhdp->bridge.encoder->crtc->state->adjusted_mode; > > + > > + qr = drm_default_rgb_quant_range(adj_mode); > > + > > + drm_hdmi_avi_infoframe_quant_range(&frame, mhdp->curr_conn, > > + adj_mode, qr); > > + > > + ret = hdmi_avi_infoframe_check(&frame); > > + if (WARN_ON(ret)) > > + return -EINVAL; > > + > > + ret = hdmi_avi_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); > > + if (ret < 0) { > > + DRM_ERROR("failed to pack AVI infoframe: %d\n", ret); > > + return -1; > > + } > > + > > + buf[0] = 0; > > + cdns_mhdp_infoframe_set(mhdp, 0, sizeof(buf), buf, > > HDMI_INFOFRAME_TYPE_AVI); + > > + return 0; > > +} > > + > > +static void hdmi_vendor_info_set(struct cdns_mhdp_device *mhdp, > > + struct drm_display_mode *mode) { > > + struct hdmi_vendor_infoframe frame; > > + u8 buf[32]; > > + int ret; > > + > > + /* Initialise vendor frame from DRM mode */ > > + ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, > mhdp- > >curr_conn, > > mode); + if (ret < 0) { > > + DRM_INFO("No vendor infoframe\n"); > > + return; > > + } > > + > > + ret = hdmi_vendor_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); > > + if (ret < 0) { > > + DRM_WARN("Unable to pack vendor infoframe: %d\n", > ret); > > + return; > > + } > > + > > + buf[0] = 0; > > + cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, > > HDMI_INFOFRAME_TYPE_VENDOR); +} > > + > > +static void hdmi_drm_info_set(struct cdns_mhdp_device *mhdp) { > > + struct drm_connector_state *conn_state; > > + struct hdmi_drm_infoframe frame; > > + u8 buf[32]; > > + int ret; > > + > > + conn_state = mhdp->curr_conn->state; > > + > > + if (!conn_state->hdr_output_metadata) > > + return; > > + > > + ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state); > > + if (ret < 0) { > > + DRM_DEBUG_KMS("couldn't set HDR metadata in > infoframe\n"); > > + return; > > + } > > + > > + ret = hdmi_drm_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1); > > + if (ret < 0) { > > + DRM_DEBUG_KMS("couldn't pack HDR infoframe\n"); > > + return; > > + } > > + > > + buf[0] = 0; > > + cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, > > HDMI_INFOFRAME_TYPE_DRM); +} > > + > > +static int hdmi_phy_colorspace(int color_fmt) { > > + int color_space; > > + > > + switch (color_fmt) { > > + case DRM_COLOR_FORMAT_YCBCR444: > > + color_space = HDMI_PHY_COLORSPACE_YUV444; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR422: > > + color_space = HDMI_PHY_COLORSPACE_YUV422; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR420: > > + color_space = HDMI_PHY_COLORSPACE_YUV420; > > + break; > > + case DRM_COLOR_FORMAT_RGB444: > > + default: > > + color_space = HDMI_PHY_COLORSPACE_RGB; > > + break; > > + } > > + > > + return color_space; > > +} > > + > > +void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp) { > > + struct drm_display_mode *mode = &mhdp->mode; > > + union phy_configure_opts phy_cfg; > > + int ret; > > + > > + /* video mode check */ > > + if (mode->clock == 0 || mode->hdisplay == 0 || mode->vdisplay == > 0) > > + return; > > + > > + hdmi_lanes_config(mhdp); > > + > > + phy_cfg.hdmi.pixel_clk_rate = mode->clock; > > + phy_cfg.hdmi.bpc = mhdp->video_info.bpc; > > + phy_cfg.hdmi.color_space = > > hdmi_phy_colorspace(mhdp->video_info.color_fmt); + ret = > > phy_configure(mhdp->phy, &phy_cfg); > > + if (ret) { > > + dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n", > > + __func__, ret); > > + return; > > + } > > + > > + hdmi_sink_config(mhdp); > > + > > + ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type, > > mhdp->hdmi.char_rate); + if (ret < 0) { > > + DRM_ERROR("%s, ret = %d\n", __func__, ret); > > + return; > > + } > > + > > + /* Config GCP */ > > + if (mhdp->video_info.bpc == 8) > > + cdns_hdmi_disable_gcp(mhdp); > > + else > > + cdns_hdmi_enable_gcp(mhdp); > > + > > + ret = hdmi_avi_info_set(mhdp, mode); > > + if (ret < 0) { > > + DRM_ERROR("%s ret = %d\n", __func__, ret); > > + return; > > + } > > + > > + /* vendor info frame is enabled only for HDMI1.4 4K mode */ > > + hdmi_vendor_info_set(mhdp, mode); > > + > > + hdmi_drm_info_set(mhdp); > > + > > + ret = cdns_hdmi_mode_config(mhdp, mode, &mhdp->video_info); > > + if (ret < 0) { > > + DRM_ERROR("CDN_API_HDMITX_SetVic_blocking ret > = %d\n", > ret); > > + return; > > + } > > +} > > +static enum drm_connector_status > > +cdns_hdmi_detect(struct cdns_mhdp_device *mhdp) { > > + u8 hpd = 0xf; > > + > > + hpd = cdns_mhdp_hdmi_read_hpd(mhdp); > > + if (hpd == 1) > > + return connector_status_connected; > > + else if (hpd == 0) > > + return connector_status_disconnected; > > + > > + DRM_INFO("Unknown cable status, hdp=%u\n", hpd); > > + return connector_status_unknown; } > > + > > +static enum drm_connector_status > > +cdns_hdmi_bridge_detect(struct drm_bridge *bridge) { > > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > > + > > + return cdns_hdmi_detect(mhdp); > > +} > > + > > +static int cdns_hdmi_connector_get_modes(struct drm_connector > > +*connector) { > > + struct cdns_mhdp_device *mhdp = > > + container_of(connector, struct > cdns_mhdp_device, connector); > > + int num_modes = 0; > > + struct edid *edid; > > + > > + edid = drm_do_get_edid(connector, > > + cdns_hdmi_get_edid_block, > mhdp); > > + if (edid) { > > + dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n", > > + edid->header[0], edid->header[1], > > + edid->header[2], edid->header[3], > > + edid->header[4], edid->header[5], > > + edid->header[6], edid->header[7]); > > + drm_connector_update_edid_property(connector, edid); > > + num_modes = drm_add_edid_modes(connector, edid); > > + kfree(edid); > > + } > > + > > + if (num_modes == 0) > > + DRM_ERROR("Invalid edid\n"); > > + return num_modes; > > +} > > + > > +static bool blob_equal(const struct drm_property_blob *a, > > + const struct drm_property_blob *b) { > > + if (a && b) > > + return a->length == b->length && > > + !memcmp(a->data, b->data, a->length); > > + > > + return !a == !b; > > +} > > + > > +static int cdns_hdmi_connector_atomic_check(struct drm_connector > > *connector, + struct > drm_atomic_state *state) > > +{ > > + struct drm_connector_state *new_con_state = > > + drm_atomic_get_new_connector_state(state, connector); > > + struct drm_connector_state *old_con_state = > > + drm_atomic_get_old_connector_state(state, connector); > > + struct drm_crtc *crtc = new_con_state->crtc; > > + struct drm_crtc_state *new_crtc_state; > > + > > + if (!blob_equal(new_con_state->hdr_output_metadata, > > + old_con_state->hdr_output_metadata) || > > + new_con_state->colorspace != old_con_state->colorspace) { > > + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); > > + if (IS_ERR(new_crtc_state)) > > + return PTR_ERR(new_crtc_state); > > + > > + new_crtc_state->mode_changed = > > + !new_con_state->hdr_output_metadata || > > + !old_con_state->hdr_output_metadata || > > + new_con_state->colorspace != old_con_state- > >colorspace; > > + } > > + > > + return 0; > > +} > > + > > +static const struct drm_connector_funcs cdns_hdmi_connector_funcs = { > > + .fill_modes = drm_helper_probe_single_connector_modes, > > + .destroy = drm_connector_cleanup, > > + .reset = drm_atomic_helper_connector_reset, > > + .atomic_duplicate_state = > drm_atomic_helper_connector_duplicate_state, > > + .atomic_destroy_state = > > +drm_atomic_helper_connector_destroy_state, > > +}; > > + > > +static const struct drm_connector_helper_funcs > > cdns_hdmi_connector_helper_funcs = { + .get_modes = > > cdns_hdmi_connector_get_modes, > > + .atomic_check = cdns_hdmi_connector_atomic_check, }; > > + > > +static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, > > + enum drm_bridge_attach_flags flags) > { > > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > > + struct drm_mode_config *config = &bridge->dev->mode_config; > > + struct drm_encoder *encoder = bridge->encoder; > > + struct drm_connector *connector = &mhdp->connector; > > + > > + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { > > + connector->interlace_allowed = 0; > > + connector->polled = DRM_CONNECTOR_POLL_HPD; > > + > > + drm_connector_helper_add(connector, > &cdns_hdmi_connector_helper_funcs); > > + > > + drm_connector_init(bridge->dev, connector, > &cdns_hdmi_connector_funcs, > > + > DRM_MODE_CONNECTOR_HDMIA); > > + > > + drm_object_attach_property(&connector->base, > > + config- > >hdr_output_metadata_property, 0); > > + > > + if > (!drm_mode_create_hdmi_colorspace_property(connector)) > > + > drm_object_attach_property(&connector->base, > > + connector- > >colorspace_property, 0); > > + > > + drm_connector_attach_encoder(connector, encoder); > > + } > > + > > + return 0; > > +} > > + > > +static enum drm_mode_status > > +cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge, > > + const struct drm_display_info *info, > > + const struct drm_display_mode > *mode) { > > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > > + enum drm_mode_status mode_status = MODE_OK; > > + union phy_configure_opts phy_cfg; > > + int ret; > > + > > + /* We don't support double-clocked and Interlaced modes */ > > + if (mode->flags & DRM_MODE_FLAG_DBLCLK || > > + mode->flags & DRM_MODE_FLAG_INTERLACE) > > + return MODE_BAD; > > + > > + /* MAX support pixel clock rate 594MHz */ > > + if (mode->clock > 594000) > > + return MODE_CLOCK_HIGH; > > + > > + /* 4096x2160 is not supported */ > > + if (mode->hdisplay > 3840 || mode->vdisplay > 2160) > > + return MODE_BAD_HVALUE; > > + > > + /* Check modes supported by PHY */ > > + phy_cfg.hdmi.pixel_clk_rate = mode->clock; > > + ret = phy_validate(mhdp->phy, PHY_MODE_HDMI, 0, &phy_cfg); > > + if (ret < 0) > > + return MODE_CLOCK_RANGE; > > + > > + return mode_status; > > +} > > + > > +bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, > > + const struct drm_display_mode > *mode, > > + struct drm_display_mode > *adjusted_mode) > > +{ > > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > > + struct video_info *video = &mhdp->video_info; > > + > > + video->bpc = 8; > > + video->color_fmt = DRM_COLOR_FORMAT_RGB444; > > + > > + return true; > > +} > > + > > +static struct edid *cdns_hdmi_bridge_get_edid(struct drm_bridge *bridge, > > + struct > drm_connector > *connector) > > +{ > > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > > + > > + return drm_do_get_edid(connector, cdns_hdmi_get_edid_block, > > +mhdp); } > > + > > +static void cdns_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, > > + struct drm_bridge_state > *old_state) > > +{ > > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > > + > > + mhdp->curr_conn = NULL; > > + > > + mutex_lock(&mhdp->lock); > > + phy_power_off(mhdp->phy); > > + mutex_unlock(&mhdp->lock); > > +} > > + > > +static void cdns_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, > > + struct drm_bridge_state > *old_state) > > +{ > > + struct cdns_mhdp_device *mhdp = bridge->driver_private; > > + struct drm_atomic_state *state = old_state->base.state; > > + struct drm_connector *connector; > > + struct video_info *video = &mhdp->video_info; > > + struct drm_crtc_state *crtc_state; > > + struct drm_connector_state *conn_state; > > + const struct drm_display_mode *mode; > > + > > + connector = drm_atomic_get_new_connector_for_encoder(state, > > + > bridge->encoder); > > + if (WARN_ON(!connector)) > > + return; > > + > > + mhdp->curr_conn = connector; > > + > > + conn_state = drm_atomic_get_new_connector_state(state, > connector); > > + if (WARN_ON(!conn_state)) > > + return; > > + > > + crtc_state = drm_atomic_get_new_crtc_state(state, > conn_state->crtc); > > + if (WARN_ON(!crtc_state)) > > + return; > > + > > + mode = &crtc_state->adjusted_mode; > > + DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, > mode->vdisplay, > > + mode- > >clock); > > + memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); > > + > > + video->v_sync_polarity = !!(mode->flags & > DRM_MODE_FLAG_NVSYNC); > > + video->h_sync_polarity = !!(mode->flags & > DRM_MODE_FLAG_NHSYNC); > > + > > + mutex_lock(&mhdp->lock); > > + cdns_hdmi_mode_set(mhdp); > > + mutex_unlock(&mhdp->lock); > > +} > > + > > +static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = { > > + .attach = cdns_hdmi_bridge_attach, > > + .detect = cdns_hdmi_bridge_detect, > > + .get_edid = cdns_hdmi_bridge_get_edid, > > + .mode_valid = cdns_hdmi_bridge_mode_valid, > > + .mode_fixup = cdns_hdmi_bridge_mode_fixup, > > + .atomic_enable = cdns_hdmi_bridge_atomic_enable, > > + .atomic_disable = cdns_hdmi_bridge_atomic_disable, > > + .atomic_duplicate_state = > drm_atomic_helper_bridge_duplicate_state, > > + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, > > + .atomic_reset = drm_atomic_helper_bridge_reset, }; > > + > > +static void hotplug_work_func(struct work_struct *work) { > > + struct cdns_mhdp_device *mhdp = container_of(work, > > + struct > cdns_mhdp_device, > hotplug_work.work); > > + enum drm_connector_status status = cdns_hdmi_detect(mhdp); > > + > > + drm_bridge_hpd_notify(&mhdp->bridge, status); > > + > > + if (status == connector_status_connected) { > > + DRM_INFO("HDMI Cable Plug In\n"); > > + enable_irq(mhdp->irq[IRQ_OUT]); > > + } else if (status == connector_status_disconnected) { > > + /* Cable Disconnedted */ > > + DRM_INFO("HDMI Cable Plug Out\n"); > > + enable_irq(mhdp->irq[IRQ_IN]); > > + } > > +} > > + > > +static irqreturn_t cdns_hdmi_irq_thread(int irq, void *data) { > > + struct cdns_mhdp_device *mhdp = data; > > + > > + disable_irq_nosync(irq); > > + > > + mod_delayed_work(system_wq, &mhdp->hotplug_work, > > + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int cdns_mhdp_imx_probe(struct platform_device *pdev) { > > + struct device *dev = &pdev->dev; > > + struct cdns_mhdp_device *mhdp; > > + struct platform_device_info pdevinfo; > > + struct resource *res; > > + u32 reg; > > + int ret; > > + > > + mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); > > + if (!mhdp) > > + return -ENOMEM; > > + > > + mutex_init(&mhdp->lock); > > + mutex_init(&mhdp->mbox_mutex); > > + > > + INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func); > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (!res) > > + return -ENODEV; > > + mhdp->regs = devm_ioremap(dev, res->start, resource_size(res)); > > + if (IS_ERR(mhdp->regs)) > > + return PTR_ERR(mhdp->regs); > > Please use devm_platform_get_and_ioremap_resource. Both HDMI PHY driver and mhdp HDMI driver should access same APB base register offset for mailbox. devm_ioremap_resource could not support such feature. > > > + mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, > 0); > > + if (IS_ERR(mhdp->phy)) { > > + dev_err(dev, "no PHY configured\n"); > > + return PTR_ERR(mhdp->phy); > > + } > > Please use dev_err_probe(). OK. > > > + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); > > + if (mhdp->irq[IRQ_IN] < 0) { > > + dev_info(dev, "No plug_in irq number\n"); > > + return -EPROBE_DEFER; > > + } > > Please use dev_err_probe(). OK. > > > + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, > "plug_out"); > > + if (mhdp->irq[IRQ_OUT] < 0) { > > + dev_info(dev, "No plug_out irq number\n"); > > + return -EPROBE_DEFER; > > + } > > Please use dev_err_probe(). OK. > > > + /* > > + * Wait for the KEEP_ALIVE "message" on the first 8 bits. > > + * Updated each sched "tick" (~2ms) > > + */ > > + ret = readl_poll_timeout(mhdp->regs + KEEP_ALIVE, reg, > > + reg & CDNS_KEEP_ALIVE_MASK, > 500, > > + CDNS_KEEP_ALIVE_TIMEOUT); > > This freezes my board TQMa8MQ > (arch/arm64/boot/dts/freescale/imx8mq-tqma8mq- > mba8mx.dts) completly if this and the PHY driver are compiled in. I have > "pd_ignore_unused clk_ignore_unused" passed to kernel command line, so I > have no idea what's wrong here. Here is the first time in the driver to access hdmi register when driver probe. For imx8mq hdmi/dp, mdhp hdmi apb clock and core clock are managed by ROM code, they are always on when device bootup. Could you dump the clock tree without "pd_ignore_unused clk_ignore_unused" ? > > Best regards, > Alexander > B.R Sandor > > + if (ret) { > > + dev_err(mhdp->dev, > > + "device didn't give any life sign: reg %d\n", > reg); > > + return ret; > > + } > > + > > + ret = phy_init(mhdp->phy); > > + if (ret) { > > + dev_err(mhdp->dev, "Failed to initialize PHY: %d\n", ret); > > + return -ENODEV; > > + } > > + > > + /* Enable Hotplug Detect thread */ > > + irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); > > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], > > + NULL, > cdns_hdmi_irq_thread, > > + IRQF_ONESHOT, > dev_name(dev), > > + mhdp); > > + if (ret < 0) { > > + dev_err(dev, "can't claim irq %d\n", > > + mhdp- > >irq[IRQ_IN]); > > + return -EINVAL; > > + } > > + > > + irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN); > > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], > > + NULL, > cdns_hdmi_irq_thread, > > + IRQF_ONESHOT, > dev_name(dev), > > + mhdp); > > + if (ret < 0) { > > + dev_err(dev, "can't claim irq %d\n", > > + mhdp- > >irq[IRQ_OUT]); > > + return -EINVAL; > > + } > > + > > + mhdp->dev = dev; > > + > > + if (cdns_mhdp_hdmi_read_hpd(mhdp)) > > + enable_irq(mhdp->irq[IRQ_OUT]); > > + else > > + enable_irq(mhdp->irq[IRQ_IN]); > > + > > + mhdp->bridge.driver_private = mhdp; > > + mhdp->bridge.funcs = &cdns_hdmi_bridge_funcs; > > + mhdp->bridge.of_node = dev->of_node; > > + mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | > DRM_BRIDGE_OP_EDID | > > + DRM_BRIDGE_OP_HPD; > > + mhdp->bridge.type = DRM_MODE_CONNECTOR_HDMIA; > > + drm_bridge_add(&mhdp->bridge); > > + > > + memset(&pdevinfo, 0, sizeof(pdevinfo)); > > + pdevinfo.parent = dev; > > + pdevinfo.id = PLATFORM_DEVID_AUTO; > > + > > + dev_set_drvdata(dev, mhdp); > > + mhdp->plat_data = of_device_get_match_data(dev); > > + > > + return 0; > > +} > > + > > +static int cdns_mhdp_imx_remove(struct platform_device *pdev) { > > + struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev); > > + int ret = 0; > > + > > + drm_bridge_remove(&mhdp->bridge); > > + > > + return ret; > > +} > > + > > +static struct cdns_plat_data imx8mq_hdmi_drv_data = { > > + .lane_mapping = 0xe4, > > +}; > > + > > +static const struct of_device_id cdns_mhdp_imx_dt_ids[] = { > > + { .compatible = "cdns,mhdp-imx8mq-hdmi", > > + .data = &imx8mq_hdmi_drv_data > > + }, > > + {}, > > +}; > > +MODULE_DEVICE_TABLE(of, cdns_mhdp_imx_dt_ids); > > + > > +static struct platform_driver cdns_mhdp_imx_platform_driver = { > > + .probe = cdns_mhdp_imx_probe, > > + .remove = cdns_mhdp_imx_remove, > > + .driver = { > > + .name = "cdns-mhdp-imx8mq-hdmi", > > + .of_match_table = cdns_mhdp_imx_dt_ids, > > + }, > > +}; > > + > > +module_platform_driver(cdns_mhdp_imx_platform_driver); > > + > > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > > +MODULE_DESCRIPTION("Cadence HDMI transmitter driver"); > > +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:cdns-hdmi"); > > >
Hi Sandor, Am Mittwoch, 9. November 2022, 14:26:14 CET schrieb Sandor Yu: > Thanks for your comments. > > > > -----Original Message----- > > From: Alexander Stein <alexander.stein@ew.tq-group.com> > > Sent: 2022年11月8日 21:17 > > To: jonas@kwiboo.se; Sandor Yu <sandor.yu@nxp.com> > > Cc: dri-devel@lists.freedesktop.org; devicetree@vger.kernel.org; > > linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; > > linux-phy@lists.infradead.org; andrzej.hajda@intel.com; > > neil.armstrong@linaro.org; robert.foss@linaro.org; > > Laurent.pinchart@ideasonboard.com; jernej.skrabec@gmail.com; > > kishon@ti.com; vkoul@kernel.org; Oliver Brown <oliver.brown@nxp.com>; > > krzysztof.kozlowski+dt@linaro.org; sam@ravnborg.org; > > jani.nikula@intel.com; tzimmermann@suse.de; s.hauer@pengutronix.de; > > javierm@redhat.com; > > penguin-kernel@i-love.sakura.ne.jp; robh+dt@kernel.org; dl-linux-imx > > <linux-imx@nxp.com>; kernel@pengutronix.de; Sandor Yu > > <sandor.yu@nxp.com>; shawnguo@kernel.org; p.yadav@ti.com; > > maxime@cerno.tech > > Subject: [EXT] Re: [v2 06/10] drm: bridge: cadence: Add MHDP HDMI driver > > for i.MX8MQ > > > > Caution: EXT Email > > > > Hello, > > > > thanks for working on this and the updated version. > > > > Am Freitag, 4. November 2022, 07:44:56 CET schrieb Sandor Yu: > > > > > Add a new DRM HDMI bridge driver for Candence MHDP used in i.MX8MQ > > > SOC. MHDP IP could support HDMI or DisplayPort standards according > > > embedded Firmware running in the uCPU. > > > > > > > > > > > > For iMX8MQ SOC, the HDMI FW was loaded and activated by SOC ROM > > > > code. > > > > > Bootload binary included HDMI FW was required for the driver. > > > > > > > > > > > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > > > --- > > > > > > drivers/gpu/drm/bridge/cadence/Kconfig | 12 + > > > .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 1038 > > > > +++++++++++++++++ > > > > > 2 files changed, 1050 insertions(+) > > > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > > > > > > > > > > > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > > > b/drivers/gpu/drm/bridge/cadence/Kconfig index > > > e79ae1af3765..377452d09992 > > > 100644 > > > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > > > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig [snip] > > > +static int cdns_hdmi_get_edid_block(void *data, u8 *edid, > > > + u32 block, size_t length) { > > > + struct cdns_mhdp_device *mhdp = data; > > > + u8 msg[2], reg[5], i; > > > + int ret; > > > + > > > + mutex_lock(&mhdp->mbox_mutex); > > > + > > > + for (i = 0; i < 4; i++) { > > > > > > What is i? It is not used inside the loop. > > EDID data read by HDMI firmware are not guarantee 100% successful. > Base on experiments, try 4 times if EDID read failed. Mh, 4 times sounds a bit too arbitrary to me. How about using a timeout in ms, like 50ms, for retrying to read the EDID? [snip] > > > +static int cdns_mhdp_imx_probe(struct platform_device *pdev) { > > > + struct device *dev = &pdev->dev; > > > + struct cdns_mhdp_device *mhdp; > > > + struct platform_device_info pdevinfo; > > > + struct resource *res; > > > + u32 reg; > > > + int ret; > > > + > > > + mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); > > > + if (!mhdp) > > > + return -ENOMEM; > > > + > > > + mutex_init(&mhdp->lock); > > > + mutex_init(&mhdp->mbox_mutex); > > > + > > > + INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func); > > > + > > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > > + if (!res) > > > + return -ENODEV; > > > + mhdp->regs = devm_ioremap(dev, res->start, resource_size(res)); > > > + if (IS_ERR(mhdp->regs)) > > > + return PTR_ERR(mhdp->regs); > > > > > > Please use devm_platform_get_and_ioremap_resource. > > Both HDMI PHY driver and mhdp HDMI driver should access same APB base > register offset for mailbox. devm_ioremap_resource could not support such > feature. Oh I see, both remap the same range. To be honest I do not like this. Is there a need to map overlapping ranges in both drivers? How can you avoid race conditions due to simultaneous accesses? > > > + mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, > > > > 0); > > > > > + if (IS_ERR(mhdp->phy)) { > > > + dev_err(dev, "no PHY configured\n"); > > > + return PTR_ERR(mhdp->phy); > > > + } > > > > > > Please use dev_err_probe(). > > OK. > > > > > > > > + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); > > > + if (mhdp->irq[IRQ_IN] < 0) { > > > + dev_info(dev, "No plug_in irq number\n"); > > > + return -EPROBE_DEFER; > > > + } > > > > > > Please use dev_err_probe(). > > OK. > > > > > > > > + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, > > > > "plug_out"); > > > > > + if (mhdp->irq[IRQ_OUT] < 0) { > > > + dev_info(dev, "No plug_out irq number\n"); > > > + return -EPROBE_DEFER; > > > + } > > > > > > Please use dev_err_probe(). > > OK. > > > > > > > > + /* > > > + * Wait for the KEEP_ALIVE "message" on the first 8 bits. > > > + * Updated each sched "tick" (~2ms) > > > + */ > > > + ret = readl_poll_timeout(mhdp->regs + KEEP_ALIVE, reg, > > > + reg & CDNS_KEEP_ALIVE_MASK, > > > > 500, > > > > > + CDNS_KEEP_ALIVE_TIMEOUT); > > > > > > This freezes my board TQMa8MQ > > (arch/arm64/boot/dts/freescale/imx8mq-tqma8mq- > > mba8mx.dts) completly if this and the PHY driver are compiled in. I have > > "pd_ignore_unused clk_ignore_unused" passed to kernel command line, so I > > have no idea what's wrong here. > > Here is the first time in the driver to access hdmi register when driver > probe. For imx8mq hdmi/dp, mdhp hdmi apb clock and core clock are managed > by ROM code, they are always on when device bootup. Could you dump the > clock tree without "pd_ignore_unused clk_ignore_unused" ? I noticed too this is the 1st access, so I have no idea what's wrong here. Here is my /sys/kernel/debug/clk/clk_summary from using the regular DT without enabling 'dcss', 'hdmi_phy' and 'mhdp_hdmi': enable prepare protect duty hardware clock count count count rate accuracy phase cycle enable ------------------------------------------------------------------------------------------------------- sys2_pll_out 7 7 0 1000000000 0 0 50000 Y sys_pll2_out_monitor 0 0 0 1000000000 0 0 50000 Y sys2_pll_1000m 0 0 0 1000000000 0 0 50000 Y sys2_pll_500m 1 2 0 500000000 0 0 50000 Y audio_ahb 0 1 0 500000000 0 0 50000 N ipg_audio_root 0 1 0 250000000 0 0 50000 Y sdma2_clk 0 2 0 250000000 0 0 50000 N sai6_ipg_clk 0 0 0 250000000 0 0 50000 N sai5_ipg_clk 0 0 0 250000000 0 0 50000 N sai4_ipg_clk 0 0 0 250000000 0 0 50000 N sai1_ipg_clk 0 0 0 250000000 0 0 50000 N nand 0 0 0 500000000 0 0 50000 N nand_root_clk 0 0 0 500000000 0 0 50000 N usb_bus 2 2 0 500000000 0 0 50000 Y usb2_ctrl_root_clk 1 1 0 500000000 0 0 50000 Y usb1_ctrl_root_clk 1 1 0 500000000 0 0 50000 Y sys2_pll_333m 1 1 0 333333333 0 0 50000 Y main_axi 1 1 0 333333333 0 0 50000 Y sys2_pll_250m 2 2 0 250000000 0 0 50000 Y pcie1_ctrl 1 1 0 250000000 0 0 50000 Y pcie1_root_clk 1 1 0 250000000 0 0 50000 Y pcie2_ctrl 1 1 0 250000000 0 0 50000 Y pcie2_root_clk 1 1 0 250000000 0 0 50000 Y sys2_pll_200m 3 3 0 200000000 0 0 50000 Y ecspi3 0 0 0 200000000 0 0 50000 N ecspi3_root_clk 0 0 0 200000000 0 0 50000 N ecspi2 1 1 0 200000000 0 0 50000 Y ecspi2_root_clk 2 2 0 200000000 0 0 50000 Y ecspi1 1 1 0 200000000 0 0 50000 Y ecspi1_root_clk 2 2 0 200000000 0 0 50000 Y gic 1 1 0 200000000 0 0 50000 Y arm_m4_core 0 0 0 200000000 0 0 50000 N sys2_pll_166m 0 0 0 166666666 0 0 50000 Y sys2_pll_125m 1 1 0 125000000 0 0 50000 Y enet_ref 1 1 0 125000000 0 0 50000 Y sys2_pll_100m 3 3 0 100000000 0 0 50000 Y pcie1_phy 1 1 0 100000000 0 0 50000 Y pcie2_phy 1 1 0 100000000 0 0 50000 Y enet_timer 1 1 0 100000000 0 0 50000 Y sys2_pll_50m 1 1 0 50000000 0 0 50000 Y enet_phy 1 1 0 50000000 0 0 50000 Y sys1_pll_out 5 5 0 800000000 0 0 50000 Y sys_pll1_out_monitor 0 0 0 800000000 0 0 50000 Y sys1_pll_800m 2 2 0 800000000 0 0 50000 Y vpu_bus 0 0 0 800000000 0 0 50000 N vpu_dec_root_clk 0 0 0 800000000 0 0 50000 N arm_a53_div 0 0 0 800000000 0 0 50000 N dram_apb 1 1 0 160000000 0 0 50000 Y noc 1 1 0 800000000 0 0 50000 Y disp_rtrm 0 0 0 400000000 0 0 50000 N disp_rtrm_root_clk 0 0 0 400000000 0 0 50000 N disp_apb 0 0 0 133333334 0 0 50000 N disp_apb_root_clk 0 0 0 133333334 0 0 50000 N disp_axi 0 0 0 800000000 0 0 50000 N disp_axi_root_clk 0 0 0 800000000 0 0 50000 N sys1_pll_400m 0 0 0 400000000 0 0 50000 Y usdhc2 0 0 0 400000000 0 0 50000 N usdhc2_root_clk 0 0 0 400000000 0 0 50000 N usdhc1 0 0 0 400000000 0 0 50000 N usdhc1_root_clk 0 0 0 400000000 0 0 50000 N sys1_pll_266m 1 1 0 266666666 0 0 50000 Y nand_usdhc_bus 0 0 0 266666666 0 0 50000 N nand_usdhc_rawnand_clk 0 0 0 266666666 0 0 50000 N enet_axi 1 1 0 266666666 0 0 50000 Y enet1_root_clk 2 2 0 266666666 0 0 50000 Y sys1_pll_200m 0 0 0 200000000 0 0 50000 Y sys1_pll_160m 0 0 0 160000000 0 0 50000 Y sys1_pll_133m 1 1 0 133333333 0 0 50000 Y ahb 9 4 0 133333333 0 0 50000 Y ipg_root 8 8 0 66666667 0 0 50000 Y sdma1_clk 6 1 0 66666667 0 0 50000 Y tmu_root_clk 1 1 0 66666667 0 0 50000 Y sai3_ipg_clk 0 0 0 66666667 0 0 50000 N sai2_ipg_clk 0 0 0 66666667 0 0 50000 N ocotp_root_clk 0 0 0 66666667 0 0 50000 N mu_root_clk 0 0 0 66666667 0 0 50000 N gpio5_root_clk 1 1 0 66666667 0 0 50000 Y gpio4_root_clk 1 1 0 66666667 0 0 50000 Y gpio3_root_clk 1 1 0 66666667 0 0 50000 Y gpio2_root_clk 1 1 0 66666667 0 0 50000 Y gpio1_root_clk 1 1 0 66666667 0 0 50000 Y sys1_pll_100m 2 2 0 100000000 0 0 50000 Y usb_phy_ref 2 2 0 100000000 0 0 50000 Y usb2_phy_root_clk 1 1 0 100000000 0 0 50000 Y usb1_phy_root_clk 1 1 0 100000000 0 0 50000 Y usb_core_ref 2 2 0 100000000 0 0 50000 Y qspi 0 0 0 100000000 0 0 50000 N qspi_root_clk 0 0 0 100000000 0 0 50000 N dram_alt 0 0 0 100000000 0 0 50000 N dram_alt_root 0 0 0 25000000 0 0 50000 Y sys1_pll_80m 2 2 0 80000000 0 0 50000 Y pcie1_aux 1 1 0 10000000 0 0 50000 Y pcie2_aux 1 1 0 10000000 0 0 50000 Y uart2 0 0 0 80000000 0 0 50000 N uart2_root_clk 0 0 0 80000000 0 0 50000 N uart1 0 0 0 80000000 0 0 50000 N uart1_root_clk 0 0 0 80000000 0 0 50000 N sys1_pll_40m 0 0 0 40000000 0 0 50000 Y wrclk 0 0 0 40000000 0 0 50000 N dummy 0 0 0 0 0 0 50000 Y clk-xtal25 2 2 0 25000000 0 0 50000 Y DIF3 0 0 0 100000000 0 0 50000 Y DIF2 1 1 0 100000000 0 0 50000 Y DIF1 0 0 0 100000000 0 0 50000 Y DIF0 1 1 0 100000000 0 0 50000 Y clock 0 0 0 32768 0 0 50000 Y clk_ext4 0 0 0 133000000 0 0 50000 Y clk_ext3 0 0 0 133000000 0 0 50000 Y clk_ext2 0 0 0 133000000 0 0 50000 Y clk_ext1 0 0 0 133000000 0 0 50000 Y hdmi_phy_27m 0 0 0 27000000 0 0 50000 Y osc_27m 0 0 0 27000000 0 0 50000 Y osc_25m 7 11 0 25000000 0 0 50000 Y gpt_3m 0 0 0 3125000 0 0 50000 Y csi2_esc 0 0 0 25000000 0 0 50000 N csi2_phy_ref 0 0 0 25000000 0 0 50000 N csi2_core 0 0 0 25000000 0 0 50000 N csi2_root_clk 0 0 0 25000000 0 0 50000 N csi1_esc 0 0 0 25000000 0 0 50000 N csi1_phy_ref 0 0 0 25000000 0 0 50000 N csi1_core 0 0 0 25000000 0 0 50000 N csi1_root_clk 0 0 0 25000000 0 0 50000 N dsi_ahb 0 0 0 25000000 0 0 50000 N dsi_ipg_div 0 0 0 12500000 0 0 50000 Y dsi_esc 0 0 0 25000000 0 0 50000 N dsi_dbi 0 0 0 25000000 0 0 50000 N dsi_phy_ref 0 0 0 25000000 0 0 50000 N dsi_core 0 0 0 25000000 0 0 50000 N clko2 0 0 0 25000000 0 0 50000 N clko1 0 0 0 25000000 0 0 50000 N wdog 1 1 0 25000000 0 0 50000 Y wdog3_root_clk 0 0 0 25000000 0 0 50000 N wdog2_root_clk 0 0 0 25000000 0 0 50000 N wdog1_root_clk 1 1 0 25000000 0 0 50000 Y gpt1 0 0 0 25000000 0 0 50000 N gpt1_root_clk 0 0 0 25000000 0 0 50000 N pwm4 0 0 0 25000000 0 0 50000 N pwm4_root_clk 0 0 0 25000000 0 0 50000 N pwm3 0 0 0 25000000 0 0 50000 N pwm3_root_clk 0 0 0 25000000 0 0 50000 N pwm2 0 0 0 25000000 0 0 50000 N pwm2_root_clk 0 0 0 25000000 0 0 50000 N pwm1 0 0 0 25000000 0 0 50000 N pwm1_root_clk 0 0 0 25000000 0 0 50000 N uart4 0 0 0 25000000 0 0 50000 N uart4_root_clk 0 0 0 25000000 0 0 50000 N uart3 1 1 0 25000000 0 0 50000 Y uart3_root_clk 4 4 0 25000000 0 0 50000 Y i2c4 0 0 0 25000000 0 0 50000 N i2c4_root_clk 0 0 0 25000000 0 0 50000 N i2c3 0 1 0 25000000 0 0 50000 N i2c3_root_clk 0 1 0 25000000 0 0 50000 N i2c2 0 1 0 25000000 0 0 50000 N i2c2_root_clk 0 1 0 25000000 0 0 50000 N i2c1 0 1 0 25000000 0 0 50000 N i2c1_root_clk 0 1 0 25000000 0 0 50000 N spdif2 0 0 0 25000000 0 0 50000 N spdif1 0 0 0 25000000 0 0 50000 N sai6 0 0 0 25000000 0 0 50000 N sai6_root_clk 0 0 0 25000000 0 0 50000 N sai5 0 0 0 25000000 0 0 50000 N sai5_root_clk 0 0 0 25000000 0 0 50000 N sai4 0 0 0 25000000 0 0 50000 N sai4_root_clk 0 0 0 25000000 0 0 50000 N sai2 0 0 0 25000000 0 0 50000 N sai2_root_clk 0 0 0 25000000 0 0 50000 N sai1 0 0 0 25000000 0 0 50000 N sai1_root_clk 0 0 0 25000000 0 0 50000 N lcdif_pixel 0 0 0 25000000 0 0 50000 N disp_dc8000 0 0 0 25000000 0 0 50000 N disp_root_clk 0 0 0 25000000 0 0 50000 N disp_dtrc 0 0 0 25000000 0 0 50000 N noc_apb 1 1 0 25000000 0 0 50000 Y video2_pll1_ref_sel 0 0 0 25000000 0 0 50000 Y video2_pll_out 0 0 0 25000000 0 0 50000 Y video_pll2_out_monitor 0 0 0 25000000 0 0 50000 Y dram_pll1_ref_sel 1 1 0 25000000 0 0 50000 Y dram_pll_out 2 2 0 800000000 0 0 50000 Y dram_core_clk 1 1 0 800000000 0 0 50000 Y dram_pll_out_monitor 0 0 0 800000000 0 0 50000 Y sys3_pll1_ref_sel 1 1 0 25000000 0 0 50000 Y sys3_pll_out 1 1 0 25000000 0 0 50000 Y sys_pll3_out_monitor 0 0 0 25000000 0 0 50000 Y video_pll1_ref_sel 0 0 0 25000000 0 0 50000 Y video_pll1_bypass 0 0 0 25000000 0 0 50000 Y video_pll1_out_monitor 0 0 0 25000000 0 0 50000 Y video_pll1_out 0 0 0 25000000 0 0 50000 N dc_pixel 0 0 0 5000000 0 0 50000 N video_pll1_ref_div 0 0 0 5000000 0 0 50000 Y video_pll1 0 0 0 650000000 0 0 50000 Y audio_pll2_ref_sel 0 0 0 25000000 0 0 50000 Y audio_pll2_ref_div 0 0 0 5000000 0 0 50000 Y audio_pll2 0 0 0 722534397 0 0 50000 Y audio_pll2_bypass 0 0 0 722534397 0 0 50000 Y audio_pll2_out_monitor 0 0 0 722534397 0 0 50000 Y audio_pll2_out 0 0 0 722534397 0 0 50000 N audio_pll1_ref_sel 0 0 0 25000000 0 0 50000 Y audio_pll1_ref_div 0 0 0 5000000 0 0 50000 Y audio_pll1 0 0 0 786431998 0 0 50000 Y audio_pll1_bypass 0 0 0 786431998 0 0 50000 Y audio_pll1_out_monitor 0 0 0 786431998 0 0 50000 Y audio_pll1_out 0 0 0 786431998 0 0 50000 N sai3 0 0 0 49152000 0 0 50000 N sai3_root_clk 0 0 0 49152000 0 0 50000 N pll 0 0 0 196608000 0 0 50000 Y codec_clkin 0 0 0 196608000 0 0 50000 Y nadc 0 0 0 196608000 0 0 50000 Y madc 0 0 0 196608000 0 0 50000 Y ndac 0 0 0 196608000 0 0 50000 Y mdac 0 0 0 196608000 0 0 50000 Y bdiv 0 0 0 196608000 0 0 50000 Y vpu_pll_ref_sel 0 1 0 25000000 0 0 50000 Y vpu_pll_ref_div 0 1 0 5000000 0 0 50000 Y vpu_pll 0 1 0 600000000 0 0 50000 Y vpu_pll_bypass 0 1 0 600000000 0 0 50000 Y vpu_pll_out_monitor 0 0 0 600000000 0 0 50000 Y vpu_pll_out 0 2 0 600000000 0 0 50000 N vpu_g2 0 1 0 600000000 0 0 50000 N vpu_g2_root_clk 0 1 0 600000000 0 0 50000 N vpu_g1 0 1 0 600000000 0 0 50000 N vpu_g1_root_clk 0 1 0 600000000 0 0 50000 N gpu_pll_ref_sel 0 0 0 25000000 0 0 50000 Y gpu_pll_ref_div 0 0 0 5000000 0 0 50000 Y gpu_pll 0 0 0 800000000 0 0 50000 Y gpu_pll_bypass 0 0 0 800000000 0 0 50000 Y gpu_pll_out_monitor 0 0 0 800000000 0 0 50000 Y pllout_monitor_sel 0 0 0 800000000 0 0 50000 Y pllout_monitor_clk2 0 0 0 800000000 0 0 50000 N gpu_pll_out 0 0 0 800000000 0 0 50000 N gpu_ahb 0 0 0 800000000 0 0 50000 N gpu_axi 0 0 0 800000000 0 0 50000 N gpu_shader 0 0 0 800000000 0 0 50000 N gpu_core 0 0 0 800000000 0 0 50000 N gpu_root_clk 0 0 0 800000000 0 0 50000 N arm_pll_ref_sel 1 1 0 25000000 0 0 50000 Y arm_pll_ref_div 1 1 0 5000000 0 0 50000 Y arm_pll 1 1 0 800000000 0 0 50000 Y arm_pll_bypass 1 1 0 800000000 0 0 50000 Y arm_pll_out_monitor 0 0 0 800000000 0 0 50000 Y arm_pll_out 1 1 0 800000000 0 0 50000 Y arm_a53_core 1 1 0 800000000 0 0 50000 Y arm 1 1 0 800000000 0 0 50000 Y vpu_core 0 0 0 800000000 0 0 50000 N ckil 2 2 0 32768 0 0 50000 Y Thanks and best regards Alexander
Hi Alexander, > -----Original Message----- > From: Alexander Stein <alexander.stein@ew.tq-group.com> > Sent: 2022年11月10日 23:44 > To: Sandor Yu <sandor.yu@nxp.com> > Cc: jonas@kwiboo.se; dri-devel@lists.freedesktop.org; > devicetree@vger.kernel.org; linux-arm-kernel@lists.infradead.org; > linux-kernel@vger.kernel.org; linux-phy@lists.infradead.org; > andrzej.hajda@intel.com; neil.armstrong@linaro.org; robert.foss@linaro.org; > Laurent.pinchart@ideasonboard.com; jernej.skrabec@gmail.com; > kishon@ti.com; vkoul@kernel.org; Oliver Brown <oliver.brown@nxp.com>; > krzysztof.kozlowski+dt@linaro.org; sam@ravnborg.org; > tzimmermann@suse.de; s.hauer@pengutronix.de; javierm@redhat. com > <javierm@redhat.com>; penguin-kernel@i-love.sakura.ne.jp; > robh+dt@kernel.org; dl-linux-imx <linux-imx@nxp.com>; > kernel@pengutronix.de; shawnguo@kernel.org; p.yadav@ti.com; > maxime@cerno.tech > Subject: RE: [EXT] Re: [v2 06/10] drm: bridge: cadence: Add MHDP HDMI > driver for i.MX8MQ > > Caution: EXT Email > > Hi Sandor, > > Am Mittwoch, 9. November 2022, 14:26:14 CET schrieb Sandor Yu: > > Thanks for your comments. > > > > > > > -----Original Message----- > > > From: Alexander Stein <alexander.stein@ew.tq-group.com> > > > Sent: 2022年11月8日 21:17 > > > To: jonas@kwiboo.se; Sandor Yu <sandor.yu@nxp.com> > > > Cc: dri-devel@lists.freedesktop.org; devicetree@vger.kernel.org; > > > linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; > > > linux-phy@lists.infradead.org; andrzej.hajda@intel.com; > > > neil.armstrong@linaro.org; robert.foss@linaro.org; > > > Laurent.pinchart@ideasonboard.com; jernej.skrabec@gmail.com; > > > kishon@ti.com; vkoul@kernel.org; Oliver Brown > > > <oliver.brown@nxp.com>; krzysztof.kozlowski+dt@linaro.org; > > > sam@ravnborg.org; jani.nikula@intel.com; > tzimmermann@suse.de; s.hauer@pengutronix.de; > > > javierm@redhat.com; > > > penguin-kernel@i-love.sakura.ne.jp; robh+dt@kernel.org; dl-linux-imx > > > <linux-imx@nxp.com>; kernel@pengutronix.de; Sandor Yu > > > <sandor.yu@nxp.com>; shawnguo@kernel.org; p.yadav@ti.com; > > > maxime@cerno.tech > > > Subject: [EXT] Re: [v2 06/10] drm: bridge: cadence: Add MHDP HDMI > > > driver for i.MX8MQ > > > > > > Caution: EXT Email > > > > > > Hello, > > > > > > thanks for working on this and the updated version. > > > > > > Am Freitag, 4. November 2022, 07:44:56 CET schrieb Sandor Yu: > > > > > > > Add a new DRM HDMI bridge driver for Candence MHDP used in > i.MX8MQ > > > > SOC. MHDP IP could support HDMI or DisplayPort standards according > > > > embedded Firmware running in the uCPU. > > > > > > > > > > > > > > > > For iMX8MQ SOC, the HDMI FW was loaded and activated by SOC ROM > > > > > > code. > > > > > > > Bootload binary included HDMI FW was required for the driver. > > > > > > > > > > > > > > > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > > > > --- > > > > > > > > drivers/gpu/drm/bridge/cadence/Kconfig | 12 + > > > > .../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 1038 > > > > > > +++++++++++++++++ > > > > > > > 2 files changed, 1050 insertions(+) create mode 100644 > > > > drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c > > > > > > > > > > > > > > > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > > > > b/drivers/gpu/drm/bridge/cadence/Kconfig index > > > > e79ae1af3765..377452d09992 > > > > 100644 > > > > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > > > > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig > > [snip] > > > > > +static int cdns_hdmi_get_edid_block(void *data, u8 *edid, > > > > + u32 block, size_t length) { > > > > + struct cdns_mhdp_device *mhdp = data; > > > > + u8 msg[2], reg[5], i; > > > > + int ret; > > > > + > > > > + mutex_lock(&mhdp->mbox_mutex); > > > > + > > > > + for (i = 0; i < 4; i++) { > > > > > > > > > What is i? It is not used inside the loop. > > > > EDID data read by HDMI firmware are not guarantee 100% successful. > > Base on experiments, try 4 times if EDID read failed. > > Mh, 4 times sounds a bit too arbitrary to me. How about using a timeout in ms, > like 50ms, for retrying to read the EDID? > For EDID read failed case, the mailbox read will timeout, additional timeout in the loop is not necessary. > [snip] > > > > > +static int cdns_mhdp_imx_probe(struct platform_device *pdev) { > > > > + struct device *dev = &pdev->dev; > > > > + struct cdns_mhdp_device *mhdp; > > > > + struct platform_device_info pdevinfo; > > > > + struct resource *res; > > > > + u32 reg; > > > > + int ret; > > > > + > > > > + mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); > > > > + if (!mhdp) > > > > + return -ENOMEM; > > > > + > > > > + mutex_init(&mhdp->lock); > > > > + mutex_init(&mhdp->mbox_mutex); > > > > + > > > > + INIT_DELAYED_WORK(&mhdp->hotplug_work, > hotplug_work_func); > > > > + > > > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > > > + if (!res) > > > > + return -ENODEV; > > > > + mhdp->regs = devm_ioremap(dev, res->start, > resource_size(res)); > > > > + if (IS_ERR(mhdp->regs)) > > > > + return PTR_ERR(mhdp->regs); > > > > > > > > > Please use devm_platform_get_and_ioremap_resource. > > > > Both HDMI PHY driver and mhdp HDMI driver should access same APB base > > register offset for mailbox. > devm_ioremap_resource could not support such > > feature. > > Oh I see, both remap the same range. To be honest I do not like this. Is there > a need to map overlapping ranges in both drivers? How can you avoid race > conditions due to simultaneous accesses? > To separate HDMI controller driver and PHY driver, I have to remap those registers in different drivers. Race conditions could be avoid in HDMI driver when PHY function is called. > > > > + mhdp->phy = devm_of_phy_get_by_index(dev, > pdev->dev.of_node, > > > > > > 0); > > > > > > > + if (IS_ERR(mhdp->phy)) { > > > > + dev_err(dev, "no PHY configured\n"); > > > > + return PTR_ERR(mhdp->phy); > > > > + } > > > > > > > > > Please use dev_err_probe(). > > > > OK. > > > > > > > > > > > > + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); > > > > + if (mhdp->irq[IRQ_IN] < 0) { > > > > + dev_info(dev, "No plug_in irq number\n"); > > > > + return -EPROBE_DEFER; > > > > + } > > > > > > > > > Please use dev_err_probe(). > > > > OK. > > > > > > > > > > > > + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, > > > > > > "plug_out"); > > > > > > > + if (mhdp->irq[IRQ_OUT] < 0) { > > > > + dev_info(dev, "No plug_out irq number\n"); > > > > + return -EPROBE_DEFER; > > > > + } > > > > > > > > > Please use dev_err_probe(). > > > > OK. > > > > > > > > > > > > + /* > > > > + * Wait for the KEEP_ALIVE "message" on the first 8 bits. > > > > + * Updated each sched "tick" (~2ms) > > > > + */ > > > > + ret = readl_poll_timeout(mhdp->regs + KEEP_ALIVE, reg, > > > > + reg & CDNS_KEEP_ALIVE_MASK, > > > > > > 500, > > > > > > > + CDNS_KEEP_ALIVE_TIMEOUT); > > > > > > > > > This freezes my board TQMa8MQ > > > (arch/arm64/boot/dts/freescale/imx8mq-tqma8mq- > > > mba8mx.dts) completly if this and the PHY driver are compiled in. I > > > have "pd_ignore_unused clk_ignore_unused" passed to kernel command > > > line, so I have no idea what's wrong here. > > > > Here is the first time in the driver to access hdmi register when > > driver probe. > For imx8mq hdmi/dp, mdhp hdmi apb clock and core clock are managed > > by ROM code, they are always on when device bootup. Could you dump the > > clock tree without "pd_ignore_unused clk_ignore_unused" ? > > I noticed too this is the 1st access, so I have no idea what's wrong here. > Here is my /sys/kernel/debug/clk/clk_summary from using the regular DT > without enabling 'dcss', 'hdmi_phy' and 'mhdp_hdmi': > > enable prepare protect duty > hardware > clock count count count > rate > accuracy phase cycle enable > --------------------------------------------------------------------------------------------- > ---------- > sys2_pll_out 7 7 0 > 1000000000 > 0 0 50000 Y > sys_pll2_out_monitor 0 0 0 > 1000000000 > 0 0 50000 Y > sys2_pll_1000m 0 0 0 > 1000000000 > 0 0 50000 Y > sys2_pll_500m 1 2 0 > 500000000 > 0 0 50000 Y > audio_ahb 0 1 0 > 500000000 > 0 0 50000 N > ipg_audio_root 0 1 0 > 250000000 > 0 0 50000 Y > sdma2_clk 0 2 0 > 250000000 > 0 0 50000 N > sai6_ipg_clk 0 0 0 > 250000000 > 0 0 50000 N > sai5_ipg_clk 0 0 0 > 250000000 > 0 0 50000 N > sai4_ipg_clk 0 0 0 > 250000000 > 0 0 50000 N > sai1_ipg_clk 0 0 0 > 250000000 > 0 0 50000 N > nand 0 0 0 > 500000000 > 0 0 50000 N > nand_root_clk 0 0 0 > 500000000 > 0 0 50000 N > usb_bus 2 2 0 > 500000000 > 0 0 50000 Y > usb2_ctrl_root_clk 1 1 0 > 500000000 > 0 0 50000 Y > usb1_ctrl_root_clk 1 1 0 > 500000000 > 0 0 50000 Y > sys2_pll_333m 1 1 0 > 333333333 > 0 0 50000 Y > main_axi 1 1 0 > 333333333 > 0 0 50000 Y > sys2_pll_250m 2 2 0 > 250000000 > 0 0 50000 Y > pcie1_ctrl 1 1 0 > 250000000 > 0 0 50000 Y > pcie1_root_clk 1 1 0 > 250000000 > 0 0 50000 Y > pcie2_ctrl 1 1 0 > 250000000 > 0 0 50000 Y > pcie2_root_clk 1 1 0 > 250000000 > 0 0 50000 Y > sys2_pll_200m 3 3 0 > 200000000 > 0 0 50000 Y > ecspi3 0 0 0 > 200000000 > 0 0 50000 N > ecspi3_root_clk 0 0 0 > 200000000 > 0 0 50000 N > ecspi2 1 1 0 > 200000000 > 0 0 50000 Y > ecspi2_root_clk 2 2 0 > 200000000 > 0 0 50000 Y > ecspi1 1 1 0 > 200000000 > 0 0 50000 Y > ecspi1_root_clk 2 2 0 > 200000000 > 0 0 50000 Y > gic 1 1 0 > 200000000 > 0 0 50000 Y > arm_m4_core 0 0 0 > 200000000 > 0 0 50000 N > sys2_pll_166m 0 0 0 > 166666666 > 0 0 50000 Y > sys2_pll_125m 1 1 0 > 125000000 > 0 0 50000 Y > enet_ref 1 1 0 > 125000000 > 0 0 50000 Y > sys2_pll_100m 3 3 0 > 100000000 > 0 0 50000 Y > pcie1_phy 1 1 0 > 100000000 > 0 0 50000 Y > pcie2_phy 1 1 0 > 100000000 > 0 0 50000 Y > enet_timer 1 1 0 > 100000000 > 0 0 50000 Y > sys2_pll_50m 1 1 0 > 50000000 > 0 0 50000 Y > enet_phy 1 1 0 > 50000000 > 0 0 50000 Y > sys1_pll_out 5 5 0 > 800000000 > 0 0 50000 Y > sys_pll1_out_monitor 0 0 0 > 800000000 > 0 0 50000 Y > sys1_pll_800m 2 2 0 > 800000000 > 0 0 50000 Y > vpu_bus 0 0 0 > 800000000 > 0 0 50000 N > vpu_dec_root_clk 0 0 0 > 800000000 > 0 0 50000 N > arm_a53_div 0 0 0 > 800000000 > 0 0 50000 N > dram_apb 1 1 0 > 160000000 > 0 0 50000 Y > noc 1 1 0 > 800000000 > 0 0 50000 Y > disp_rtrm 0 0 0 > 400000000 > 0 0 50000 N > disp_rtrm_root_clk 0 0 0 > 400000000 > 0 0 50000 N > disp_apb 0 0 0 > 133333334 > 0 0 50000 N > disp_apb_root_clk 0 0 0 > 133333334 > 0 0 50000 N > disp_axi 0 0 0 > 800000000 > 0 0 50000 N > disp_axi_root_clk 0 0 0 > 800000000 > 0 0 50000 N > sys1_pll_400m 0 0 0 > 400000000 > 0 0 50000 Y > usdhc2 0 0 0 > 400000000 > 0 0 50000 N > usdhc2_root_clk 0 0 0 > 400000000 > 0 0 50000 N > usdhc1 0 0 0 > 400000000 > 0 0 50000 N > usdhc1_root_clk 0 0 0 > 400000000 > 0 0 50000 N > sys1_pll_266m 1 1 0 > 266666666 > 0 0 50000 Y > nand_usdhc_bus 0 0 0 > 266666666 > 0 0 50000 N > nand_usdhc_rawnand_clk 0 0 0 > 266666666 > 0 0 50000 N > enet_axi 1 1 0 > 266666666 > 0 0 50000 Y > enet1_root_clk 2 2 0 > 266666666 > 0 0 50000 Y > sys1_pll_200m 0 0 0 > 200000000 > 0 0 50000 Y > sys1_pll_160m 0 0 0 > 160000000 > 0 0 50000 Y > sys1_pll_133m 1 1 0 > 133333333 > 0 0 50000 Y > ahb 9 4 0 > 133333333 > 0 0 50000 Y > ipg_root 8 8 0 > 66666667 > 0 0 50000 Y > sdma1_clk 6 1 0 > 66666667 > 0 0 50000 Y > tmu_root_clk 1 1 0 > 66666667 > 0 0 50000 Y > sai3_ipg_clk 0 0 0 > 66666667 > 0 0 50000 N > sai2_ipg_clk 0 0 0 > 66666667 > 0 0 50000 N > ocotp_root_clk 0 0 0 > 66666667 > 0 0 50000 N > mu_root_clk 0 0 0 > 66666667 > 0 0 50000 N > gpio5_root_clk 1 1 0 > 66666667 > 0 0 50000 Y > gpio4_root_clk 1 1 0 > 66666667 > 0 0 50000 Y > gpio3_root_clk 1 1 0 > 66666667 > 0 0 50000 Y > gpio2_root_clk 1 1 0 > 66666667 > 0 0 50000 Y > gpio1_root_clk 1 1 0 > 66666667 > 0 0 50000 Y > sys1_pll_100m 2 2 0 > 100000000 > 0 0 50000 Y > usb_phy_ref 2 2 0 > 100000000 > 0 0 50000 Y > usb2_phy_root_clk 1 1 0 > 100000000 > 0 0 50000 Y > usb1_phy_root_clk 1 1 0 > 100000000 > 0 0 50000 Y > usb_core_ref 2 2 0 > 100000000 > 0 0 50000 Y > qspi 0 0 0 > 100000000 > 0 0 50000 N > qspi_root_clk 0 0 0 > 100000000 > 0 0 50000 N > dram_alt 0 0 0 > 100000000 > 0 0 50000 N > dram_alt_root 0 0 0 > 25000000 > 0 0 50000 Y > sys1_pll_80m 2 2 0 > 80000000 > 0 0 50000 Y > pcie1_aux 1 1 0 > 10000000 > 0 0 50000 Y > pcie2_aux 1 1 0 > 10000000 > 0 0 50000 Y > uart2 0 0 0 > 80000000 > 0 0 50000 N > uart2_root_clk 0 0 0 > 80000000 > 0 0 50000 N > uart1 0 0 0 > 80000000 > 0 0 50000 N > uart1_root_clk 0 0 0 > 80000000 > 0 0 50000 N > sys1_pll_40m 0 0 0 > 40000000 > 0 0 50000 Y > wrclk 0 0 0 > 40000000 > 0 0 50000 N > dummy 0 0 0 > 0 > 0 0 50000 Y > clk-xtal25 2 2 0 > 25000000 > 0 0 50000 Y > DIF3 0 0 0 > 100000000 > 0 0 50000 Y > DIF2 1 1 0 > 100000000 > 0 0 50000 Y > DIF1 0 0 0 > 100000000 > 0 0 50000 Y > DIF0 1 1 0 > 100000000 > 0 0 50000 Y > clock 0 0 0 > 32768 > 0 0 50000 Y > clk_ext4 0 0 0 > 133000000 > 0 0 50000 Y > clk_ext3 0 0 0 > 133000000 > 0 0 50000 Y > clk_ext2 0 0 0 > 133000000 > 0 0 50000 Y > clk_ext1 0 0 0 > 133000000 > 0 0 50000 Y > hdmi_phy_27m 0 0 0 > 27000000 > 0 0 50000 Y > osc_27m 0 0 0 > 27000000 > 0 0 50000 Y > osc_25m 7 11 0 > 25000000 > 0 0 50000 Y > gpt_3m 0 0 0 > 3125000 > 0 0 50000 Y > csi2_esc 0 0 0 > 25000000 > 0 0 50000 N > csi2_phy_ref 0 0 0 > 25000000 > 0 0 50000 N > csi2_core 0 0 0 > 25000000 > 0 0 50000 N > csi2_root_clk 0 0 0 > 25000000 > 0 0 50000 N > csi1_esc 0 0 0 > 25000000 > 0 0 50000 N > csi1_phy_ref 0 0 0 > 25000000 > 0 0 50000 N > csi1_core 0 0 0 > 25000000 > 0 0 50000 N > csi1_root_clk 0 0 0 > 25000000 > 0 0 50000 N > dsi_ahb 0 0 0 > 25000000 > 0 0 50000 N > dsi_ipg_div 0 0 0 > 12500000 > 0 0 50000 Y > dsi_esc 0 0 0 > 25000000 > 0 0 50000 N > dsi_dbi 0 0 0 > 25000000 > 0 0 50000 N > dsi_phy_ref 0 0 0 > 25000000 > 0 0 50000 N > dsi_core 0 0 0 > 25000000 > 0 0 50000 N > clko2 0 0 0 > 25000000 > 0 0 50000 N > clko1 0 0 0 > 25000000 > 0 0 50000 N > wdog 1 1 0 > 25000000 > 0 0 50000 Y > wdog3_root_clk 0 0 0 > 25000000 > 0 0 50000 N > wdog2_root_clk 0 0 0 > 25000000 > 0 0 50000 N > wdog1_root_clk 1 1 0 > 25000000 > 0 0 50000 Y > gpt1 0 0 0 > 25000000 > 0 0 50000 N > gpt1_root_clk 0 0 0 > 25000000 > 0 0 50000 N > pwm4 0 0 0 > 25000000 > 0 0 50000 N > pwm4_root_clk 0 0 0 > 25000000 > 0 0 50000 N > pwm3 0 0 0 > 25000000 > 0 0 50000 N > pwm3_root_clk 0 0 0 > 25000000 > 0 0 50000 N > pwm2 0 0 0 > 25000000 > 0 0 50000 N > pwm2_root_clk 0 0 0 > 25000000 > 0 0 50000 N > pwm1 0 0 0 > 25000000 > 0 0 50000 N > pwm1_root_clk 0 0 0 > 25000000 > 0 0 50000 N > uart4 0 0 0 > 25000000 > 0 0 50000 N > uart4_root_clk 0 0 0 > 25000000 > 0 0 50000 N > uart3 1 1 0 > 25000000 > 0 0 50000 Y > uart3_root_clk 4 4 0 > 25000000 > 0 0 50000 Y > i2c4 0 0 0 > 25000000 > 0 0 50000 N > i2c4_root_clk 0 0 0 > 25000000 > 0 0 50000 N > i2c3 0 1 0 > 25000000 > 0 0 50000 N > i2c3_root_clk 0 1 0 > 25000000 > 0 0 50000 N > i2c2 0 1 0 > 25000000 > 0 0 50000 N > i2c2_root_clk 0 1 0 > 25000000 > 0 0 50000 N > i2c1 0 1 0 > 25000000 > 0 0 50000 N > i2c1_root_clk 0 1 0 > 25000000 > 0 0 50000 N > spdif2 0 0 0 > 25000000 > 0 0 50000 N > spdif1 0 0 0 > 25000000 > 0 0 50000 N > sai6 0 0 0 > 25000000 > 0 0 50000 N > sai6_root_clk 0 0 0 > 25000000 > 0 0 50000 N > sai5 0 0 0 > 25000000 > 0 0 50000 N > sai5_root_clk 0 0 0 > 25000000 > 0 0 50000 N > sai4 0 0 0 > 25000000 > 0 0 50000 N > sai4_root_clk 0 0 0 > 25000000 > 0 0 50000 N > sai2 0 0 0 > 25000000 > 0 0 50000 N > sai2_root_clk 0 0 0 > 25000000 > 0 0 50000 N > sai1 0 0 0 > 25000000 > 0 0 50000 N > sai1_root_clk 0 0 0 > 25000000 > 0 0 50000 N > lcdif_pixel 0 0 0 > 25000000 > 0 0 50000 N > disp_dc8000 0 0 0 > 25000000 > 0 0 50000 N > disp_root_clk 0 0 0 > 25000000 > 0 0 50000 N > disp_dtrc 0 0 0 > 25000000 > 0 0 50000 N > noc_apb 1 1 0 > 25000000 > 0 0 50000 Y > video2_pll1_ref_sel 0 0 0 > 25000000 > 0 0 50000 Y > video2_pll_out 0 0 0 > 25000000 > 0 0 50000 Y > video_pll2_out_monitor 0 0 0 > 25000000 > 0 0 50000 Y > dram_pll1_ref_sel 1 1 0 > 25000000 > 0 0 50000 Y > dram_pll_out 2 2 0 > 800000000 > 0 0 50000 Y > dram_core_clk 1 1 0 > 800000000 > 0 0 50000 Y > dram_pll_out_monitor 0 0 0 > 800000000 > 0 0 50000 Y > sys3_pll1_ref_sel 1 1 0 > 25000000 > 0 0 50000 Y > sys3_pll_out 1 1 0 > 25000000 > 0 0 50000 Y > sys_pll3_out_monitor 0 0 0 > 25000000 > 0 0 50000 Y > video_pll1_ref_sel 0 0 0 > 25000000 > 0 0 50000 Y > video_pll1_bypass 0 0 0 > 25000000 > 0 0 50000 Y > video_pll1_out_monitor 0 0 0 > 25000000 > 0 0 50000 Y > video_pll1_out 0 0 0 > 25000000 > 0 0 50000 N > dc_pixel 0 0 0 > 5000000 > 0 0 50000 N > video_pll1_ref_div 0 0 0 > 5000000 > 0 0 50000 Y > video_pll1 0 0 0 > 650000000 > 0 0 50000 Y > audio_pll2_ref_sel 0 0 0 > 25000000 > 0 0 50000 Y > audio_pll2_ref_div 0 0 0 > 5000000 > 0 0 50000 Y > audio_pll2 0 0 0 > 722534397 > 0 0 50000 Y > audio_pll2_bypass 0 0 0 > 722534397 > 0 0 50000 Y > audio_pll2_out_monitor 0 0 0 > 722534397 > 0 0 50000 Y > audio_pll2_out 0 0 0 > 722534397 > 0 0 50000 N > audio_pll1_ref_sel 0 0 0 > 25000000 > 0 0 50000 Y > audio_pll1_ref_div 0 0 0 > 5000000 > 0 0 50000 Y > audio_pll1 0 0 0 > 786431998 > 0 0 50000 Y > audio_pll1_bypass 0 0 0 > 786431998 > 0 0 50000 Y > audio_pll1_out_monitor 0 0 0 > 786431998 > 0 0 50000 Y > audio_pll1_out 0 0 0 > 786431998 > 0 0 50000 N > sai3 0 0 0 > 49152000 > 0 0 50000 N > sai3_root_clk 0 0 0 > 49152000 > 0 0 50000 N > pll 0 0 0 > 196608000 > 0 0 50000 Y > codec_clkin 0 0 > 0 196608000 > 0 0 50000 Y > nadc 0 0 0 > 196608000 > 0 0 50000 Y > madc 0 0 > 0 196608000 > 0 0 50000 Y > ndac 0 0 0 > 196608000 > 0 0 50000 Y > mdac 0 0 > 0 196608000 > 0 0 50000 Y > bdiv 0 0 > 0 > 196608000 0 0 50000 Y > vpu_pll_ref_sel 0 1 0 > 25000000 > 0 0 50000 Y > vpu_pll_ref_div 0 1 0 > 5000000 > 0 0 50000 Y > vpu_pll 0 1 0 > 600000000 > 0 0 50000 Y > vpu_pll_bypass 0 1 0 > 600000000 > 0 0 50000 Y > vpu_pll_out_monitor 0 0 0 > 600000000 > 0 0 50000 Y > vpu_pll_out 0 2 0 > 600000000 > 0 0 50000 N > vpu_g2 0 1 0 > 600000000 > 0 0 50000 N > vpu_g2_root_clk 0 1 0 > 600000000 > 0 0 50000 N > vpu_g1 0 1 0 > 600000000 > 0 0 50000 N > vpu_g1_root_clk 0 1 0 > 600000000 > 0 0 50000 N > gpu_pll_ref_sel 0 0 0 > 25000000 > 0 0 50000 Y > gpu_pll_ref_div 0 0 0 > 5000000 > 0 0 50000 Y > gpu_pll 0 0 0 > 800000000 > 0 0 50000 Y > gpu_pll_bypass 0 0 0 > 800000000 > 0 0 50000 Y > gpu_pll_out_monitor 0 0 0 > 800000000 > 0 0 50000 Y > pllout_monitor_sel 0 0 0 > 800000000 > 0 0 50000 Y > pllout_monitor_clk2 0 0 > 0 > 800000000 0 0 50000 N > gpu_pll_out 0 0 0 > 800000000 > 0 0 50000 N > gpu_ahb 0 0 0 > 800000000 > 0 0 50000 N > gpu_axi 0 0 0 > 800000000 > 0 0 50000 N > gpu_shader 0 0 0 > 800000000 > 0 0 50000 N > gpu_core 0 0 0 > 800000000 > 0 0 50000 N > gpu_root_clk 0 0 0 > 800000000 > 0 0 50000 N > arm_pll_ref_sel 1 1 0 > 25000000 > 0 0 50000 Y > arm_pll_ref_div 1 1 0 > 5000000 > 0 0 50000 Y > arm_pll 1 1 0 > 800000000 > 0 0 50000 Y > arm_pll_bypass 1 1 0 > 800000000 > 0 0 50000 Y > arm_pll_out_monitor 0 0 0 > 800000000 > 0 0 50000 Y > arm_pll_out 1 1 0 > 800000000 > 0 0 50000 Y > arm_a53_core 1 1 0 > 800000000 > 0 0 50000 Y > arm 1 1 0 > 800000000 > 0 0 50000 Y > vpu_core 0 0 0 > 800000000 > 0 0 50000 N > ckil 2 2 0 > 32768 > 0 0 50000 Y > I will try to duplicate the same clocks status as your on imx8mq evk. Any result will let you know in different mail. Thanks Sandor > Thanks and best regards > Alexander > >