Thanks for your comments. > -----Original Message----- > From: Alexander Stein <alexander.stein@xxxxxxxxxxxxxxx> > Sent: 2022年11月8日 21:17 > To: jonas@xxxxxxxxx; Sandor Yu <sandor.yu@xxxxxxx> > Cc: dri-devel@xxxxxxxxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; > linux-arm-kernel@xxxxxxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx; > linux-phy@xxxxxxxxxxxxxxxxxxx; andrzej.hajda@xxxxxxxxx; > neil.armstrong@xxxxxxxxxx; robert.foss@xxxxxxxxxx; > Laurent.pinchart@xxxxxxxxxxxxxxxx; jernej.skrabec@xxxxxxxxx; > kishon@xxxxxx; vkoul@xxxxxxxxxx; Oliver Brown <oliver.brown@xxxxxxx>; > krzysztof.kozlowski+dt@xxxxxxxxxx; sam@xxxxxxxxxxxx; jani.nikula@xxxxxxxxx; > tzimmermann@xxxxxxx; s.hauer@xxxxxxxxxxxxxx; javierm@xxxxxxxxxx; > penguin-kernel@xxxxxxxxxxxxxxxxxxx; robh+dt@xxxxxxxxxx; dl-linux-imx > <linux-imx@xxxxxxx>; kernel@xxxxxxxxxxxxxx; Sandor Yu > <sandor.yu@xxxxxxx>; shawnguo@xxxxxxxxxx; p.yadav@xxxxxx; > maxime@xxxxxxxxxx > 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@xxxxxxx> > > --- > > 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@xxxxxxx>"); > > +MODULE_DESCRIPTION("Cadence HDMI transmitter driver"); > > +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:cdns-hdmi"); > > >