[EXT] Re: [v2 06/10] drm: bridge: cadence: Add MHDP HDMI driver for i.MX8MQ
Sandor Yu
sandor.yu at nxp.com
Wed Nov 9 05:26:14 PST 2022
Thanks for your comments.
> -----Original Message-----
> From: Alexander Stein <alexander.stein at ew.tq-group.com>
> Sent: 2022年11月8日 21:17
> To: jonas at kwiboo.se; Sandor Yu <sandor.yu at nxp.com>
> Cc: dri-devel at lists.freedesktop.org; devicetree at vger.kernel.org;
> linux-arm-kernel at lists.infradead.org; linux-kernel at vger.kernel.org;
> linux-phy at lists.infradead.org; andrzej.hajda at intel.com;
> neil.armstrong at linaro.org; robert.foss at linaro.org;
> Laurent.pinchart at ideasonboard.com; jernej.skrabec at gmail.com;
> kishon at ti.com; vkoul at kernel.org; Oliver Brown <oliver.brown at nxp.com>;
> krzysztof.kozlowski+dt at linaro.org; sam at ravnborg.org; jani.nikula at intel.com;
> tzimmermann at suse.de; s.hauer at pengutronix.de; javierm at redhat.com;
> penguin-kernel at i-love.sakura.ne.jp; robh+dt at kernel.org; dl-linux-imx
> <linux-imx at nxp.com>; kernel at pengutronix.de; Sandor Yu
> <sandor.yu at nxp.com>; shawnguo at kernel.org; p.yadav at ti.com;
> maxime at 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 at 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 at nxp.com>");
> > +MODULE_DESCRIPTION("Cadence HDMI transmitter driver");
> > +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:cdns-hdmi");
>
>
>
More information about the linux-phy
mailing list