Message ID | cover.1697524277.git.Sandor.yu@nxp.com |
---|---|
Headers | show |
Series | Initial support Cadence MHDP8501(HDMI/DP) for i.MX8MQ | expand |
Hi Sandor, thanks for the update. One small typo in the commit message: 'Creat' -> 'Create' Am Dienstag, 17. Oktober 2023, 09:03:57 CEST schrieb Sandor Yu: > MHDP8546 mailbox access functions will be share to other mhdp driver > and Cadence HDP-TX HDMI/DP PHY drivers. > Create a new mhdp helper driver and move all those functions into. > > cdns_mhdp_reg_write() is renamed to cdns_mhdp_dp_reg_write(), > because it use the DPTX command ID DPTX_WRITE_REGISTER. > > New cdns_mhdp_reg_write() is created with the general command ID > GENERAL_REGISTER_WRITE. > > rewrite cdns_mhdp_set_firmware_active() in mhdp8546 core driver, > use cdns_mhdp_mailbox_send() to replace cdns_mhdp_mailbox_write() > same as the other mailbox access functions. > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > --- > v10->v11: > - rewrite cdns_mhdp_set_firmware_active() in mhdp8546 core driver, > use cdns_mhdp_mailbox_send() to replace cdns_mhdp_mailbox_write() > same as the other mailbox access functions. > - use static for cdns_mhdp_mailbox_write() and cdns_mhdp_mailbox_read() > and remove them from EXPORT_SYMBOL_GPL(). > > v9->v10: > *use mhdp helper driver to replace macro functions, > move maibox access function and mhdp hdmi/dp common API > functions into the driver. > > drivers/gpu/drm/bridge/cadence/Kconfig | 4 + > drivers/gpu/drm/bridge/cadence/Makefile | 1 + > .../gpu/drm/bridge/cadence/cdns-mhdp-helper.c | 304 +++++++++++++ > .../drm/bridge/cadence/cdns-mhdp8546-core.c | 403 +++--------------- > .../drm/bridge/cadence/cdns-mhdp8546-core.h | 44 +- > include/drm/bridge/cdns-mhdp-helper.h | 94 ++++ > 6 files changed, 476 insertions(+), 374 deletions(-) > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c > create mode 100644 include/drm/bridge/cdns-mhdp-helper.h > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > b/drivers/gpu/drm/bridge/cadence/Kconfig index ec35215a20034..0b7b4626a7af0 > 100644 > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig > @@ -20,6 +20,9 @@ config DRM_CDNS_DSI_J721E > the routing of the DSS DPI signal to the Cadence DSI. > endif > > +config CDNS_MHDP_HELPER > + tristate > + > config DRM_CDNS_MHDP8546 > tristate "Cadence DPI/DP bridge" > select DRM_DISPLAY_DP_HELPER > @@ -27,6 +30,7 @@ config DRM_CDNS_MHDP8546 > select DRM_DISPLAY_HELPER > select DRM_KMS_HELPER > select DRM_PANEL_BRIDGE > + select CDNS_MHDP_HELPER > depends on OF > help > Support Cadence DPI to DP bridge. This is an internal > diff --git a/drivers/gpu/drm/bridge/cadence/Makefile > b/drivers/gpu/drm/bridge/cadence/Makefile index > c95fd5b81d137..087dc074820d7 100644 > --- a/drivers/gpu/drm/bridge/cadence/Makefile > +++ b/drivers/gpu/drm/bridge/cadence/Makefile > @@ -2,6 +2,7 @@ > obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o > cdns-dsi-y := cdns-dsi-core.o > cdns-dsi-$(CONFIG_DRM_CDNS_DSI_J721E) += cdns-dsi-j721e.o > +obj-$(CONFIG_CDNS_MHDP_HELPER) += cdns-mhdp-helper.o > obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o > cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o > cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c new file mode 100644 > index 0000000000000..8cabd79ad9663 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c > @@ -0,0 +1,304 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2023 NXP Semiconductor, Inc. > + * > + */ > +#include <drm/bridge/cdns-mhdp-helper.h> > +#include <linux/dev_printk.h> > +#include <linux/module.h> > + > +/* Mailbox helper functions */ > +static int cdns_mhdp_mailbox_read(struct cdns_mhdp_base *base) > +{ > + int ret, empty; > + > + WARN_ON(!mutex_is_locked(base->mbox_mutex)); Actually this should be moved to cdns_mhdp_mailbox_recv_header() and cdns_mhdp_mailbox_recv_data(). > + ret = readx_poll_timeout(readl, base->regs + CDNS_MAILBOX_EMPTY, > + empty, !empty, MAILBOX_RETRY_US, > + MAILBOX_TIMEOUT_US); > + if (ret < 0) > + return ret; > + > + return readl(base->regs + CDNS_MAILBOX_RX_DATA) & 0xff; > +} > + > +static int cdns_mhdp_mailbox_write(struct cdns_mhdp_base *base, u8 val) > +{ > + int ret, full; > + > + WARN_ON(!mutex_is_locked(base->mbox_mutex)); I think this should be moved to cdns_mhdp_mailbox_send() as well. > + ret = readx_poll_timeout(readl, base->regs + CDNS_MAILBOX_FULL, > + full, !full, MAILBOX_RETRY_US, > + MAILBOX_TIMEOUT_US); > + if (ret < 0) > + return ret; > + > + writel(val, base->regs + CDNS_MAILBOX_TX_DATA); > + > + return 0; > +} Nice, much better having these as static now. > +int cdns_mhdp_mailbox_recv_header(struct cdns_mhdp_base *base, > + u8 module_id, u8 opcode, > + u16 req_size) > +{ > + u32 mbox_size, i; > + u8 header[4]; > + int ret; > + > + /* read the header of the message */ > + for (i = 0; i < sizeof(header); i++) { > + ret = cdns_mhdp_mailbox_read(base); > + if (ret < 0) > + return ret; > + > + header[i] = ret; > + } > + > + mbox_size = get_unaligned_be16(header + 2); > + > + if (opcode != header[0] || module_id != header[1] || > + req_size != mbox_size) { > + /* > + * If the message in mailbox is not what we want, we need to > + * clear the mailbox by reading its contents. > + */ > + for (i = 0; i < mbox_size; i++) > + if (cdns_mhdp_mailbox_read(base) < 0) > + break; > + > + return -EINVAL; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_recv_header); > + > +int cdns_mhdp_mailbox_recv_data(struct cdns_mhdp_base *base, > + u8 *buff, u16 buff_size) > +{ > + u32 i; > + int ret; > + > + for (i = 0; i < buff_size; i++) { > + ret = cdns_mhdp_mailbox_read(base); > + if (ret < 0) > + return ret; > + > + buff[i] = ret; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_recv_data); > + > +int cdns_mhdp_mailbox_send(struct cdns_mhdp_base *base, u8 module_id, > + u8 opcode, u16 size, u8 *message) > +{ > + u8 header[4]; > + int ret, i; > + > + header[0] = opcode; > + header[1] = module_id; > + put_unaligned_be16(size, header + 2); > + > + for (i = 0; i < sizeof(header); i++) { > + ret = cdns_mhdp_mailbox_write(base, header[i]); > + if (ret) > + return ret; > + } > + > + for (i = 0; i < size; i++) { > + ret = cdns_mhdp_mailbox_write(base, message[i]); > + if (ret) > + return ret; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_send); > + > +/* General helper functions */ > +int cdns_mhdp_reg_read(struct cdns_mhdp_base *base, u32 addr, u32 *value) > +{ > + u8 msg[4], resp[8]; > + int ret; > + > + put_unaligned_be32(addr, msg); > + > + mutex_lock(base->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_GENERAL, > + GENERAL_REGISTER_READ, > + sizeof(msg), msg); > + if (ret) > + goto out; > + > + ret = cdns_mhdp_mailbox_recv_header(base, MB_MODULE_ID_GENERAL, > + GENERAL_REGISTER_READ, > + sizeof(resp)); > + if (ret) > + goto out; > + > + ret = cdns_mhdp_mailbox_recv_data(base, resp, sizeof(resp)); > + if (ret) > + goto out; > + > + /* Returned address value should be the same as requested */ > + if (memcmp(msg, resp, sizeof(msg))) { > + ret = -EINVAL; > + goto out; > + } > + > + *value = get_unaligned_be32(resp + 4); > + > +out: > + mutex_unlock(base->mbox_mutex); > + if (ret) { > + dev_err(base->dev, "Failed to read register\n"); > + *value = 0; > + } > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_reg_read); > + > +int cdns_mhdp_reg_write(struct cdns_mhdp_base *base, u32 addr, u32 val) > +{ > + u8 msg[8]; > + int ret; > + > + put_unaligned_be32(addr, msg); > + put_unaligned_be32(val, msg + 4); > + > + mutex_lock(base->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_GENERAL, > + GENERAL_REGISTER_WRITE, > + sizeof(msg), msg); > + > + mutex_unlock(base->mbox_mutex); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_reg_write); > + > +/* DPTX helper functions */ > +int cdns_mhdp_dp_reg_write(struct cdns_mhdp_base *base, u16 addr, u32 val) > +{ > + u8 msg[6]; > + int ret; > + > + put_unaligned_be16(addr, msg); > + put_unaligned_be32(val, msg + 2); > + > + mutex_lock(base->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > + DPTX_WRITE_REGISTER, sizeof(msg), msg); > + > + mutex_unlock(base->mbox_mutex); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_dp_reg_write); > + > +int cdns_mhdp_dp_reg_write_bit(struct cdns_mhdp_base *base, u16 addr, > + u8 start_bit, u8 bits_no, u32 val) > +{ > + u8 field[8]; > + int ret; > + > + put_unaligned_be16(addr, field); > + field[2] = start_bit; > + field[3] = bits_no; > + put_unaligned_be32(val, field + 4); > + > + mutex_lock(base->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > + DPTX_WRITE_FIELD, sizeof(field), field); > + > + mutex_unlock(base->mbox_mutex); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_dp_reg_write_bit); > + > +int cdns_mhdp_dpcd_read(struct cdns_mhdp_base *base, > + u32 addr, u8 *data, u16 len) > +{ > + u8 msg[5], reg[5]; > + int ret; > + > + put_unaligned_be16(len, msg); > + put_unaligned_be24(addr, msg + 2); > + > + mutex_lock(base->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > + DPTX_READ_DPCD, sizeof(msg), msg); > + if (ret) > + goto out; > + > + ret = cdns_mhdp_mailbox_recv_header(base, MB_MODULE_ID_DP_TX, > + DPTX_READ_DPCD, > + sizeof(reg) + len); > + if (ret) > + goto out; > + > + ret = cdns_mhdp_mailbox_recv_data(base, reg, sizeof(reg)); > + if (ret) > + goto out; > + > + ret = cdns_mhdp_mailbox_recv_data(base, data, len); > + > +out: > + mutex_unlock(base->mbox_mutex); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_dpcd_read); > + > +int cdns_mhdp_dpcd_write(struct cdns_mhdp_base *base, u32 addr, u8 value) > +{ > + u8 msg[6], reg[5]; > + int ret; > + > + put_unaligned_be16(1, msg); > + put_unaligned_be24(addr, msg + 2); > + msg[5] = value; > + > + mutex_lock(base->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > + DPTX_WRITE_DPCD, sizeof(msg), msg); > + if (ret) > + goto out; > + > + ret = cdns_mhdp_mailbox_recv_header(base, MB_MODULE_ID_DP_TX, > + DPTX_WRITE_DPCD, sizeof(reg)); > + if (ret) > + goto out; > + > + ret = cdns_mhdp_mailbox_recv_data(base, reg, sizeof(reg)); > + if (ret) > + goto out; > + > + if (addr != get_unaligned_be24(reg + 2)) > + ret = -EINVAL; No need to have the mutex locked while doing this check. This should be moved below 'out' label. > +out: > + mutex_unlock(base->mbox_mutex); > + > + if (ret) > + dev_err(base->dev, "dpcd write failed: %d\n", ret); > + return ret; > +} > +EXPORT_SYMBOL_GPL(cdns_mhdp_dpcd_write); > + > +MODULE_DESCRIPTION("Cadence MHDP Helper driver"); > +MODULE_AUTHOR("Sandor Yu <Sandor.yu@nxp.com>"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index > 6af565ac307ae..9d9dbb976868c 100644 > --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > @@ -73,298 +73,28 @@ static void cdns_mhdp_bridge_hpd_disable(struct > drm_bridge *bridge) mhdp->regs + CDNS_APB_INT_MASK); > } > > -static int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) > -{ > - int ret, empty; > - > - WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); > - > - ret = readx_poll_timeout(readl, mhdp->regs + CDNS_MAILBOX_EMPTY, > - empty, !empty, MAILBOX_RETRY_US, > - MAILBOX_TIMEOUT_US); > - if (ret < 0) > - return ret; > - > - return readl(mhdp->regs + CDNS_MAILBOX_RX_DATA) & 0xff; > -} > - > -static int cdns_mhdp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val) > -{ > - int ret, full; > - > - WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); > - > - ret = readx_poll_timeout(readl, mhdp->regs + CDNS_MAILBOX_FULL, > - full, !full, MAILBOX_RETRY_US, > - MAILBOX_TIMEOUT_US); > - if (ret < 0) > - return ret; > - > - writel(val, mhdp->regs + CDNS_MAILBOX_TX_DATA); > - > - return 0; > -} > - > -static int cdns_mhdp_mailbox_recv_header(struct cdns_mhdp_device *mhdp, > - u8 module_id, u8 opcode, > - u16 req_size) > -{ > - u32 mbox_size, i; > - u8 header[4]; > - int ret; > - > - /* read the header of the message */ > - for (i = 0; i < sizeof(header); i++) { > - ret = cdns_mhdp_mailbox_read(mhdp); > - if (ret < 0) > - return ret; > - > - header[i] = ret; > - } > - > - mbox_size = get_unaligned_be16(header + 2); > - > - if (opcode != header[0] || module_id != header[1] || > - req_size != mbox_size) { > - /* > - * If the message in mailbox is not what we want, we need to > - * clear the mailbox by reading its contents. > - */ > - for (i = 0; i < mbox_size; i++) > - if (cdns_mhdp_mailbox_read(mhdp) < 0) > - break; > - > - return -EINVAL; > - } > - > - return 0; > -} > - > -static int cdns_mhdp_mailbox_recv_data(struct cdns_mhdp_device *mhdp, > - u8 *buff, u16 buff_size) > -{ > - u32 i; > - int ret; > - > - for (i = 0; i < buff_size; i++) { > - ret = cdns_mhdp_mailbox_read(mhdp); > - if (ret < 0) > - return ret; > - > - buff[i] = ret; > - } > - > - return 0; > -} > - > -static int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 > module_id, - u8 opcode, u16 size, u8 *message) > -{ > - u8 header[4]; > - int ret, i; > - > - header[0] = opcode; > - header[1] = module_id; > - put_unaligned_be16(size, header + 2); > - > - for (i = 0; i < sizeof(header); i++) { > - ret = cdns_mhdp_mailbox_write(mhdp, header[i]); > - if (ret) > - return ret; > - } > - > - for (i = 0; i < size; i++) { > - ret = cdns_mhdp_mailbox_write(mhdp, message[i]); > - if (ret) > - return ret; > - } > - > - return 0; > -} > - > -static > -int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr, u32 *value) > -{ > - u8 msg[4], resp[8]; > - int ret; > - > - put_unaligned_be32(addr, msg); > - > - mutex_lock(&mhdp->mbox_mutex); > - > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, > - GENERAL_REGISTER_READ, > - sizeof(msg), msg); > - if (ret) > - goto out; > - > - ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_GENERAL, > - GENERAL_REGISTER_READ, > - sizeof(resp)); > - if (ret) > - goto out; > - > - ret = cdns_mhdp_mailbox_recv_data(mhdp, resp, sizeof(resp)); > - if (ret) > - goto out; > - > - /* Returned address value should be the same as requested */ > - if (memcmp(msg, resp, sizeof(msg))) { > - ret = -EINVAL; > - goto out; > - } > - > - *value = get_unaligned_be32(resp + 4); > - > -out: > - mutex_unlock(&mhdp->mbox_mutex); > - if (ret) { > - dev_err(mhdp->dev, "Failed to read register\n"); > - *value = 0; > - } > - > - return ret; > -} > - > -static > -int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u16 addr, u32 val) > -{ > - u8 msg[6]; > - int ret; > - > - put_unaligned_be16(addr, msg); > - put_unaligned_be32(val, msg + 2); > - > - mutex_lock(&mhdp->mbox_mutex); > - > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > - DPTX_WRITE_REGISTER, sizeof(msg), msg); > - > - mutex_unlock(&mhdp->mbox_mutex); > - > - return ret; > -} > - > static > -int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, > - u8 start_bit, u8 bits_no, u32 val) > -{ > - u8 field[8]; > - int ret; > - > - put_unaligned_be16(addr, field); > - field[2] = start_bit; > - field[3] = bits_no; > - put_unaligned_be32(val, field + 4); > - > - mutex_lock(&mhdp->mbox_mutex); > - > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > - DPTX_WRITE_FIELD, sizeof(field), field); > - > - mutex_unlock(&mhdp->mbox_mutex); > - > - return ret; > -} > - > -static > -int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, > - u32 addr, u8 *data, u16 len) > -{ > - u8 msg[5], reg[5]; > - int ret; > - > - put_unaligned_be16(len, msg); > - put_unaligned_be24(addr, msg + 2); > - > - mutex_lock(&mhdp->mbox_mutex); > - > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > - DPTX_READ_DPCD, sizeof(msg), msg); > - if (ret) > - goto out; > - > - ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX, > - DPTX_READ_DPCD, > - sizeof(reg) + len); > - if (ret) > - goto out; > - > - ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > - if (ret) > - goto out; > - > - ret = cdns_mhdp_mailbox_recv_data(mhdp, data, len); > - > -out: > - mutex_unlock(&mhdp->mbox_mutex); > - > - return ret; > -} > - > -static > -int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value) > +int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool > enable) { > - u8 msg[6], reg[5]; > + u8 status; > int ret; > > - put_unaligned_be16(1, msg); > - put_unaligned_be24(addr, msg + 2); > - msg[5] = value; > - > mutex_lock(&mhdp->mbox_mutex); > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > - DPTX_WRITE_DPCD, sizeof(msg), msg); > - if (ret) > - goto out; > - > - ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX, > - DPTX_WRITE_DPCD, sizeof(reg)); > - if (ret) > - goto out; > + status = enable ? FW_ACTIVE : FW_STANDBY; Initialising status can be done outside of the locked mutex. > - ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_GENERAL, > + GENERAL_MAIN_CONTROL, sizeof(status), &status); > if (ret) > goto out; > > - if (addr != get_unaligned_be24(reg + 2)) > - ret = -EINVAL; > - > -out: > - mutex_unlock(&mhdp->mbox_mutex); > - > - if (ret) > - dev_err(mhdp->dev, "dpcd write failed: %d\n", ret); > - return ret; > -} > - > -static > -int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool > enable) -{ > - u8 msg[5]; > - int ret, i; > - > - msg[0] = GENERAL_MAIN_CONTROL; > - msg[1] = MB_MODULE_ID_GENERAL; > - msg[2] = 0; > - msg[3] = 1; > - msg[4] = enable ? FW_ACTIVE : FW_STANDBY; > - > - mutex_lock(&mhdp->mbox_mutex); > - > - for (i = 0; i < sizeof(msg); i++) { > - ret = cdns_mhdp_mailbox_write(mhdp, msg[i]); > - if (ret) > - goto out; > - } > - > - /* read the firmware state */ > - ret = cdns_mhdp_mailbox_recv_data(mhdp, msg, sizeof(msg)); > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_GENERAL, > + GENERAL_MAIN_CONTROL, > + sizeof(status)); > if (ret) > goto out; > > - ret = 0; > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &status, sizeof(status)); > > out: > mutex_unlock(&mhdp->mbox_mutex); > @@ -382,18 +112,18 @@ int cdns_mhdp_get_hpd_status(struct cdns_mhdp_device > *mhdp) > > mutex_lock(&mhdp->mbox_mutex); > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_HPD_STATE, 0, NULL); > if (ret) > goto err_get_hpd; > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_HPD_STATE, > sizeof(status)); > if (ret) > goto err_get_hpd; > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, &status, sizeof(status)); > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &status, sizeof(status)); > if (ret) > goto err_get_hpd; > > @@ -424,22 +154,22 @@ int cdns_mhdp_get_edid_block(void *data, u8 *edid, > msg[0] = block / 2; > msg[1] = block % 2; > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_GET_EDID, sizeof(msg), msg); > if (ret) > continue; > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_GET_EDID, > sizeof(reg) + length); > if (ret) > continue; > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, reg, sizeof(reg)); > if (ret) > continue; > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, edid, length); > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, edid, length); > if (ret) > continue; > > @@ -464,17 +194,17 @@ int cdns_mhdp_read_hpd_event(struct cdns_mhdp_device > *mhdp) > > mutex_lock(&mhdp->mbox_mutex); > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_READ_EVENT, 0, NULL); > if (ret) > goto out; > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_READ_EVENT, sizeof(event)); > if (ret < 0) > goto out; > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, &event, sizeof(event)); > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &event, sizeof(event)); > out: > mutex_unlock(&mhdp->mbox_mutex); > > @@ -512,20 +242,20 @@ int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp, > unsigned int nlanes, > > mutex_lock(&mhdp->mbox_mutex); > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_ADJUST_LT, > sizeof(payload), payload); > if (ret) > goto out; > > /* Yes, read the DPCD read command response */ > - ret = cdns_mhdp_mailbox_recv_header(mhdp, MB_MODULE_ID_DP_TX, > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_READ_DPCD, > sizeof(hdr) + DP_LINK_STATUS_SIZE); > if (ret) > goto out; > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, hdr, sizeof(hdr)); > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, hdr, sizeof(hdr)); > if (ret) > goto out; > > @@ -533,7 +263,7 @@ int cdns_mhdp_adjust_lt(struct cdns_mhdp_device *mhdp, > unsigned int nlanes, if (addr != DP_LANE0_1_STATUS) > goto out; > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, link_status, > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, link_status, > DP_LINK_STATUS_SIZE); > > out: > @@ -847,7 +577,7 @@ static ssize_t cdns_mhdp_transfer(struct drm_dp_aux > *aux, unsigned int i; > > for (i = 0; i < msg->size; ++i) { > - ret = cdns_mhdp_dpcd_write(mhdp, > + ret = cdns_mhdp_dpcd_write(&mhdp->base, > msg->address + i, buf[i]); > if (!ret) > continue; > @@ -859,7 +589,7 @@ static ssize_t cdns_mhdp_transfer(struct drm_dp_aux > *aux, return ret; > } > } else { > - ret = cdns_mhdp_dpcd_read(mhdp, msg->address, > + ret = cdns_mhdp_dpcd_read(&mhdp->base, msg->address, > msg->buffer, msg->size); > if (ret) { > dev_err(mhdp->dev, > @@ -887,12 +617,12 @@ static int cdns_mhdp_link_training_init(struct > cdns_mhdp_device *mhdp) if (!mhdp->host.scrambler) > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, reg32); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_ENHNCD, > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_ENHNCD, > mhdp->sink.enhanced & mhdp->host.enhanced); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_LANE_EN, > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_LANE_EN, > CDNS_DP_LANE_EN_LANES(mhdp- >link.num_lanes)); > > cdns_mhdp_link_configure(&mhdp->aux, &mhdp->link); > @@ -913,7 +643,7 @@ static int cdns_mhdp_link_training_init(struct > cdns_mhdp_device *mhdp) return ret; > } > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, > CDNS_PHY_COMMON_CONFIG | > CDNS_PHY_TRAINING_EN | > CDNS_PHY_TRAINING_TYPE(1) | > @@ -1058,7 +788,7 @@ static bool cdns_mhdp_link_training_channel_eq(struct > cdns_mhdp_device *mhdp, CDNS_PHY_TRAINING_TYPE(eq_tps); > if (eq_tps != 4) > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, reg32); > > drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET, > (eq_tps != 4) ? eq_tps | DP_LINK_SCRAMBLING_DISABLE : > @@ -1322,7 +1052,7 @@ static int cdns_mhdp_link_training(struct > cdns_mhdp_device *mhdp, mhdp->host.scrambler ? 0 : > DP_LINK_SCRAMBLING_DISABLE); > > - ret = cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, ®32); > + ret = cdns_mhdp_reg_read(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, > ®32); if (ret < 0) { > dev_err(mhdp->dev, > "Failed to read CDNS_DP_FRAMER_GLOBAL_CONFIG %d\n", > @@ -1333,13 +1063,13 @@ static int cdns_mhdp_link_training(struct > cdns_mhdp_device *mhdp, reg32 |= CDNS_DP_NUM_LANES(mhdp->link.num_lanes); > reg32 |= CDNS_DP_WR_FAILING_EDGE_VSYNC; > reg32 |= CDNS_DP_FRAMER_EN; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, reg32); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, reg32); > > /* Reset PHY config */ > reg32 = CDNS_PHY_COMMON_CONFIG | CDNS_PHY_TRAINING_TYPE(1); > if (!mhdp->host.scrambler) > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, reg32); > > return 0; > err: > @@ -1347,7 +1077,7 @@ static int cdns_mhdp_link_training(struct > cdns_mhdp_device *mhdp, reg32 = CDNS_PHY_COMMON_CONFIG | > CDNS_PHY_TRAINING_TYPE(1); > if (!mhdp->host.scrambler) > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, reg32); > > drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET, > DP_TRAINING_PATTERN_DISABLE); > @@ -1461,7 +1191,7 @@ static int cdns_mhdp_link_up(struct cdns_mhdp_device > *mhdp) mhdp->link.num_lanes = cdns_mhdp_max_num_lanes(mhdp); > > /* Disable framer for link training */ > - err = cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp); > + err = cdns_mhdp_reg_read(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, > &resp); if (err < 0) { > dev_err(mhdp->dev, > "Failed to read CDNS_DP_FRAMER_GLOBAL_CONFIG %d\n", > @@ -1470,7 +1200,7 @@ static int cdns_mhdp_link_up(struct cdns_mhdp_device > *mhdp) } > > resp &= ~CDNS_DP_FRAMER_EN; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, resp); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, resp); > > /* Spread AMP if required, enable 8b/10b coding */ > amp[0] = cdns_mhdp_get_ssc_supported(mhdp) ? DP_SPREAD_AMP_0_5 : 0; > @@ -1837,7 +1567,7 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, if (mode->flags & DRM_MODE_FLAG_INTERLACE) > bnd_hsync2vsync |= CDNS_IP_DET_INTERLACE_FORMAT; > > - cdns_mhdp_reg_write(mhdp, CDNS_BND_HSYNC2VSYNC(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_BND_HSYNC2VSYNC(stream_id), > bnd_hsync2vsync); > > hsync2vsync_pol_ctrl = 0; > @@ -1845,10 +1575,10 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, hsync2vsync_pol_ctrl |= > CDNS_H2V_HSYNC_POL_ACTIVE_LOW; > if (mode->flags & DRM_MODE_FLAG_NVSYNC) > hsync2vsync_pol_ctrl |= CDNS_H2V_VSYNC_POL_ACTIVE_LOW; > - cdns_mhdp_reg_write(mhdp, CDNS_HSYNC2VSYNC_POL_CTRL(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_HSYNC2VSYNC_POL_CTRL(stream_id), > hsync2vsync_pol_ctrl); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_PXL_REPR(stream_id), pxl_repr); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_PXL_REPR(stream_id), > pxl_repr); > > if (mode->flags & DRM_MODE_FLAG_INTERLACE) > dp_framer_sp |= CDNS_DP_FRAMER_INTERLACE; > @@ -1856,19 +1586,19 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, dp_framer_sp |= CDNS_DP_FRAMER_HSYNC_POL_LOW; > if (mode->flags & DRM_MODE_FLAG_NVSYNC) > dp_framer_sp |= CDNS_DP_FRAMER_VSYNC_POL_LOW; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_SP(stream_id), dp_framer_sp); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_SP(stream_id), > dp_framer_sp); > > front_porch = mode->crtc_hsync_start - mode->crtc_hdisplay; > back_porch = mode->crtc_htotal - mode->crtc_hsync_end; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRONT_BACK_PORCH(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRONT_BACK_PORCH(stream_id), > CDNS_DP_FRONT_PORCH(front_porch) | > CDNS_DP_BACK_PORCH(back_porch)); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_BYTE_COUNT(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_BYTE_COUNT(stream_id), > mode->crtc_hdisplay * bpp / 8); > > msa_h0 = mode->crtc_htotal - mode->crtc_hsync_start; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_HORIZONTAL_0(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_MSA_HORIZONTAL_0(stream_id), > CDNS_DP_MSAH0_H_TOTAL(mode->crtc_htotal) | > CDNS_DP_MSAH0_HSYNC_START(msa_h0)); > > @@ -1877,11 +1607,11 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, CDNS_DP_MSAH1_HDISP_WIDTH(mode->crtc_hdisplay); > if (mode->flags & DRM_MODE_FLAG_NHSYNC) > msa_horizontal_1 |= CDNS_DP_MSAH1_HSYNC_POL_LOW; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_HORIZONTAL_1(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_MSA_HORIZONTAL_1(stream_id), > msa_horizontal_1); > > msa_v0 = mode->crtc_vtotal - mode->crtc_vsync_start; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_VERTICAL_0(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_MSA_VERTICAL_0(stream_id), > CDNS_DP_MSAV0_V_TOTAL(mode->crtc_vtotal) | > CDNS_DP_MSAV0_VSYNC_START(msa_v0)); > > @@ -1890,7 +1620,7 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, CDNS_DP_MSAV1_VDISP_WIDTH(mode->crtc_vdisplay); > if (mode->flags & DRM_MODE_FLAG_NVSYNC) > msa_vertical_1 |= CDNS_DP_MSAV1_VSYNC_POL_LOW; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_VERTICAL_1(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_MSA_VERTICAL_1(stream_id), > msa_vertical_1); > > if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && > @@ -1902,14 +1632,14 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, if (pxlfmt == DRM_COLOR_FORMAT_YCBCR420) > misc1 = CDNS_DP_TEST_VSC_SDP; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_MISC(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_MSA_MISC(stream_id), > misc0 | (misc1 << 8)); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_HORIZONTAL(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_HORIZONTAL(stream_id), > CDNS_DP_H_HSYNC_WIDTH(hsync) | > CDNS_DP_H_H_TOTAL(mode->crtc_hdisplay)); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_VERTICAL_0(stream_id), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_VERTICAL_0(stream_id), > CDNS_DP_V0_VHEIGHT(mode->crtc_vdisplay) | > CDNS_DP_V0_VSTART(msa_v0)); > > @@ -1918,13 +1648,13 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, mode->crtc_vtotal % 2 == 0) > dp_vertical_1 |= CDNS_DP_V1_VTOTAL_EVEN; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_VERTICAL_1(stream_id), dp_vertical_1); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_VERTICAL_1(stream_id), > dp_vertical_1); > > - cdns_mhdp_reg_write_bit(mhdp, CDNS_DP_VB_ID(stream_id), 2, 1, > - (mode->flags & DRM_MODE_FLAG_INTERLACE) ? > - CDNS_DP_VB_ID_INTERLACED : 0); > + cdns_mhdp_dp_reg_write_bit(&mhdp->base, CDNS_DP_VB_ID(stream_id), 2, 1, > + (mode->flags & DRM_MODE_FLAG_INTERLACE) ? > + CDNS_DP_VB_ID_INTERLACED : 0); > > - ret = cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &framer); > + ret = cdns_mhdp_reg_read(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, > &framer); if (ret < 0) { > dev_err(mhdp->dev, > "Failed to read CDNS_DP_FRAMER_GLOBAL_CONFIG %d\n", > @@ -1933,7 +1663,7 @@ static void cdns_mhdp_configure_video(struct > cdns_mhdp_device *mhdp, } > framer |= CDNS_DP_FRAMER_EN; > framer &= ~CDNS_DP_NO_VIDEO_MODE; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, framer); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, framer); > } > > static void cdns_mhdp_sst_enable(struct cdns_mhdp_device *mhdp, > @@ -1966,15 +1696,15 @@ static void cdns_mhdp_sst_enable(struct > cdns_mhdp_device *mhdp, > > mhdp->stream_id = 0; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_TU, > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_TU, > CDNS_DP_FRAMER_TU_VS(vs) | > CDNS_DP_FRAMER_TU_SIZE(tu_size) | > CDNS_DP_FRAMER_TU_CNT_RST_EN); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_LINE_THRESH(0), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_LINE_THRESH(0), > line_thresh & GENMASK(5, 0)); > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_STREAM_CONFIG_2(0), > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_STREAM_CONFIG_2(0), > CDNS_DP_SC2_TU_VS_DIFF((tu_size - vs > 3) ? > 0 : tu_size - vs)); > > @@ -2009,13 +1739,13 @@ static void cdns_mhdp_atomic_enable(struct > drm_bridge *bridge, mhdp->info->ops->enable(mhdp); > > /* Enable VIF clock for stream 0 */ > - ret = cdns_mhdp_reg_read(mhdp, CDNS_DPTX_CAR, &resp); > + ret = cdns_mhdp_reg_read(&mhdp->base, CDNS_DPTX_CAR, &resp); > if (ret < 0) { > dev_err(mhdp->dev, "Failed to read CDNS_DPTX_CAR %d\n", ret); > goto out; > } > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_CAR, > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_CAR, > resp | CDNS_VIF_CLK_EN | CDNS_VIF_CLK_RSTN); > > connector = drm_atomic_get_new_connector_for_encoder(state, > @@ -2083,16 +1813,16 @@ static void cdns_mhdp_atomic_disable(struct > drm_bridge *bridge, cdns_mhdp_hdcp_disable(mhdp); > > mhdp->bridge_enabled = false; > - cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp); > + cdns_mhdp_reg_read(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp); > resp &= ~CDNS_DP_FRAMER_EN; > resp |= CDNS_DP_NO_VIDEO_MODE; > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, resp); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_GLOBAL_CONFIG, resp); > > cdns_mhdp_link_down(mhdp); > > /* Disable VIF clock for stream 0 */ > - cdns_mhdp_reg_read(mhdp, CDNS_DPTX_CAR, &resp); > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_CAR, > + cdns_mhdp_reg_read(&mhdp->base, CDNS_DPTX_CAR, &resp); > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_CAR, > resp & ~(CDNS_VIF_CLK_EN | CDNS_VIF_CLK_RSTN)); > > if (mhdp->info && mhdp->info->ops && mhdp->info->ops->disable) > @@ -2502,6 +2232,11 @@ static int cdns_mhdp_probe(struct platform_device > *pdev) > > platform_set_drvdata(pdev, mhdp); > > + /* init base struct for access mailbox */ > + mhdp->base.dev = mhdp->dev; > + mhdp->base.regs = mhdp->regs; > + mhdp->base.mbox_mutex = &mhdp->mbox_mutex; > + > mhdp->info = of_device_get_match_data(dev); > > clk_prepare_enable(clk); > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h index > bad2fc0c73066..f08db38c82bbd 100644 > --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > @@ -15,6 +15,7 @@ > #include <linux/mutex.h> > #include <linux/spinlock.h> > > +#include <drm/bridge/cdns-mhdp-helper.h> > #include <drm/display/drm_dp_helper.h> > #include <drm/drm_bridge.h> > #include <drm/drm_connector.h> > @@ -27,10 +28,6 @@ struct phy; > #define CDNS_APB_CTRL 0x00000 > #define CDNS_CPU_STALL BIT(3) > > -#define CDNS_MAILBOX_FULL 0x00008 > -#define CDNS_MAILBOX_EMPTY 0x0000c > -#define CDNS_MAILBOX_TX_DATA 0x00010 > -#define CDNS_MAILBOX_RX_DATA 0x00014 > #define CDNS_KEEP_ALIVE 0x00018 > #define CDNS_KEEP_ALIVE_MASK GENMASK(7, 0) > > @@ -198,45 +195,10 @@ struct phy; > #define CDNS_DP_BYTE_COUNT(s) (CDNS_DPTX_STREAM(s) + 0x7c) > #define CDNS_DP_BYTE_COUNT_BYTES_IN_CHUNK_SHIFT 16 > > -/* mailbox */ > -#define MAILBOX_RETRY_US 1000 > -#define MAILBOX_TIMEOUT_US 2000000 > - > -#define MB_OPCODE_ID 0 > -#define MB_MODULE_ID 1 > -#define MB_SIZE_MSB_ID 2 > -#define MB_SIZE_LSB_ID 3 > -#define MB_DATA_ID 4 > - > -#define MB_MODULE_ID_DP_TX 0x01 > -#define MB_MODULE_ID_HDCP_TX 0x07 > -#define MB_MODULE_ID_HDCP_RX 0x08 > -#define MB_MODULE_ID_HDCP_GENERAL 0x09 > -#define MB_MODULE_ID_GENERAL 0x0a > - > -/* firmware and opcodes */ > +/* firmware */ > #define FW_NAME "cadence/ mhdp8546.bin" > #define CDNS_MHDP_IMEM 0x10000 > > -#define GENERAL_MAIN_CONTROL 0x01 > -#define GENERAL_TEST_ECHO 0x02 > -#define GENERAL_BUS_SETTINGS 0x03 > -#define GENERAL_TEST_ACCESS 0x04 > -#define GENERAL_REGISTER_READ 0x07 > - > -#define DPTX_SET_POWER_MNG 0x00 > -#define DPTX_GET_EDID 0x02 > -#define DPTX_READ_DPCD 0x03 > -#define DPTX_WRITE_DPCD 0x04 > -#define DPTX_ENABLE_EVENT 0x05 > -#define DPTX_WRITE_REGISTER 0x06 > -#define DPTX_READ_REGISTER 0x07 > -#define DPTX_WRITE_FIELD 0x08 > -#define DPTX_READ_EVENT 0x0a > -#define DPTX_GET_LAST_AUX_STAUS 0x0e > -#define DPTX_HPD_STATE 0x11 > -#define DPTX_ADJUST_LT 0x12 > - > #define FW_STANDBY 0 > #define FW_ACTIVE 1 > > @@ -352,6 +314,8 @@ struct cdns_mhdp_hdcp { > }; > > struct cdns_mhdp_device { > + struct cdns_mhdp_base base; > + > void __iomem *regs; > void __iomem *sapb_regs; > void __iomem *j721e_regs; > diff --git a/include/drm/bridge/cdns-mhdp-helper.h > b/include/drm/bridge/cdns-mhdp-helper.h new file mode 100644 > index 0000000000000..477e67601ee5f > --- /dev/null > +++ b/include/drm/bridge/cdns-mhdp-helper.h > @@ -0,0 +1,94 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2023 NXP Semiconductor, Inc. > + */ > +#ifndef __CDNS_MHDP_HELPER_H__ > +#define __CDNS_MHDP_HELPER_H__ > + > +#include <asm/unaligned.h> > +#include <linux/iopoll.h> > + > +/* mailbox regs offset */ > +#define CDNS_MAILBOX_FULL 0x00008 > +#define CDNS_MAILBOX_EMPTY 0x0000c > +#define CDNS_MAILBOX_TX_DATA 0x00010 > +#define CDNS_MAILBOX_RX_DATA 0x00014 > + > +#define MAILBOX_RETRY_US 1000 > +#define MAILBOX_TIMEOUT_US 2000000 > + > +/* Module ID Code */ > +#define MB_MODULE_ID_DP_TX 0x01 > +#define MB_MODULE_ID_HDMI_TX 0x03 > +#define MB_MODULE_ID_HDCP_TX 0x07 > +#define MB_MODULE_ID_HDCP_RX 0x08 > +#define MB_MODULE_ID_HDCP_GENERAL 0x09 > +#define MB_MODULE_ID_GENERAL 0x0A > + > +/* General Commands */ > +#define GENERAL_MAIN_CONTROL 0x01 > +#define GENERAL_TEST_ECHO 0x02 > +#define GENERAL_BUS_SETTINGS 0x03 > +#define GENERAL_TEST_ACCESS 0x04 > +#define GENERAL_REGISTER_WRITE 0x05 > +#define GENERAL_WRITE_FIELD 0x06 > +#define GENERAL_REGISTER_READ 0x07 > +#define GENERAL_GET_HPD_STATE 0x11 > + > +/* DPTX Commands */ > +#define DPTX_SET_POWER_MNG 0x00 > +#define DPTX_SET_HOST_CAPABILITIES 0x01 > +#define DPTX_GET_EDID 0x02 > +#define DPTX_READ_DPCD 0x03 > +#define DPTX_WRITE_DPCD 0x04 > +#define DPTX_ENABLE_EVENT 0x05 > +#define DPTX_WRITE_REGISTER 0x06 > +#define DPTX_READ_REGISTER 0x07 > +#define DPTX_WRITE_FIELD 0x08 > +#define DPTX_TRAINING_CONTROL 0x09 > +#define DPTX_READ_EVENT 0x0a > +#define DPTX_READ_LINK_STAT 0x0b > +#define DPTX_SET_VIDEO 0x0c > +#define DPTX_SET_AUDIO 0x0d > +#define DPTX_GET_LAST_AUX_STAUS 0x0e > +#define DPTX_SET_LINK_BREAK_POINT 0x0f > +#define DPTX_FORCE_LANES 0x10 > +#define DPTX_HPD_STATE 0x11 > +#define DPTX_ADJUST_LT 0x12 > + > +/* HDMI TX Commands */ > +#define HDMI_TX_READ 0x00 > +#define HDMI_TX_WRITE 0x01 > +#define HDMI_TX_UPDATE_READ 0x02 > +#define HDMI_TX_EDID 0x03 > +#define HDMI_TX_EVENTS 0x04 > +#define HDMI_TX_HPD_STATUS 0x05 > + > +struct cdns_mhdp_base { > + struct device *dev; > + void __iomem *regs; > + /* protect mailbox communications with the firmware */ > + struct mutex *mbox_mutex; > +}; > + > +/* Mailbox helper functions */ > +int cdns_mhdp_mailbox_send(struct cdns_mhdp_base *base, u8 module_id, > + u8 opcode, u16 size, u8 *message); Any reason to move the declaration for send before recv? It seems reasonable to have the in alphabetical order. > +int cdns_mhdp_mailbox_recv_header(struct cdns_mhdp_base *base, > + u8 module_id, u8 opcode, u16 req_size); > +int cdns_mhdp_mailbox_recv_data(struct cdns_mhdp_base *base, > + u8 *buff, u16 buff_size); AFAICS while calling a sequence of these 3 functions mhdp->mbox_mutex is locked. This should be noted here. Best regards, Alexander > +/* General commands helper functions */ > +int cdns_mhdp_reg_read(struct cdns_mhdp_base *base, u32 addr, u32 *value); > +int cdns_mhdp_reg_write(struct cdns_mhdp_base *base, u32 addr, u32 val); > + > +/* DPTX commands helper functions */ > +int cdns_mhdp_dp_reg_write(struct cdns_mhdp_base *base, u16 addr, u32 val); > +int cdns_mhdp_dp_reg_write_bit(struct cdns_mhdp_base *base, u16 addr, + > u8 start_bit, u8 bits_no, u32 val); > +int cdns_mhdp_dpcd_read(struct cdns_mhdp_base *base, > + u32 addr, u8 *data, u16 len); > +int cdns_mhdp_dpcd_write(struct cdns_mhdp_base *base, u32 addr, u8 value); > + > +#endif /* __CDNS_MHDP_HELPER_H__ */
Hi Sandor, thanks for the patch. Am Dienstag, 17. Oktober 2023, 09:04:00 CEST schrieb Sandor Yu: > Add a new DRM DisplayPort and HDMI bridge driver for Candence MHDP8501 > used in i.MX8MQ SOC. MHDP8501 could support HDMI or DisplayPort > standards according embedded Firmware running in the uCPU. > > For iMX8MQ SOC, the DisplayPort/HDMI FW was loaded and activated by > SOC's ROM code. Bootload binary included respective specific firmware > is required. > > Driver will check display connector type and > then load the corresponding driver. > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> > --- > v10->v11: > - remove MODULE_ALIAS() from mhdp8501 driver. > > v9->v10: > - struct cdns_mhdp_device is renamed to cdns_mhdp8501_device. > - update for mhdp helper driver is introduced. > Remove head file cdns-mhdp-mailbox.h and add cdns-mhdp-helper.h > Add struct cdns_mhdp_base base to struct cdns_mhdp8501_device. > Init struct cdns_mhdp_base base when driver probe. > > drivers/gpu/drm/bridge/cadence/Kconfig | 16 + > drivers/gpu/drm/bridge/cadence/Makefile | 2 + > .../drm/bridge/cadence/cdns-mhdp8501-core.c | 315 ++++++++ > .../drm/bridge/cadence/cdns-mhdp8501-core.h | 365 +++++++++ > .../gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c | 708 ++++++++++++++++++ > .../drm/bridge/cadence/cdns-mhdp8501-hdmi.c | 673 +++++++++++++++++ > 6 files changed, 2079 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > b/drivers/gpu/drm/bridge/cadence/Kconfig index 0b7b4626a7af0..81685ab4e874a > 100644 > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig > @@ -50,3 +50,19 @@ config DRM_CDNS_MHDP8546_J721E > initializes the J721E Display Port and sets up the > clock and data muxes. > endif > + > +config DRM_CDNS_MHDP8501 > + tristate "Cadence MHDP8501 DP/HDMI bridge" > + select DRM_KMS_HELPER > + select DRM_PANEL_BRIDGE > + select DRM_DISPLAY_DP_HELPER > + select DRM_DISPLAY_HELPER > + select CDNS_MHDP_HELPER > + select DRM_CDNS_AUDIO > + depends on OF > + help > + Support Cadence MHDP8501 DisplayPort/HDMI bridge. > + Cadence MHDP8501 support one or more protocols, > + including DisplayPort and HDMI. > + To use the DP and HDMI drivers, their respective > + specific firmware is required. > diff --git a/drivers/gpu/drm/bridge/cadence/Makefile > b/drivers/gpu/drm/bridge/cadence/Makefile index > 087dc074820d7..02c1a9f3cf6fc 100644 > --- a/drivers/gpu/drm/bridge/cadence/Makefile > +++ b/drivers/gpu/drm/bridge/cadence/Makefile > @@ -6,3 +6,5 @@ obj-$(CONFIG_CDNS_MHDP_HELPER) += cdns-mhdp-helper.o > obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o > cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o > cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o > +obj-$(CONFIG_DRM_CDNS_MHDP8501) += cdns-mhdp8501.o > +cdns-mhdp8501-y := cdns-mhdp8501-core.o cdns-mhdp8501-dp.o > cdns-mhdp8501-hdmi.o diff --git > a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c new file mode 100644 > index 0000000000000..23860a260e637 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c > @@ -0,0 +1,315 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Cadence Display Port Interface (DP) driver > + * > + * Copyright (C) 2023 NXP Semiconductor, Inc. > + * > + */ > +#include <drm/drm_of.h> > +#include <drm/drm_print.h> > +#include <linux/clk.h> > +#include <linux/irq.h> > +#include <linux/mutex.h> > +#include <linux/of_device.h> > +#include <linux/phy/phy.h> > + > +#include "cdns-mhdp8501-core.h" > + > +static int cdns_mhdp8501_read_hpd(struct cdns_mhdp8501_device *mhdp) > +{ > + u8 status; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_GENERAL, > + GENERAL_GET_HPD_STATE, 0, NULL); > + if (ret) > + goto err_get_hpd; > + > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_GENERAL, > + GENERAL_GET_HPD_STATE, > + sizeof(status)); > + if (ret) > + goto err_get_hpd; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &status, sizeof(status)); > + if (ret) > + goto err_get_hpd; > + > + mutex_unlock(&mhdp->mbox_mutex); > + > + return status; > + > +err_get_hpd: > + DRM_ERROR("read hpd failed: %d\n", ret); Use dev_err() instead, there is a device pointer available. > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +enum drm_connector_status cdns_mhdp8501_detect(struct cdns_mhdp8501_device > *mhdp) +{ > + u8 hpd = 0xf; > + > + hpd = cdns_mhdp8501_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); I suppose this is a somewhat unexpected return value. Shouldn't this be DRM_WARN instead to indicate something went wrong? Despite that dev_warn (or dev_info) should be used. > + return connector_status_unknown; > +} > + > +static void hotplug_work_func(struct work_struct *work) > +{ > + struct cdns_mhdp8501_device *mhdp = container_of(work, > + struct cdns_mhdp8501_device, > + hotplug_work.work); > + enum drm_connector_status status = cdns_mhdp8501_detect(mhdp); > + > + drm_bridge_hpd_notify(&mhdp->bridge, status); > + > + if (status == connector_status_connected) { > + /* Cable connedted */ Small typo: Cable connected > + DRM_INFO("HDMI/DP Cable Plug In\n"); drm_info() > + enable_irq(mhdp->irq[IRQ_OUT]); > + } else if (status == connector_status_disconnected) { > + /* Cable Disconnedted */ Small typo: Cable Disconnected > + DRM_INFO("HDMI/DP Cable Plug Out\n"); drm_info() > + enable_irq(mhdp->irq[IRQ_IN]); > + } > +} > + > +static irqreturn_t cdns_mhdp8501_irq_thread(int irq, void *data) > +{ > + struct cdns_mhdp8501_device *mhdp = data; > + > + disable_irq_nosync(irq); Is it really necessary to enable/disable the IRQ_IN and IRQ_OUT interrupts upon each IRQ event? The actual status is returned by the firmware using cdns_mhdp8501_read_hpd() anyway. There is a small window between the IRQ happening here and enabling the other one in hotplug_work_func() where an IRQ event is lost. IMHO both IRQs should be enabled at all time and let cdns_mhdp8501_read_hpd() return whether the connector is connected or disconnected. > + > + mod_delayed_work(system_wq, &mhdp->hotplug_work, > + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); > + > + return IRQ_HANDLED; > +} > + > +static int cdns_mhdp8501_dt_parse(struct cdns_mhdp8501_device *mhdp, > + struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *np = dev->of_node; > + struct device_node *remote; > + > + remote = of_graph_get_remote_node(np, 1, 0); > + if (!remote) { > + dev_err(dev, "fail to get remote node\n"); > + of_node_put(remote); > + return -EINVAL; > + } > + > + /* get connector type */ > + if (of_device_is_compatible(remote, "hdmi-connector")) { > + mhdp->connector_type = DRM_MODE_CONNECTOR_HDMIA; > + > + } else if (of_device_is_compatible(remote, "dp-connector")) { > + mhdp->connector_type = DRM_MODE_CONNECTOR_DisplayPort; > + > + } else { > + dev_err(dev, "Unknown connector type\n"); > + of_node_put(remote); > + return -EINVAL; > + } > + > + of_node_put(remote); > + return true; > +} > + > +static void cdns_mhdp8501_add_bridge(struct cdns_mhdp8501_device *mhdp) > +{ > + if (mhdp->connector_type == DRM_MODE_CONNECTOR_HDMIA) { > + mhdp->bridge.funcs = &cdns_hdmi_bridge_funcs; > + } else if (mhdp->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { > + mhdp->bridge.funcs = &cdns_dp_bridge_funcs; > + } else { > + dev_err(mhdp->dev, "Unsupported connector type!\n"); > + return; > + } > + > + mhdp->bridge.type = mhdp->connector_type; > + mhdp->bridge.driver_private = mhdp; > + mhdp->bridge.of_node = mhdp->dev->of_node; > + mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | > + DRM_BRIDGE_OP_HPD; > + drm_bridge_add(&mhdp->bridge); > +} > + > +static int cdns_mhdp8501_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct cdns_mhdp8501_device *mhdp; > + struct resource *res; > + u32 reg; > + int ret; > + > + mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); > + if (!mhdp) > + return -ENOMEM; > + > + mutex_init(&mhdp->mbox_mutex); > + mhdp->dev = dev; > + > + 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); You can use devm_platform_ioremap_resource instead, no? > + > + ret = cdns_mhdp8501_dt_parse(mhdp, pdev); > + if (ret < 0) > + return -EINVAL; > + > + mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, 0); > + if (IS_ERR(mhdp->phy)) > + return dev_err_probe(dev, PTR_ERR(mhdp->phy), "no PHY configured\n"); > + > + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); > + if (mhdp->irq[IRQ_IN] < 0) > + return dev_err_probe(dev, mhdp->irq[IRQ_IN], "No plug_in irq number\n"); > + > + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out"); > + if (mhdp->irq[IRQ_OUT] < 0) > + return dev_err_probe(dev, mhdp->irq[IRQ_OUT], "No plug_out irq > number\n"); + > + irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); As mentioned above both interrupts should be enabled at all the time. > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], > + NULL, cdns_mhdp8501_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); See above. > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], > + NULL, cdns_mhdp8501_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; > + } > + > + /* set default lane mapping */ > + mhdp->lane_mapping = LANE_MAPPING_NORMAL; > + > + mhdp->plat_data = of_device_get_match_data(dev); > + if (mhdp->plat_data) { > + if (mhdp->connector_type == DRM_MODE_CONNECTOR_DisplayPort) > + mhdp->lane_mapping = mhdp->plat_data- >dp_lane_mapping; > + else if (mhdp->connector_type == DRM_MODE_CONNECTOR_HDMIA) > + mhdp->lane_mapping = mhdp->plat_data- >hdmi_lane_mapping; > + } > + > + dev_set_drvdata(dev, mhdp); > + > + /* init base struct for access mhdp mailbox */ > + mhdp->base.dev = mhdp->dev; > + mhdp->base.regs = mhdp->regs; > + mhdp->base.mbox_mutex = &mhdp->mbox_mutex; > + > + if (mhdp->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { > + drm_dp_aux_init(&mhdp->dp.aux); > + mhdp->dp.aux.name = "mhdp8501_dp_aux"; > + mhdp->dp.aux.dev = dev; > + mhdp->dp.aux.transfer = cdns_dp_aux_transfer; > + } > + > + /* Enable APB clock */ > + mhdp->apb_clk = devm_clk_get(dev, NULL); > + if (IS_ERR(mhdp->apb_clk)) > + return dev_err_probe(dev, PTR_ERR(mhdp->apb_clk), > + "couldn't get apb clk\n"); > + > + clk_prepare_enable(mhdp->apb_clk); > + > + /* > + * 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); > + if (ret) { > + dev_err(dev, "device didn't give any life sign: reg %d\n", reg); > + goto clk_disable; > + } > + > + /* Mailbox protect for HDMI PHY access */ > + mutex_lock(&mhdp->mbox_mutex); > + ret = phy_init(mhdp->phy); > + mutex_unlock(&mhdp->mbox_mutex); > + if (ret) { > + dev_err(dev, "Failed to initialize PHY: %d\n", ret); > + goto clk_disable; > + } > + > + /* Enable cable hotplug detect */ > + if (cdns_mhdp8501_read_hpd(mhdp)) > + enable_irq(mhdp->irq[IRQ_OUT]); > + else > + enable_irq(mhdp->irq[IRQ_IN]); > + > + cdns_mhdp8501_add_bridge(mhdp); > + > + return 0; > + > +clk_disable: > + clk_disable_unprepare(mhdp->apb_clk); > + > + return -EINVAL; > +} > + > +static int cdns_mhdp8501_remove(struct platform_device *pdev) > +{ > + struct cdns_mhdp8501_device *mhdp = platform_get_drvdata(pdev); > + > + if (mhdp->connector_type == DRM_MODE_CONNECTOR_DisplayPort) > + cdns_dp_aux_destroy(mhdp); > + > + drm_bridge_remove(&mhdp->bridge); > + clk_disable_unprepare(mhdp->apb_clk); > + > + return 0; > +} > + > +static struct mhdp8501_plat_data imx8mq_mhdp_drv_data = { > + .hdmi_lane_mapping = LANE_MAPPING_FLIPPED, > + .dp_lane_mapping = LANE_MAPPING_IMX8MQ_DP, > +}; > + > +static const struct of_device_id cdns_mhdp8501_dt_ids[] = { > + { .compatible = "fsl,imx8mq-mhdp8501", > + .data = &imx8mq_mhdp_drv_data > + }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, cdns_mhdp8501_dt_ids); > + > +static struct platform_driver cdns_mhdp8501_driver = { > + .probe = cdns_mhdp8501_probe, > + .remove = cdns_mhdp8501_remove, > + .driver = { > + .name = "cdns-mhdp8501", > + .of_match_table = cdns_mhdp8501_dt_ids, > + }, > +}; > + > +module_platform_driver(cdns_mhdp8501_driver); > + > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > +MODULE_DESCRIPTION("Cadence MHDP8501 bridge driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h new file mode 100644 > index 0000000000000..97170be57ffcb > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h > @@ -0,0 +1,365 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Cadence MHDP 8501 Common head file > + * > + * Copyright (C) 2019-2023 NXP Semiconductor, Inc. > + * > + */ > + > +#ifndef _CDNS_MHDP8501_CORE_H_ > +#define _CDNS_MHDP8501_CORE_H_ > + > +#include <drm/bridge/cdns-mhdp-helper.h> > +#include <drm/drm_bridge.h> > +#include <drm/drm_connector.h> > +#include <drm/display/drm_dp_helper.h> > +#include <linux/bitops.h> > + > +#define ADDR_IMEM 0x10000 > +#define ADDR_DMEM 0x20000 > + > +/* APB CFG addr */ > +#define APB_CTRL 0 > +#define XT_INT_CTRL 0x04 > +#define MAILBOX_FULL_ADDR 0x08 > +#define MAILBOX_EMPTY_ADDR 0x0c > +#define MAILBOX0_WR_DATA 0x10 > +#define MAILBOX0_RD_DATA 0x14 > +#define KEEP_ALIVE 0x18 > +#define VER_L 0x1c > +#define VER_H 0x20 > +#define VER_LIB_L_ADDR 0x24 > +#define VER_LIB_H_ADDR 0x28 > +#define SW_DEBUG_L 0x2c > +#define SW_DEBUG_H 0x30 > +#define MAILBOX_INT_MASK 0x34 > +#define MAILBOX_INT_STATUS 0x38 > +#define SW_CLK_L 0x3c > +#define SW_CLK_H 0x40 > +#define SW_EVENTS0 0x44 > +#define SW_EVENTS1 0x48 > +#define SW_EVENTS2 0x4c > +#define SW_EVENTS3 0x50 > +#define XT_OCD_CTRL 0x60 > +#define APB_INT_MASK 0x6c > +#define APB_STATUS_MASK 0x70 > + > +/* Source phy comp */ > +#define PHY_DATA_SEL 0x0818 > +#define LANES_CONFIG 0x0814 > + > +/* Source CAR Addr */ > +#define SOURCE_HDTX_CAR 0x0900 > +#define SOURCE_DPTX_CAR 0x0904 > +#define SOURCE_PHY_CAR 0x0908 > +#define SOURCE_CEC_CAR 0x090c > +#define SOURCE_CBUS_CAR 0x0910 > +#define SOURCE_PKT_CAR 0x0918 > +#define SOURCE_AIF_CAR 0x091c > +#define SOURCE_CIPHER_CAR 0x0920 > +#define SOURCE_CRYPTO_CAR 0x0924 > + > +/* clock meters addr */ > +#define CM_CTRL 0x0a00 > +#define CM_I2S_CTRL 0x0a04 > +#define CM_SPDIF_CTRL 0x0a08 > +#define CM_VID_CTRL 0x0a0c > +#define CM_LANE_CTRL 0x0a10 > +#define I2S_NM_STABLE 0x0a14 > +#define I2S_NCTS_STABLE 0x0a18 > +#define SPDIF_NM_STABLE 0x0a1c > +#define SPDIF_NCTS_STABLE 0x0a20 > +#define NMVID_MEAS_STABLE 0x0a24 > +#define I2S_MEAS 0x0a40 > +#define SPDIF_MEAS 0x0a80 > +#define NMVID_MEAS 0x0ac0 > + > +/* source vif addr */ > +#define BND_HSYNC2VSYNC 0x0b00 > +#define HSYNC2VSYNC_F1_L1 0x0b04 > +#define HSYNC2VSYNC_STATUS 0x0b0c > +#define HSYNC2VSYNC_POL_CTRL 0x0b10 > + > +/* MHDP TX_top_comp */ > +#define SCHEDULER_H_SIZE 0x1000 > +#define SCHEDULER_V_SIZE 0x1004 > +#define HDTX_SIGNAL_FRONT_WIDTH 0x100c > +#define HDTX_SIGNAL_SYNC_WIDTH 0x1010 > +#define HDTX_SIGNAL_BACK_WIDTH 0x1014 > +#define HDTX_CONTROLLER 0x1018 > +#define HDTX_HPD 0x1020 > +#define HDTX_CLOCK_REG_0 0x1024 > +#define HDTX_CLOCK_REG_1 0x1028 > + > +/* DPTX hpd addr */ > +#define HPD_IRQ_DET_MIN_TIMER 0x2100 > +#define HPD_IRQ_DET_MAX_TIMER 0x2104 > +#define HPD_UNPLGED_DET_MIN_TIMER 0x2108 > +#define HPD_STABLE_TIMER 0x210c > +#define HPD_FILTER_TIMER 0x2110 > +#define HPD_EVENT_MASK 0x211c > +#define HPD_EVENT_DET 0x2120 > + > +/* DPTX framer addr */ > +#define DP_FRAMER_GLOBAL_CONFIG 0x2200 > +#define DP_SW_RESET 0x2204 > +#define DP_FRAMER_TU 0x2208 > +#define DP_FRAMER_PXL_REPR 0x220c > +#define DP_FRAMER_SP 0x2210 > +#define AUDIO_PACK_CONTROL 0x2214 > +#define DP_VC_TABLE(x) (0x2218 + ((x) << 2)) > +#define DP_VB_ID 0x2258 > +#define DP_MTPH_LVP_CONTROL 0x225c > +#define DP_MTPH_SYMBOL_VALUES 0x2260 > +#define DP_MTPH_ECF_CONTROL 0x2264 > +#define DP_MTPH_ACT_CONTROL 0x2268 > +#define DP_MTPH_STATUS 0x226c > +#define DP_INTERRUPT_SOURCE 0x2270 > +#define DP_INTERRUPT_MASK 0x2274 > +#define DP_FRONT_BACK_PORCH 0x2278 > +#define DP_BYTE_COUNT 0x227c > + > +/* DPTX stream addr */ > +#define MSA_HORIZONTAL_0 0x2280 > +#define MSA_HORIZONTAL_1 0x2284 > +#define MSA_VERTICAL_0 0x2288 > +#define MSA_VERTICAL_1 0x228c > +#define MSA_MISC 0x2290 > +#define STREAM_CONFIG 0x2294 > +#define AUDIO_PACK_STATUS 0x2298 > +#define VIF_STATUS 0x229c > +#define PCK_STUFF_STATUS_0 0x22a0 > +#define PCK_STUFF_STATUS_1 0x22a4 > +#define INFO_PACK_STATUS 0x22a8 > +#define RATE_GOVERNOR_STATUS 0x22ac > +#define DP_HORIZONTAL 0x22b0 > +#define DP_VERTICAL_0 0x22b4 > +#define DP_VERTICAL_1 0x22b8 > +#define DP_BLOCK_SDP 0x22bc > + > +/* DPTX glbl addr */ > +#define DPTX_LANE_EN 0x2300 > +#define DPTX_ENHNCD 0x2304 > +#define DPTX_INT_MASK 0x2308 > +#define DPTX_INT_STATUS 0x230c > + > +/* DP AUX Addr */ > +#define DP_AUX_HOST_CONTROL 0x2800 > +#define DP_AUX_INTERRUPT_SOURCE 0x2804 > +#define DP_AUX_INTERRUPT_MASK 0x2808 > +#define DP_AUX_SWAP_INVERSION_CONTROL 0x280c > +#define DP_AUX_SEND_NACK_TRANSACTION 0x2810 > +#define DP_AUX_CLEAR_RX 0x2814 > +#define DP_AUX_CLEAR_TX 0x2818 > +#define DP_AUX_TIMER_STOP 0x281c > +#define DP_AUX_TIMER_CLEAR 0x2820 > +#define DP_AUX_RESET_SW 0x2824 > +#define DP_AUX_DIVIDE_2M 0x2828 > +#define DP_AUX_TX_PREACHARGE_LENGTH 0x282c > +#define DP_AUX_FREQUENCY_1M_MAX 0x2830 > +#define DP_AUX_FREQUENCY_1M_MIN 0x2834 > +#define DP_AUX_RX_PRE_MIN 0x2838 > +#define DP_AUX_RX_PRE_MAX 0x283c > +#define DP_AUX_TIMER_PRESET 0x2840 > +#define DP_AUX_NACK_FORMAT 0x2844 > +#define DP_AUX_TX_DATA 0x2848 > +#define DP_AUX_RX_DATA 0x284c > +#define DP_AUX_TX_STATUS 0x2850 > +#define DP_AUX_RX_STATUS 0x2854 > +#define DP_AUX_RX_CYCLE_COUNTER 0x2858 > +#define DP_AUX_MAIN_STATES 0x285c > +#define DP_AUX_MAIN_TIMER 0x2860 > +#define DP_AUX_AFE_OUT 0x2864 > + > +/* source pif addr */ > +#define SOURCE_PIF_WR_ADDR 0x30800 > +#define SOURCE_PIF_WR_REQ 0x30804 > +#define SOURCE_PIF_RD_ADDR 0x30808 > +#define SOURCE_PIF_RD_REQ 0x3080c > +#define SOURCE_PIF_DATA_WR 0x30810 > +#define SOURCE_PIF_DATA_RD 0x30814 > +#define SOURCE_PIF_FIFO1_FLUSH 0x30818 > +#define SOURCE_PIF_FIFO2_FLUSH 0x3081c > +#define SOURCE_PIF_STATUS 0x30820 > +#define SOURCE_PIF_INTERRUPT_SOURCE 0x30824 > +#define SOURCE_PIF_INTERRUPT_MASK 0x30828 > +#define SOURCE_PIF_PKT_ALLOC_REG 0x3082c > +#define SOURCE_PIF_PKT_ALLOC_WR_EN 0x30830 > +#define SOURCE_PIF_SW_RESET 0x30834 > + > +#define LINK_TRAINING_NOT_ACTIV 0 > +#define LINK_TRAINING_RUN 1 > +#define LINK_TRAINING_RESTART 2 > + > +#define CONTROL_VIDEO_IDLE 0 > +#define CONTROL_VIDEO_VALID 1 > + > +#define INTERLACE_FMT_DET BIT(12) > +#define VIF_BYPASS_INTERLACE BIT(13) > +#define TU_CNT_RST_EN BIT(15) > +#define INTERLACE_DTCT_WIN 0x20 > + > +#define DP_FRAMER_SP_INTERLACE_EN BIT(2) > +#define DP_FRAMER_SP_HSP BIT(1) > +#define DP_FRAMER_SP_VSP BIT(0) > + > +/* Capability */ > +#define AUX_HOST_INVERT 3 > +#define FAST_LT_SUPPORT 1 > +#define FAST_LT_NOT_SUPPORT 0 > +#define LANE_MAPPING_NORMAL 0x1b > +#define LANE_MAPPING_FLIPPED 0xe4 > +#define LANE_MAPPING_IMX8MQ_DP 0xc6 > +#define ENHANCED 1 > +#define SCRAMBLER_EN BIT(4) > + > +#define FULL_LT_STARTED BIT(0) > +#define FASE_LT_STARTED BIT(1) > +#define CLK_RECOVERY_FINISHED BIT(2) > +#define EQ_PHASE_FINISHED BIT(3) > +#define FASE_LT_START_FINISHED BIT(4) > +#define CLK_RECOVERY_FAILED BIT(5) > +#define EQ_PHASE_FAILED BIT(6) > +#define FASE_LT_FAILED BIT(7) > + > +#define TU_SIZE 30 > +#define CDNS_DP_MAX_LINK_RATE 540000 > + > +#define F_HDMI_ENCODING(x) (((x) & ((1 << 2) - 1)) << 16) > +#define F_VIF_DATA_WIDTH(x) (((x) & ((1 << 2) - 1)) << 2) > +#define F_HDMI_MODE(x) (((x) & ((1 << 2) - 1)) << 0) > +#define F_GCP_EN(x) (((x) & ((1 << 1) - 1)) << 12) > +#define F_DATA_EN(x) (((x) & ((1 << 1) - 1)) << 15) > +#define F_HDMI2_PREAMBLE_EN(x) (((x) & ((1 << 1) - 1)) << 18) > +#define F_PIC_3D(x) (((x) & ((1 << 4) - 1)) << 7) > +#define F_BCH_EN(x) (((x) & ((1 << 1) - 1)) << 11) > +#define F_SOURCE_PHY_MHDP_SEL(x) (((x) & ((1 << 2) - 1)) << 3) > +#define F_HPD_VALID_WIDTH(x) (((x) & ((1 << 12) - 1)) << 0) > +#define F_HPD_GLITCH_WIDTH(x) (((x) & ((1 << 8) - 1)) << 12) > +#define F_HDMI2_CTRL_IL_MODE(x) (((x) & ((1 << 1) - 1)) << 19) > +#define F_SOURCE_PHY_LANE0_SWAP(x) (((x) & ((1 << 2) - 1)) << 0) > +#define F_SOURCE_PHY_LANE1_SWAP(x) (((x) & ((1 << 2) - 1)) << 2) > +#define F_SOURCE_PHY_LANE2_SWAP(x) (((x) & ((1 << 2) - 1)) << 4) > +#define F_SOURCE_PHY_LANE3_SWAP(x) (((x) & ((1 << 2) - 1)) << 6) > +#define F_SOURCE_PHY_COMB_BYPASS(x) (((x) & ((1 << 1) - 1)) << 21) > +#define F_SOURCE_PHY_20_10(x) (((x) & ((1 << 1) - 1)) << 22) > +#define F_PKT_ALLOC_ADDRESS(x) (((x) & ((1 << 4) - 1)) << 0) > +#define F_ACTIVE_IDLE_TYPE(x) (((x) & ((1 << 1) - 1)) << 17) > +#define F_FIFO1_FLUSH(x) (((x) & ((1 << 1) - 1)) << 0) > +#define F_PKT_ALLOC_WR_EN(x) (((x) & ((1 << 1) - 1)) << 0) > +#define F_DATA_WR(x) (x) > +#define F_WR_ADDR(x) (((x) & ((1 << 4) - 1)) << 0) > +#define F_HOST_WR(x) (((x) & ((1 << 1) - 1)) << 0) > +#define F_TYPE_VALID(x) (((x) & ((1 << 1) - 1)) << 16) > +#define F_PACKET_TYPE(x) (((x) & ((1 << 8) - 1)) << 8) > + > +/* Reference cycles when using lane clock as reference */ > +#define LANE_REF_CYC 0x8000 > + > +/* HPD Debounce */ > +#define HOTPLUG_DEBOUNCE_MS 200 > + > +/* HPD IRQ Index */ > +#define IRQ_IN 0 > +#define IRQ_OUT 1 > +#define IRQ_NUM 2 > + > +/* FW check alive timeout */ > +#define CDNS_KEEP_ALIVE_TIMEOUT 2000 > +#define CDNS_KEEP_ALIVE_MASK GENMASK(7, 0) > + > +enum voltage_swing_level { > + VOLTAGE_LEVEL_0, > + VOLTAGE_LEVEL_1, > + VOLTAGE_LEVEL_2, > + VOLTAGE_LEVEL_3, > +}; > + > +enum pre_emphasis_level { > + PRE_EMPHASIS_LEVEL_0, > + PRE_EMPHASIS_LEVEL_1, > + PRE_EMPHASIS_LEVEL_2, > + PRE_EMPHASIS_LEVEL_3, > +}; > + > +enum pattern_set { > + PTS1 = BIT(0), > + PTS2 = BIT(1), > + PTS3 = BIT(2), > + PTS4 = BIT(3), > + DP_NONE = BIT(4) > +}; > + > +enum vic_color_depth { > + BCS_6 = 0x1, > + BCS_8 = 0x2, > + BCS_10 = 0x4, > + BCS_12 = 0x8, > + BCS_16 = 0x10, > +}; > + > +enum vic_bt_type { > + BT_601 = 0x0, > + BT_709 = 0x1, > +}; > + > +enum { > + MODE_DVI, > + MODE_HDMI_1_4, > + MODE_HDMI_2_0, > +}; > + > +struct video_info { > + int bpc; > + int color_fmt; > +}; > + > +struct mhdp8501_plat_data { > + int hdmi_lane_mapping; > + int dp_lane_mapping; > +}; > + > +struct cdns_mhdp8501_device { > + struct cdns_mhdp_base base; > + > + struct device *dev; > + void __iomem *regs; > + struct drm_connector *curr_conn; > + struct drm_bridge bridge; > + struct clk *apb_clk; > + struct phy *phy; > + > + struct video_info video_info; > + struct drm_display_mode mode; > + > + int irq[IRQ_NUM]; > + struct delayed_work hotplug_work; > + int connector_type; > + u32 lane_mapping; > + > + /* protect mailbox communications with the firmware */ > + struct mutex mbox_mutex; > + > + const struct mhdp8501_plat_data *plat_data; > + > + union { > + struct _dp_data { > + u32 rate; > + u8 num_lanes; > + struct drm_dp_aux aux; > + u8 dpcd[DP_RECEIVER_CAP_SIZE]; > + } dp; > + struct _hdmi_data { > + u32 hdmi_type; > + } hdmi; > + }; > +}; > + > +extern const struct drm_bridge_funcs cdns_dp_bridge_funcs; > +extern const struct drm_bridge_funcs cdns_hdmi_bridge_funcs; > + > +ssize_t cdns_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg > *msg); +enum drm_connector_status cdns_mhdp8501_detect(struct > cdns_mhdp8501_device *mhdp); +int cdns_dp_aux_destroy(struct > cdns_mhdp8501_device *mhdp); > + > +#endif > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c new file mode 100644 > index 0000000000000..5576db967cac6 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c > @@ -0,0 +1,708 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Cadence MHDP8501 DisplayPort(DP) bridge driver > + * > + * Copyright (C) 2019-2023 NXP Semiconductor, Inc. > + * > + */ > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_edid.h> > +#include <drm/drm_print.h> > +#include <linux/phy/phy.h> > +#include <linux/phy/phy-dp.h> > + > +#include "cdns-mhdp8501-core.h" > + > +#define LINK_TRAINING_TIMEOUT_MS 500 > +#define LINK_TRAINING_RETRY_MS 20 > + > +ssize_t cdns_dp_aux_transfer(struct drm_dp_aux *aux, > + struct drm_dp_aux_msg *msg) > +{ > + struct cdns_mhdp8501_device *mhdp = dev_get_drvdata(aux->dev); > + bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ); > + int ret; > + > + /* Ignore address only message */ > + if (!msg->size || !msg->buffer) { > + msg->reply = native ? > + DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK; > + return msg->size; > + } > + > + if (!native) { > + dev_err(mhdp->dev, "%s: only native messages supported\n", __func__); > + return -EINVAL; > + } > + > + /* msg sanity check */ > + if (msg->size > DP_AUX_MAX_PAYLOAD_BYTES) { > + dev_err(mhdp->dev, "%s: invalid msg: size(%zu), request(%x)\n", > + __func__, msg->size, (unsigned int)msg- >request); > + return -EINVAL; > + } > + > + if (msg->request == DP_AUX_NATIVE_WRITE) { > + const u8 *buf = msg->buffer; > + int i; > + > + for (i = 0; i < msg->size; ++i) { > + ret = cdns_mhdp_dpcd_write(&mhdp->base, > + msg->address + i, buf[i]); > + if (!ret) > + continue; I personally don't like this style. I would prefer checking for 'if (ret)' to bail out in error case. > + DRM_DEV_ERROR(mhdp->dev, "Failed to write DPCD\n"); Please replace all DRM_ macros, see [1] for details. As you don't have a drm_dev you will most probably need a dev_err & friends. > + > + return ret; > + } > + msg->reply = DP_AUX_NATIVE_REPLY_ACK; > + return msg->size; > + } > + > + if (msg->request == DP_AUX_NATIVE_READ) { > + ret = cdns_mhdp_dpcd_read(&mhdp->base, msg->address, > + msg->buffer, msg->size); > + if (ret < 0) > + return -EIO; Any specific reason to return -EIO instead of ret? You return ret in case of error from cdns_mhdp_dpcd_write as well. > + msg->reply = DP_AUX_NATIVE_REPLY_ACK; > + return msg->size; > + } > + return 0; > +} > + > +int cdns_dp_aux_destroy(struct cdns_mhdp8501_device *mhdp) > +{ > + drm_dp_aux_unregister(&mhdp->dp.aux); > + > + return 0; > +} > + > +static int cdns_dp_get_msa_misc(struct video_info *video, > + struct drm_display_mode *mode) mode is unused. > +{ > + u32 msa_misc; > + u8 val[2] = {0}; Please use two separate variables for color space and bpc. > + > + switch (video->color_fmt) { > + /* set YUV default color space conversion to BT601 */ > + case DRM_COLOR_FORMAT_YCBCR444: > + val[0] = 6 + BT_601 * 8; > + break; > + case DRM_COLOR_FORMAT_YCBCR422: > + val[0] = 5 + BT_601 * 8; > + break; > + case DRM_COLOR_FORMAT_YCBCR420: > + val[0] = 5; > + break; > + case DRM_COLOR_FORMAT_RGB444: > + default: > + val[0] = 0; > + break; > + }; > + > + switch (video->bpc) { > + case 6: > + val[1] = 0; > + break; > + case 10: > + val[1] = 2; > + break; > + case 12: > + val[1] = 3; > + break; > + case 16: > + val[1] = 4; > + break; > + case 8: > + default: > + val[1] = 1; > + break; > + }; > + > + msa_misc = 2 * val[0] + 32 * val[1]; Is this multiplication intended to do bit shifting? > + > + return msa_misc; > +} > + > +static int cdns_dp_config_video(struct cdns_mhdp8501_device *mhdp) > +{ > + struct video_info *video = &mhdp->video_info; > + struct drm_display_mode *mode = &mhdp->mode; > + bool h_sync_polarity, v_sync_polarity; > + u64 symbol; > + u32 val, link_rate, rem; > + u8 bit_per_pix, tu_size_reg = TU_SIZE; > + int ret; > + > + bit_per_pix = (video->color_fmt == DRM_COLOR_FORMAT_YCBCR422) ? > + (video->bpc * 2) : (video->bpc * 3); > + > + link_rate = mhdp->dp.rate / 1000; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, BND_HSYNC2VSYNC, > VIF_BYPASS_INTERLACE); + if (ret) > + goto err_config_video; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, HSYNC2VSYNC_POL_CTRL, 0); > + if (ret) > + goto err_config_video; > + > + /* > + * get a best tu_size and valid symbol: > + * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 > + * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) > + * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set > + * TU += 2 and repeat 2nd step. > + */ > + do { > + tu_size_reg += 2; > + symbol = tu_size_reg * mode->clock * bit_per_pix; > + do_div(symbol, mhdp->dp.num_lanes * link_rate * 8); > + rem = do_div(symbol, 1000); > + if (tu_size_reg > 64) { > + ret = -EINVAL; > + DRM_DEV_ERROR(mhdp->dev, > + "tu error, clk:%d, lanes:%d, rate:%d\n", > + mode->clock, mhdp->dp.num_lanes, link_rate); > + goto err_config_video; > + } > + } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || > + (rem > 850) || (rem < 100)); > + > + val = symbol + (tu_size_reg << 8); > + val |= TU_CNT_RST_EN; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_FRAMER_TU, val); > + if (ret) > + goto err_config_video; > + > + /* set the FIFO Buffer size */ > + val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; > + val /= (mhdp->dp.num_lanes * link_rate); > + val = div_u64(8 * (symbol + 1), bit_per_pix) - val; > + val += 2; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_VC_TABLE(15), val); > + > + switch (video->bpc) { > + case 6: > + val = BCS_6; > + break; > + case 10: > + val = BCS_10; > + break; > + case 12: > + val = BCS_12; > + break; > + case 16: > + val = BCS_16; > + break; > + case 8: > + default: > + val = BCS_8; > + break; > + }; > + > + val += video->color_fmt << 8; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_FRAMER_PXL_REPR, val); > + if (ret) > + goto err_config_video; > + > + v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); > + h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); > + > + val = h_sync_polarity ? DP_FRAMER_SP_HSP : 0; > + val |= v_sync_polarity ? DP_FRAMER_SP_VSP : 0; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_FRAMER_SP, val); > + if (ret) > + goto err_config_video; > + > + val = (mode->hsync_start - mode->hdisplay) << 16; > + val |= mode->htotal - mode->hsync_end; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_FRONT_BACK_PORCH, val); > + if (ret) > + goto err_config_video; > + > + val = mode->hdisplay * bit_per_pix / 8; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_BYTE_COUNT, val); > + if (ret) > + goto err_config_video; > + > + val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_HORIZONTAL_0, val); > + if (ret) > + goto err_config_video; > + > + val = mode->hsync_end - mode->hsync_start; > + val |= (mode->hdisplay << 16) | (h_sync_polarity << 15); > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_HORIZONTAL_1, val); > + if (ret) > + goto err_config_video; > + > + val = mode->vtotal; > + val |= (mode->vtotal - mode->vsync_start) << 16; > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_VERTICAL_0, val); > + if (ret) > + goto err_config_video; > + > + val = mode->vsync_end - mode->vsync_start; > + val |= (mode->vdisplay << 16) | (v_sync_polarity << 15); > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_VERTICAL_1, val); > + if (ret) > + goto err_config_video; > + > + val = cdns_dp_get_msa_misc(video, mode); > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_MISC, val); > + if (ret) > + goto err_config_video; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, STREAM_CONFIG, 1); > + if (ret) > + goto err_config_video; > + > + val = mode->hsync_end - mode->hsync_start; > + val |= mode->hdisplay << 16; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_HORIZONTAL, val); > + if (ret) > + goto err_config_video; > + > + val = mode->vdisplay; > + val |= (mode->vtotal - mode->vsync_start) << 16; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_VERTICAL_0, val); > + if (ret) > + goto err_config_video; > + > + val = mode->vtotal; > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_VERTICAL_1, val); > + if (ret) > + goto err_config_video; > + > + ret = cdns_mhdp_dp_reg_write_bit(&mhdp->base, DP_VB_ID, 2, 1, 0); > + > +err_config_video: > + if (ret) > + DRM_DEV_ERROR(mhdp->dev, "config video failed: %d\n", ret); > + return ret; > +} > + > +static void cdns_dp_pixel_clk_reset(struct cdns_mhdp8501_device *mhdp) > +{ > + u32 val; > + > + /* reset pixel clk */ > + cdns_mhdp_reg_read(&mhdp->base, SOURCE_HDTX_CAR, &val); > + cdns_mhdp_reg_write(&mhdp->base, SOURCE_HDTX_CAR, val & 0xFD); > + cdns_mhdp_reg_write(&mhdp->base, SOURCE_HDTX_CAR, val); > +} > + > +static int cdns_dp_set_video_status(struct cdns_mhdp8501_device *mhdp, int > active) +{ > + u8 msg; > + int ret; > + > + msg = !!active; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > DPTX_SET_VIDEO, + sizeof(msg), &msg); > + if (ret) > + DRM_DEV_ERROR(mhdp->dev, "set video status failed: %d\n", ret); > + > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +static int cdns_dp_training_start(struct cdns_mhdp8501_device *mhdp) > +{ > + unsigned long timeout; > + u8 msg, event[2]; > + int ret; > + > + msg = LINK_TRAINING_RUN; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + /* start training */ > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_TRAINING_CONTROL, sizeof(msg), &msg); > + if (ret) > + goto err_training_start; > + > + timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); > + while (time_before(jiffies, timeout)) { > + msleep(LINK_TRAINING_RETRY_MS); > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_READ_EVENT, 0, NULL); > + if (ret) > + goto err_training_start; > + > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_READ_EVENT, sizeof(event)); > + if (ret) > + goto err_training_start; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, event, sizeof(event)); > + if (ret) > + goto err_training_start; > + > + if (event[1] & CLK_RECOVERY_FAILED) { > + DRM_DEV_ERROR(mhdp->dev, "clock recovery failed\n"); > + } else if (event[1] & EQ_PHASE_FINISHED) { > + mutex_unlock(&mhdp->mbox_mutex); > + return 0; > + } > + } > + > + ret = -ETIMEDOUT; > + > +err_training_start: > + mutex_unlock(&mhdp->mbox_mutex); > + > + DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret); > + return ret; > +} > + > +static int cdns_dp_get_training_status(struct cdns_mhdp8501_device *mhdp) > +{ > + u8 status[13]; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_READ_LINK_STAT, 0, NULL); > + if (ret) > + goto err_get_training_status; > + > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_READ_LINK_STAT, > + sizeof(status)); > + if (ret) > + goto err_get_training_status; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, status, sizeof(status)); > + if (ret) > + goto err_get_training_status; > + > + mhdp->dp.rate = drm_dp_bw_code_to_link_rate(status[0]); > + mhdp->dp.num_lanes = status[1]; > + > +err_get_training_status: > + mutex_unlock(&mhdp->mbox_mutex); > + > + if (ret) > + DRM_DEV_ERROR(mhdp->dev, "get training status failed: %d\n", > + ret); > + return ret; > +} > + > +static int cdns_dp_train_link(struct cdns_mhdp8501_device *mhdp) > +{ > + int ret; > + > + ret = cdns_dp_training_start(mhdp); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed to start training %d\n", > + ret); > + return ret; > + } > + > + ret = cdns_dp_get_training_status(mhdp); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat %d\n", > + ret); > + return ret; > + } > + > + DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", mhdp->dp.rate, > + mhdp->dp.num_lanes); > + return ret; > +} > + > +int cdns_dp_set_host_cap(struct cdns_mhdp8501_device *mhdp) > +{ > + u8 msg[8]; > + int ret; > + > + msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate); > + msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN; > + msg[2] = VOLTAGE_LEVEL_2; > + msg[3] = PRE_EMPHASIS_LEVEL_3; > + msg[4] = PTS1 | PTS2 | PTS3 | PTS4; > + msg[5] = FAST_LT_NOT_SUPPORT; > + msg[6] = mhdp->lane_mapping; > + msg[7] = ENHANCED; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_SET_HOST_CAPABILITIES, > + sizeof(msg), msg); > + > + mutex_unlock(&mhdp->mbox_mutex); > + > + if (ret) > + DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret); > + > + return ret; > +} > + > +static int cdns_dp_get_edid_block(void *data, u8 *edid, > + unsigned int block, size_t length) > +{ > + struct cdns_mhdp8501_device *mhdp = data; > + u8 msg[2], reg[2], i; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + for (i = 0; i < 4; i++) { > + msg[0] = block / 2; > + msg[1] = block % 2; > + > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_GET_EDID, sizeof(msg), msg); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_DP_TX, > + DPTX_GET_EDID, > + sizeof(reg) + length); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, reg, sizeof(reg)); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, edid, length); > + if (ret) > + continue; > + > + if (reg[0] == length && reg[1] == block / 2) > + break; > + } > + > + if (ret) > + DRM_DEV_ERROR(mhdp->dev, "get block[%d] edid failed: %d\n", > + block, ret); > + > + mutex_unlock(&mhdp->mbox_mutex); > + return ret; > +} > + > +static void cdns_dp_mode_set(struct cdns_mhdp8501_device *mhdp) > +{ > + union phy_configure_opts phy_cfg; > + int ret; > + > + cdns_dp_pixel_clk_reset(mhdp); > + > + /* Get DP Caps */ > + ret = drm_dp_dpcd_read(&mhdp->dp.aux, DP_DPCD_REV, mhdp->dp.dpcd, > + DP_RECEIVER_CAP_SIZE); > + if (ret < 0) { > + DRM_ERROR("Failed to get caps %d\n", ret); > + return; > + } > + > + mhdp->dp.rate = drm_dp_max_link_rate(mhdp->dp.dpcd); > + mhdp->dp.num_lanes = drm_dp_max_lane_count(mhdp->dp.dpcd); > + > + /* check the max link rate */ > + if (mhdp->dp.rate > CDNS_DP_MAX_LINK_RATE) > + mhdp->dp.rate = CDNS_DP_MAX_LINK_RATE; > + > + phy_cfg.dp.lanes = mhdp->dp.num_lanes; > + phy_cfg.dp.link_rate = mhdp->dp.rate; > + phy_cfg.dp.set_lanes = false; > + phy_cfg.dp.set_rate = false; > + phy_cfg.dp.set_voltages = true; > + > + /* Mailbox protect for DP PHY access */ > + mutex_lock(&mhdp->mbox_mutex); > + ret = phy_configure(mhdp->phy, &phy_cfg); > + mutex_unlock(&mhdp->mbox_mutex); > + if (ret) { > + dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n", > + __func__, ret); > + return; > + } > + > + /* Video off */ > + ret = cdns_dp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret); > + return; > + } > + > + /* Line swapping */ > + cdns_mhdp_reg_write(&mhdp->base, LANES_CONFIG, 0x00400000 | > mhdp->lane_mapping); + > + /* Set DP host capability */ > + ret = cdns_dp_set_host_cap(mhdp); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed to set host cap %d\n", ret); > + return; > + } > + > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_AUX_SWAP_INVERSION_CONTROL, > + AUX_HOST_INVERT); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed to set host invert %d\n", ret); > + return; > + } > + > + ret = cdns_dp_config_video(mhdp); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed to config video %d\n", ret); > + return; > + } > +} > + > +static int cdns_dp_bridge_attach(struct drm_bridge *bridge, > + enum drm_bridge_attach_flags flags) > +{ > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > + > + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { > + DRM_ERROR("do not support creating a drm_connector\n"); > + return -EINVAL; > + } > + > + mhdp->dp.aux.drm_dev = bridge->dev; > + > + return drm_dp_aux_register(&mhdp->dp.aux); > +} > + > +static enum drm_mode_status > +cdns_dp_bridge_mode_valid(struct drm_bridge *bridge, > + const struct drm_display_info *info, > + const struct drm_display_mode *mode) > +{ > + enum drm_mode_status mode_status = MODE_OK; > + > + /* We don't support double-clocked 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) Comment does not match code. > + return MODE_BAD_HVALUE; > + > + if (mode->vdisplay > 2160) > + return MODE_BAD_VVALUE; > + > + return mode_status; > +} > + > +static enum drm_connector_status > +cdns_dp_bridge_detect(struct drm_bridge *bridge) > +{ > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > + > + return cdns_mhdp8501_detect(mhdp); > +} > + > +static struct edid *cdns_dp_bridge_get_edid(struct drm_bridge *bridge, > + struct drm_connector *connector) > +{ > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > + > + return drm_do_get_edid(connector, cdns_dp_get_edid_block, mhdp); > +} > + > +static void cdns_dp_bridge_atomic_disable(struct drm_bridge *bridge, > + struct drm_bridge_state *old_state) > +{ > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > + > + cdns_dp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); > + mhdp->curr_conn = NULL; > + > + /* Mailbox protect for DP PHY access */ > + mutex_lock(&mhdp->mbox_mutex); > + phy_power_off(mhdp->phy); > + mutex_unlock(&mhdp->mbox_mutex); > +} > + > +static void cdns_dp_bridge_atomic_enable(struct drm_bridge *bridge, > + struct drm_bridge_state *old_state) > +{ > + struct cdns_mhdp8501_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; > + int ret; > + > + 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; > + > + switch (connector->display_info.bpc) { > + case 10: > + video->bpc = 10; > + break; > + case 6: > + video->bpc = 6; > + break; > + default: > + video->bpc = 8; > + break; > + } > + > + /* The only currently supported format */ > + video->color_fmt = DRM_COLOR_FORMAT_RGB444; > + > + DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode- >clock); > + memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); > + > + cdns_dp_mode_set(mhdp); > + > + /* Link trainning */ > + ret = cdns_dp_train_link(mhdp); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed link train %d\n", ret); > + return; > + } > + > + ret = cdns_dp_set_video_status(mhdp, CONTROL_VIDEO_VALID); > + if (ret) { > + DRM_DEV_ERROR(mhdp->dev, "Failed to valid video %d\n", ret); > + return; > + } > +} > + > +const struct drm_bridge_funcs cdns_dp_bridge_funcs = { > + .attach = cdns_dp_bridge_attach, > + .mode_valid = cdns_dp_bridge_mode_valid, > + .detect = cdns_dp_bridge_detect, > + .get_edid = cdns_dp_bridge_get_edid, > + .atomic_enable = cdns_dp_bridge_atomic_enable, > + .atomic_disable = cdns_dp_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, Can you please sort the entries in the same order as cdns_hdmi_bridge_funcs? This makes it easier to detect which entries are provided in both files. > +}; > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c new file mode 100644 > index 0000000000000..73d1c35a74599 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c > @@ -0,0 +1,673 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Cadence MHDP8501 HDMI bridge driver > + * > + * Copyright (C) 2019-2023 NXP Semiconductor, Inc. > + * > + */ > +#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_print.h> > +#include <linux/phy/phy.h> > +#include <linux/phy/phy-hdmi.h> > + > +#include "cdns-mhdp8501-core.h" > + > +/** > + * cdns_hdmi_infoframe_set() - fill the HDMI AVI infoframe > + * @mhdp: phandle to mhdp device. > + * @entry_id: The packet memory address in which the data is written. > + * @packet_len: 32, only 32 bytes now. > + * @packet: point to InfoFrame Packet. > + * packet[0] = 0 > + * packet[1-3] = HB[0-2] InfoFrame Packet Header > + * packet[4-31 = PB[0-27] InfoFrame Packet Contents > + * @packet_type: Packet Type of InfoFrame in HDMI Specification. > + * > + */ > +static void cdns_hdmi_infoframe_set(struct cdns_mhdp8501_device *mhdp, > + u8 entry_id, u8 packet_len, > + u8 *packet, u8 packet_type) > +{ > + u32 packet32, len32; > + u32 val, i; > + > + /* only support 32 bytes now */ > + if (packet_len != 32) > + return; > + > + /* 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 */ > + len32 = packet_len / 4; > + for (i = 0; i < len32; i++) { > + packet32 = get_unaligned_le32(packet + 4 * i); > + writel(F_DATA_WR(packet32), 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_mhdp8501_device *mhdp = data; > + u8 msg[2], reg[5], i; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + > + for (i = 0; i < 4; i++) { > + msg[0] = block / 2; > + msg[1] = block % 2; > + > + ret = cdns_mhdp_mailbox_send(&mhdp->base, MB_MODULE_ID_HDMI_TX, > HDMI_TX_EDID, + sizeof(msg), msg); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_HDMI_TX, > + HDMI_TX_EDID, sizeof(reg) + length); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, reg, sizeof(reg)); > + if (ret) > + continue; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, 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); Please replace all DRM_ macros, see [1] for details. As you don't have a drm_dev you will most probably need a dev_err & friends. [1] https://lore.kernel.org/linux-arm-kernel/285db5bc-f901-e09f-7f86-6638d260c283@linaro.org/T/#ma30715ccd9004ad19a6741c3f6b3dfd68d526018 > + return ret; > +} > + > +static int cdns_hdmi_scdc_write(struct cdns_mhdp8501_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->base, MB_MODULE_ID_HDMI_TX, > HDMI_TX_WRITE, + sizeof(msg), msg); > + if (ret) > + goto err_scdc_write; > + > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, MB_MODULE_ID_HDMI_TX, > + HDMI_TX_WRITE, sizeof(reg)); > + if (ret) > + goto err_scdc_write; > + > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, 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; > +} > + > +static int cdns_hdmi_ctrl_init(struct cdns_mhdp8501_device *mhdp, int > protocol) +{ > + u32 reg0, reg1, val; > + int ret; > + > + /* Set PHY to HDMI data */ > + ret = cdns_mhdp_reg_write(&mhdp->base, PHY_DATA_SEL, > F_SOURCE_PHY_MHDP_SEL(1)); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, 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->base, SOURCE_PHY_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_HDTX_CAR, 0xFF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_PKT_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_AIF_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_CIPHER_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_CRYPTO_CAR, 0xF); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_CEC_CAR, 3); > + if (ret < 0) > + return ret; > + > + reg0 = 0x7c1f; > + reg1 = 0x7c1f; > + if (protocol == MODE_HDMI_2_0) { > + reg0 = 0; > + reg1 = 0xFFFFF; > + } > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_CLOCK_REG_0, reg0); > + if (ret < 0) > + return ret; > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_CLOCK_REG_1, reg1); > + if (ret < 0) > + return ret; > + > + /* set hdmi mode and preemble mode data enable */ Please stick to consistent uppercase when naming HDMI. > + 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->base, HDTX_CONTROLLER, val); > + > + return ret; > +} > + > +static int cdns_hdmi_mode_config(struct cdns_mhdp8501_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); > + > + ret = cdns_mhdp_reg_write(&mhdp->base, SCHEDULER_H_SIZE, (hactive << 16) + > hblank); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, SCHEDULER_V_SIZE, (vactive << 16) + > vblank); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_SIGNAL_FRONT_WIDTH, (vfront << > 16) + hfront); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_SIGNAL_SYNC_WIDTH, (vsync << > 16) + hsync); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_SIGNAL_BACK_WIDTH, (vback << > 16) + hback); + if (ret < 0) > + return ret; > + > + ret = cdns_mhdp_reg_write(&mhdp->base, HSYNC2VSYNC_POL_CTRL, > v_h_polarity); + if (ret < 0) > + return ret; > + > + /* Reset Data Enable */ > + cdns_mhdp_reg_read(&mhdp->base, HDTX_CONTROLLER, &val); > + val &= ~F_DATA_EN(1); > + ret = cdns_mhdp_reg_write(&mhdp->base, 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->base, HDTX_CONTROLLER, val); > + if (ret < 0) > + return ret; > + > + /* set data enable */ > + val |= F_DATA_EN(1); > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_CONTROLLER, val); > + > + return ret; > +} > + > +static int cdns_hdmi_disable_gcp(struct cdns_mhdp8501_device *mhdp) > +{ > + u32 val; > + > + cdns_mhdp_reg_read(&mhdp->base, HDTX_CONTROLLER, &val); > + val &= ~F_GCP_EN(1); > + > + return cdns_mhdp_reg_write(&mhdp->base, HDTX_CONTROLLER, val); > +} > + > +static int cdns_hdmi_enable_gcp(struct cdns_mhdp8501_device *mhdp) > +{ > + u32 val; > + > + cdns_mhdp_reg_read(&mhdp->base, HDTX_CONTROLLER, &val); > + val |= F_GCP_EN(1); > + > + return cdns_mhdp_reg_write(&mhdp->base, HDTX_CONTROLLER, val); > +} > + > +static void cdns_hdmi_sink_config(struct cdns_mhdp8501_device *mhdp) > +{ > + struct drm_scdc *scdc = &mhdp->curr_conn->display_info.hdmi.scdc; > + u32 char_rate = mhdp->mode.clock * mhdp->video_info.bpc / 8; > + 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) { > + DRM_INFO("Sink Not Support SCDC\n"); > + return; > + } > + > + if (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 cdns_hdmi_lanes_config(struct cdns_mhdp8501_device *mhdp) > +{ > + /* Line swapping */ > + cdns_mhdp_reg_write(&mhdp->base, LANES_CONFIG, 0x00400000 | > mhdp->lane_mapping); +} > + > +static int cdns_hdmi_colorspace(int color_fmt) > +{ > + int color_space; > + > + switch (color_fmt) { > + case DRM_COLOR_FORMAT_YCBCR444: > + color_space = HDMI_COLORSPACE_YUV444; > + break; > + case DRM_COLOR_FORMAT_YCBCR422: > + color_space = HDMI_COLORSPACE_YUV422; > + break; > + case DRM_COLOR_FORMAT_YCBCR420: > + color_space = HDMI_COLORSPACE_YUV420; > + break; > + case DRM_COLOR_FORMAT_RGB444: > + default: > + color_space = HDMI_COLORSPACE_RGB; > + break; > + } > + > + return color_space; > +} > + > +static int cdns_hdmi_avi_info_set(struct cdns_mhdp8501_device *mhdp, > + struct drm_display_mode *mode) > +{ > + struct hdmi_avi_infoframe frame; > + 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); > + > + frame.colorspace = cdns_hdmi_colorspace(mhdp->video_info.color_fmt); > + > + 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_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_hdmi_infoframe_set(mhdp, 0, sizeof(buf), buf, > HDMI_INFOFRAME_TYPE_AVI); + > + return 0; > +} > + > +static void cdns_hdmi_vendor_info_set(struct cdns_mhdp8501_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_hdmi_infoframe_set(mhdp, 3, sizeof(buf), buf, > HDMI_INFOFRAME_TYPE_VENDOR); +} > + > +static void cdns_hdmi_drm_info_set(struct cdns_mhdp8501_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_hdmi_infoframe_set(mhdp, 3, sizeof(buf), buf, > HDMI_INFOFRAME_TYPE_DRM); +} > + > +static void cdns_hdmi_mode_set(struct cdns_mhdp8501_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; > + > + cdns_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 = > cdns_hdmi_colorspace(mhdp->video_info.color_fmt); + > + /* Mailbox protect for HDMI PHY access */ > + mutex_lock(&mhdp->mbox_mutex); > + ret = phy_configure(mhdp->phy, &phy_cfg); > + mutex_unlock(&mhdp->mbox_mutex); > + if (ret) { > + dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n", > + __func__, ret); > + return; > + } > + > + cdns_hdmi_sink_config(mhdp); > + > + ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type); > + 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 = cdns_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 */ > + cdns_hdmi_vendor_info_set(mhdp, mode); > + > + cdns_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 int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, > + enum drm_bridge_attach_flags flags) > +{ > + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { > + DRM_ERROR("do not support creating a drm_connector\n"); > + return -EINVAL; > + } > + > + 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_mhdp8501_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) Comment does not match code. Despite that separately check for vdisplay and return MODE_BAD_VVALUE if it's too big. Best regards, Alexander > + return MODE_BAD_HVALUE; > + > + /* Check modes supported by PHY */ > + phy_cfg.hdmi.pixel_clk_rate = mode->clock; > + > + /* Mailbox protect for HDMI PHY access */ > + mutex_lock(&mhdp->mbox_mutex); > + ret = phy_validate(mhdp->phy, PHY_MODE_HDMI, 0, &phy_cfg); > + mutex_unlock(&mhdp->mbox_mutex); > + 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_mhdp8501_device *mhdp = bridge->driver_private; > + struct video_info *video = &mhdp->video_info; > + > + /* The only currently supported format */ > + video->bpc = 8; > + video->color_fmt = DRM_COLOR_FORMAT_RGB444; > + > + return true; > +} > + > +static enum drm_connector_status > +cdns_hdmi_bridge_detect(struct drm_bridge *bridge) > +{ > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > + > + return cdns_mhdp8501_detect(mhdp); > +} > + > +static struct edid *cdns_hdmi_bridge_get_edid(struct drm_bridge *bridge, > + struct drm_connector *connector) > +{ > + struct cdns_mhdp8501_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_mhdp8501_device *mhdp = bridge->driver_private; > + > + mhdp->curr_conn = NULL; > + > + /* Mailbox protect for HDMI PHY access */ > + mutex_lock(&mhdp->mbox_mutex); > + phy_power_off(mhdp->phy); > + mutex_unlock(&mhdp->mbox_mutex); > +} > + > +static void cdns_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, > + struct drm_bridge_state *old_state) > +{ > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > + struct drm_atomic_state *state = old_state->base.state; > + struct drm_connector *connector; > + 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)); > + > + cdns_hdmi_mode_set(mhdp); > +} > + > +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, > +};
Hi Sandor, thanks for the patch. Am Dienstag, 17. Oktober 2023, 09:04:02 CEST schrieb Sandor Yu: > Add Cadence HDP-TX DisplayPort PHY driver for i.MX8MQ > > Cadence HDP-TX PHY could be put in either DP mode or > HDMI mode base on the configuration chosen. > DisplayPort PHY mode is configurated in the driver. > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > --- > v9->v11: > *No change. > > drivers/phy/freescale/Kconfig | 10 + > drivers/phy/freescale/Makefile | 1 + > drivers/phy/freescale/phy-fsl-imx8mq-dp.c | 720 ++++++++++++++++++++++ > 3 files changed, 731 insertions(+) > create mode 100644 drivers/phy/freescale/phy-fsl-imx8mq-dp.c > > diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig > index 853958fb2c063..c39709fd700ac 100644 > --- a/drivers/phy/freescale/Kconfig > +++ b/drivers/phy/freescale/Kconfig > @@ -35,6 +35,16 @@ config PHY_FSL_IMX8M_PCIE > Enable this to add support for the PCIE PHY as found on > i.MX8M family of SOCs. > > +config PHY_FSL_IMX8MQ_DP > + tristate "Freescale i.MX8MQ DP PHY support" > + depends on OF && HAS_IOMEM > + depends on COMMON_CLK > + select GENERIC_PHY > + select CDNS_MHDP_HELPER > + help > + Enable this to support the Cadence HDPTX DP PHY driver > + on i.MX8MQ SOC. > + > endif > > config PHY_FSL_LYNX_28G > diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile > index cedb328bc4d28..47e5285209fa8 100644 > --- a/drivers/phy/freescale/Makefile > +++ b/drivers/phy/freescale/Makefile > @@ -1,4 +1,5 @@ > # SPDX-License-Identifier: GPL-2.0-only > +obj-$(CONFIG_PHY_FSL_IMX8MQ_DP) += phy-fsl-imx8mq-dp.o > obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o > obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o > obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o > diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-dp.c > b/drivers/phy/freescale/phy-fsl-imx8mq-dp.c new file mode 100644 > index 0000000000000..5f0d7da16b422 > --- /dev/null > +++ b/drivers/phy/freescale/phy-fsl-imx8mq-dp.c > @@ -0,0 +1,720 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Cadence HDP-TX Display Port Interface (DP) PHY driver > + * > + * Copyright (C) 2022, 2023 NXP Semiconductor, Inc. > + */ > +#include <asm/unaligned.h> > +#include <drm/bridge/cdns-mhdp-helper.h> > +#include <linux/clk.h> > +#include <linux/kernel.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/io.h> > +#include <linux/iopoll.h> > + > +#define ADDR_PHY_AFE 0x80000 > + > +/* PHY registers */ > +#define CMN_SSM_BIAS_TMR 0x0022 > +#define CMN_PLLSM0_PLLEN_TMR 0x0029 > +#define CMN_PLLSM0_PLLPRE_TMR 0x002a > +#define CMN_PLLSM0_PLLVREF_TMR 0x002b > +#define CMN_PLLSM0_PLLLOCK_TMR 0x002c > +#define CMN_PLLSM0_USER_DEF_CTRL 0x002f > +#define CMN_PSM_CLK_CTRL 0x0061 > +#define CMN_PLL0_VCOCAL_START 0x0081 > +#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084 > +#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085 > +#define CMN_PLL0_INTDIV 0x0094 > +#define CMN_PLL0_FRACDIV 0x0095 > +#define CMN_PLL0_HIGH_THR 0x0096 > +#define CMN_PLL0_DSM_DIAG 0x0097 > +#define CMN_PLL0_SS_CTRL2 0x0099 > +#define CMN_ICAL_INIT_TMR 0x00c4 > +#define CMN_ICAL_ITER_TMR 0x00c5 > +#define CMN_RXCAL_INIT_TMR 0x00d4 > +#define CMN_RXCAL_ITER_TMR 0x00d5 > +#define CMN_TXPUCAL_INIT_TMR 0x00e4 > +#define CMN_TXPUCAL_ITER_TMR 0x00e5 > +#define CMN_TXPDCAL_INIT_TMR 0x00f4 > +#define CMN_TXPDCAL_ITER_TMR 0x00f5 > +#define CMN_ICAL_ADJ_INIT_TMR 0x0102 > +#define CMN_ICAL_ADJ_ITER_TMR 0x0103 > +#define CMN_RX_ADJ_INIT_TMR 0x0106 > +#define CMN_RX_ADJ_ITER_TMR 0x0107 > +#define CMN_TXPU_ADJ_INIT_TMR 0x010a > +#define CMN_TXPU_ADJ_ITER_TMR 0x010b > +#define CMN_TXPD_ADJ_INIT_TMR 0x010e > +#define CMN_TXPD_ADJ_ITER_TMR 0x010f > +#define CMN_DIAG_PLL0_FBH_OVRD 0x01c0 > +#define CMN_DIAG_PLL0_FBL_OVRD 0x01c1 > +#define CMN_DIAG_PLL0_OVRD 0x01c2 > +#define CMN_DIAG_PLL0_TEST_MODE 0x01c4 > +#define CMN_DIAG_PLL0_V2I_TUNE 0x01c5 > +#define CMN_DIAG_PLL0_CP_TUNE 0x01c6 > +#define CMN_DIAG_PLL0_LF_PROG 0x01c7 > +#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01c8 > +#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01c9 > +#define CMN_DIAG_HSCLK_SEL 0x01e0 > +#define CMN_DIAG_PER_CAL_ADJ 0x01ec > +#define CMN_DIAG_CAL_CTRL 0x01ed > +#define CMN_DIAG_ACYA 0x01ff > +#define XCVR_PSM_RCTRL 0x4001 > +#define XCVR_PSM_CAL_TMR 0x4002 > +#define XCVR_PSM_A0IN_TMR 0x4003 > +#define TX_TXCC_CAL_SCLR_MULT_0 0x4047 > +#define TX_TXCC_CPOST_MULT_00_0 0x404c > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > +#define XCVR_DIAG_HSCLK_SEL 0x40e1 > +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2 > +#define TX_PSC_A0 0x4100 > +#define TX_PSC_A1 0x4101 > +#define TX_PSC_A2 0x4102 > +#define TX_PSC_A3 0x4103 > +#define TX_RCVDET_EN_TMR 0x4122 > +#define TX_RCVDET_ST_TMR 0x4123 > +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 > +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 > +#define TX_DIAG_ACYA_0 0x41ff > +#define TX_DIAG_ACYA_1 0x43ff > +#define TX_DIAG_ACYA_2 0x45ff > +#define TX_DIAG_ACYA_3 0x47ff > +#define TX_ANA_CTRL_REG_1 0x5020 > +#define TX_ANA_CTRL_REG_2 0x5021 > +#define TX_DIG_CTRL_REG_1 0x5023 > +#define TX_DIG_CTRL_REG_2 0x5024 > +#define TXDA_CYA_AUXDA_CYA 0x5025 > +#define TX_ANA_CTRL_REG_3 0x5026 > +#define TX_ANA_CTRL_REG_4 0x5027 > +#define TX_ANA_CTRL_REG_5 0x5029 > +#define RX_PSC_A0 0x8000 > +#define RX_PSC_CAL 0x8006 > +#define PHY_HDP_MODE_CTRL 0xc008 > +#define PHY_HDP_CLK_CTL 0xc009 > +#define PHY_PMA_CMN_CTRL1 0xc800 > + > +/* PHY_PMA_CMN_CTRL1 */ > +#define CMA_REF_CLK_SEL_MASK GENMASK(6, 4) > +#define CMA_REF_CLK_RCV_EN_MASK BIT(3) > +#define CMA_REF_CLK_RCV_EN 1 > + > +/* PHY_HDP_CLK_CTL */ > +#define PLL_DATA_RATE_CLK_DIV_MASK GENMASK(15, 8) > +#define PLL_DATA_RATE_CLK_DIV_HBR 0x24 > +#define PLL_DATA_RATE_CLK_DIV_HBR2 0x12 > +#define PLL_CLK_EN_ACK BIT(3) > +#define PLL_CLK_EN BIT(2) > +#define PLL_READY BIT(1) > +#define PLL_EN BIT(0) > + > +/* CMN_DIAG_HSCLK_SEL */ > +#define HSCLK1_SEL_MASK GENMASK(5, 4) > +#define HSCLK0_SEL_MASK GENMASK(1, 0) > +#define HSCLK_PLL0_DIV2 1 > + > +/* XCVR_DIAG_HSCLK_SEL */ > +#define HSCLK_SEL_MODE3_MASK GENMASK(13, 12) > +#define HSCLK_SEL_MODE3_HSCLK1 1 > + > +/* XCVR_DIAG_PLLDRC_CTRL */ > +#define DPLL_CLK_SEL_MODE3 BIT(14) > +#define DPLL_DATA_RATE_DIV_MODE3_MASK GENMASK(13, 12) > + > +/* PHY_HDP_MODE_CTRL */ > +#define POWER_STATE_A3_ACK BIT(7) > +#define POWER_STATE_A2_ACK BIT(6) > +#define POWER_STATE_A1_ACK BIT(5) > +#define POWER_STATE_A0_ACK BIT(4) > +#define POWER_STATE_A3 BIT(3) > +#define POWER_STATE_A2 BIT(2) > +#define POWER_STATE_A1 BIT(1) > +#define POWER_STATE_A0 BIT(0) > + > +#define REF_CLK_27MHZ 27000000 > + > +enum dp_link_rate { > + RATE_1_6 = 162000, > + RATE_2_1 = 216000, > + RATE_2_4 = 243000, > + RATE_2_7 = 270000, > + RATE_3_2 = 324000, > + RATE_4_3 = 432000, > + RATE_5_4 = 540000, > + RATE_8_1 = 810000, RATE_8_1 is unused. > +}; > + > +#define MAX_LINK_RATE RATE_5_4 > + > +struct phy_pll_reg { > + u16 val[7]; > + u32 addr; > +}; > + > +static const struct phy_pll_reg phy_pll_27m_cfg[] = { > + /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 register > address */ + {{ 0x010e, 0x010e, 0x010e, 0x010e, 0x010e, 0x010e, 0x010e }, > CMN_PLL0_VCOCAL_INIT_TMR }, + {{ 0x001b, 0x001b, 0x001b, 0x001b, 0x001b, > 0x001b, 0x001b }, CMN_PLL0_VCOCAL_ITER_TMR }, + {{ 0x30b9, 0x3087, 0x3096, > 0x30b4, 0x30b9, 0x3087, 0x30b4 }, CMN_PLL0_VCOCAL_START }, + {{ 0x0077, > 0x009f, 0x00b3, 0x00c7, 0x0077, 0x009f, 0x00c7 }, CMN_PLL0_INTDIV }, + {{ > 0xf9da, 0xf7cd, 0xf6c7, 0xf5c1, 0xf9da, 0xf7cd, 0xf5c1 }, CMN_PLL0_FRACDIV > }, + {{ 0x001e, 0x0028, 0x002d, 0x0032, 0x001e, 0x0028, 0x0032 }, > CMN_PLL0_HIGH_THR }, + {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, > 0x0020 }, CMN_PLL0_DSM_DIAG }, + {{ 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, > 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL }, + {{ 0x0000, 0x0000, 0x0000, > 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD }, + {{ 0x0000, > 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBH_OVRD }, > + {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, > CMN_DIAG_PLL0_FBL_OVRD }, + {{ 0x0006, 0x0007, 0x0007, 0x0007, 0x0006, > 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE }, + {{ 0x0043, 0x0043, 0x0043, > 0x0042, 0x0043, 0x0043, 0x0042 }, CMN_DIAG_PLL0_CP_TUNE }, + {{ 0x0008, > 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, CMN_DIAG_PLL0_LF_PROG }, > + {{ 0x0100, 0x0001, 0x0001, 0x0001, 0x0100, 0x0001, 0x0001 }, > CMN_DIAG_PLL0_PTATIS_TUNE1 }, + {{ 0x0007, 0x0001, 0x0001, 0x0001, 0x0007, > 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE2 }, + {{ 0x0020, 0x0020, > 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_DIAG_PLL0_TEST_MODE}, + {{ > 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, CMN_PSM_CLK_CTRL > } +}; > + > +struct cdns_hdptx_dp_phy { > + struct cdns_mhdp_base base; > + > + void __iomem *regs; /* DPTX registers base */ > + struct device *dev; > + struct phy *phy; > + struct mutex mbox_mutex; /* mutex to protect mailbox */ > + struct clk *ref_clk, *apb_clk; > + u32 ref_clk_rate; > + u32 num_lanes; > + u32 link_rate; > + bool power_up; > +}; > + > +static int cdns_phy_reg_write(struct cdns_hdptx_dp_phy *cdns_phy, u32 addr, > u32 val) +{ > + return cdns_mhdp_reg_write(&cdns_phy->base, ADDR_PHY_AFE + (addr << 2), > val); +} > + > +static u32 cdns_phy_reg_read(struct cdns_hdptx_dp_phy *cdns_phy, u32 addr) > +{ > + u32 reg32; > + > + cdns_mhdp_reg_read(&cdns_phy->base, ADDR_PHY_AFE + (addr << 2), ®32); > + return reg32; > +} > + > +static int link_rate_index(u32 rate) > +{ > + switch (rate) { > + case RATE_1_6: > + return 0; > + case RATE_2_1: > + return 1; > + case RATE_2_4: > + return 2; > + case RATE_2_7: > + return 3; > + case RATE_3_2: > + return 4; > + case RATE_4_3: > + return 5; > + case RATE_5_4: > + return 6; > + default: > + return -1; > + } > +} > + > +static int hdptx_dp_clk_enable(struct cdns_hdptx_dp_phy *cdns_phy) > +{ > + struct device *dev = cdns_phy->dev; > + u32 ref_clk_rate; > + int ret; > + > + cdns_phy->ref_clk = devm_clk_get(dev, "ref"); > + if (IS_ERR(cdns_phy->ref_clk)) { > + dev_err(dev, "phy ref clock not found\n"); > + return PTR_ERR(cdns_phy->ref_clk); > + } > + > + cdns_phy->apb_clk = devm_clk_get(dev, "apb"); > + if (IS_ERR(cdns_phy->apb_clk)) { > + dev_err(dev, "phy apb clock not found\n"); > + return PTR_ERR(cdns_phy->apb_clk); > + } > + > + ret = clk_prepare_enable(cdns_phy->ref_clk); > + if (ret) { > + dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); > + return ret; > + } > + > + ref_clk_rate = clk_get_rate(cdns_phy->ref_clk); > + if (!ref_clk_rate) { > + dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); > + goto err_ref_clk; > + } > + > + if (ref_clk_rate == REF_CLK_27MHZ) { > + cdns_phy->ref_clk_rate = ref_clk_rate; > + } else { > + dev_err(cdns_phy->dev, "Not support Ref Clock Rate(%dHz) \n", > ref_clk_rate); + goto err_ref_clk; > + } > + > + ret = clk_prepare_enable(cdns_phy->apb_clk); > + if (ret) { > + dev_err(cdns_phy->dev, "Failed to prepare apb clock\n"); > + goto err_ref_clk; > + } > + > + return 0; > + > +err_ref_clk: > + clk_disable_unprepare(cdns_phy->ref_clk); > + return -EINVAL; > +} > + > +static void hdptx_dp_clk_disable(struct cdns_hdptx_dp_phy *cdns_phy) > +{ > + clk_disable_unprepare(cdns_phy->ref_clk); > + clk_disable_unprepare(cdns_phy->apb_clk); > +} > + > +static void hdptx_dp_aux_cfg(struct cdns_hdptx_dp_phy *cdns_phy) > +{ > + /* Power up Aux */ > + cdns_phy_reg_write(cdns_phy, TXDA_CYA_AUXDA_CYA, 1); > + > + cdns_phy_reg_write(cdns_phy, TX_DIG_CTRL_REG_1, 0x3); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_DIG_CTRL_REG_2, 36); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0100); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0300); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_3, 0x0000); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2008); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2018); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0xa018); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030c); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_5, 0x0000); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_4, 0x1001); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0xa098); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0xa198); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030d); > + ndelay(150); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030f); > +} > + > +/* PMA common configuration for 27MHz */ > +static void hdptx_dp_phy_pma_cmn_cfg_27mhz(struct cdns_hdptx_dp_phy > *cdns_phy) +{ > + u32 num_lanes = cdns_phy->num_lanes; > + u16 val; > + int k; > + > + /* Enable PMA input ref clk(CMN_REF_CLK_RCV_EN) */ > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > + val &= ~CMA_REF_CLK_RCV_EN_MASK; > + val |= FIELD_PREP(CMA_REF_CLK_RCV_EN_MASK, CMA_REF_CLK_RCV_EN); > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); > + > + /* Startup state machine registers */ > + cdns_phy_reg_write(cdns_phy, CMN_SSM_BIAS_TMR, 0x0087); > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLEN_TMR, 0x001b); > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLPRE_TMR, 0x0036); > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLVREF_TMR, 0x001b); > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLLOCK_TMR, 0x006c); > + > + /* Current calibration registers */ > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_INIT_TMR, 0x0044); > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_ITER_TMR, 0x0006); > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_ADJ_INIT_TMR, 0x0022); > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_ADJ_ITER_TMR, 0x0006); > + > + /* Resistor calibration registers */ > + cdns_phy_reg_write(cdns_phy, CMN_TXPUCAL_INIT_TMR, 0x0022); > + cdns_phy_reg_write(cdns_phy, CMN_TXPUCAL_ITER_TMR, 0x0006); > + cdns_phy_reg_write(cdns_phy, CMN_TXPU_ADJ_INIT_TMR, 0x0022); > + cdns_phy_reg_write(cdns_phy, CMN_TXPU_ADJ_ITER_TMR, 0x0006); > + cdns_phy_reg_write(cdns_phy, CMN_TXPDCAL_INIT_TMR, 0x0022); > + cdns_phy_reg_write(cdns_phy, CMN_TXPDCAL_ITER_TMR, 0x0006); > + cdns_phy_reg_write(cdns_phy, CMN_TXPD_ADJ_INIT_TMR, 0x0022); > + cdns_phy_reg_write(cdns_phy, CMN_TXPD_ADJ_ITER_TMR, 0x0006); > + cdns_phy_reg_write(cdns_phy, CMN_RXCAL_INIT_TMR, 0x0022); > + cdns_phy_reg_write(cdns_phy, CMN_RXCAL_ITER_TMR, 0x0006); > + cdns_phy_reg_write(cdns_phy, CMN_RX_ADJ_INIT_TMR, 0x0022); > + cdns_phy_reg_write(cdns_phy, CMN_RX_ADJ_ITER_TMR, 0x0006); > + > + for (k = 0; k < num_lanes; k = k + 1) { > + /* Power state machine registers */ > + cdns_phy_reg_write(cdns_phy, XCVR_PSM_CAL_TMR | (k << 9), 0x016d); > + cdns_phy_reg_write(cdns_phy, XCVR_PSM_A0IN_TMR | (k << 9), 0x016d); > + /* Transceiver control and diagnostic registers */ > + cdns_phy_reg_write(cdns_phy, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), > 0x00a2); + cdns_phy_reg_write(cdns_phy, TX_DIAG_BGREF_PREDRV_DELAY | (k << > 9), 0x0097); + /* Transmitter receiver detect registers */ > + cdns_phy_reg_write(cdns_phy, TX_RCVDET_EN_TMR | (k << 9), 0x0a8c); > + cdns_phy_reg_write(cdns_phy, TX_RCVDET_ST_TMR | (k << 9), 0x0036); > + } > + > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_0, 1); > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_1, 1); > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_2, 1); > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_3, 1); > +} > + > +static void hdptx_dp_phy_pma_cmn_pll0_27mhz(struct cdns_hdptx_dp_phy > *cdns_phy) +{ > + u32 num_lanes = cdns_phy->num_lanes; > + u32 link_rate = cdns_phy->link_rate; > + u16 val; > + int index, i, k; > + > + /* DP PLL data rate 0/1 clock divider value */ > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > + val &= ~PLL_DATA_RATE_CLK_DIV_MASK; > + if (link_rate <= RATE_2_7) > + val |= FIELD_PREP(PLL_DATA_RATE_CLK_DIV_MASK, > + PLL_DATA_RATE_CLK_DIV_HBR); > + else > + val |= FIELD_PREP(PLL_DATA_RATE_CLK_DIV_MASK, > + PLL_DATA_RATE_CLK_DIV_HBR2); > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > + > + /* High speed clock 0/1 div */ > + val = cdns_phy_reg_read(cdns_phy, CMN_DIAG_HSCLK_SEL); > + val &= ~(HSCLK1_SEL_MASK | HSCLK0_SEL_MASK); > + if (link_rate <= RATE_2_7) { > + val |= FIELD_PREP(HSCLK1_SEL_MASK, HSCLK_PLL0_DIV2); > + val |= FIELD_PREP(HSCLK0_SEL_MASK, HSCLK_PLL0_DIV2); > + } > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_HSCLK_SEL, val); > + > + for (k = 0; k < num_lanes; k++) { > + val = cdns_phy_reg_read(cdns_phy, (XCVR_DIAG_HSCLK_SEL | (k << 9))); > + val &= ~HSCLK_SEL_MODE3_MASK; > + if (link_rate <= RATE_2_7) > + val |= FIELD_PREP(HSCLK_SEL_MODE3_MASK, HSCLK_SEL_MODE3_HSCLK1); > + cdns_phy_reg_write(cdns_phy, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val); > + } > + > + /* DP PHY PLL 27MHz configuration */ > + index = link_rate_index(link_rate); > + for (i = 0; i < ARRAY_SIZE(phy_pll_27m_cfg); i++) > + cdns_phy_reg_write(cdns_phy, phy_pll_27m_cfg[i].addr, > + phy_pll_27m_cfg[i].val[index]); > + > + /* Transceiver control and diagnostic registers */ > + for (k = 0; k < num_lanes; k++) { > + val = cdns_phy_reg_read(cdns_phy, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); > + val &= ~(DPLL_DATA_RATE_DIV_MODE3_MASK | DPLL_CLK_SEL_MODE3); > + if (link_rate <= RATE_2_7) > + val |= FIELD_PREP(DPLL_DATA_RATE_DIV_MODE3_MASK, 2); > + else > + val |= FIELD_PREP(DPLL_DATA_RATE_DIV_MODE3_MASK, 1); > + cdns_phy_reg_write(cdns_phy, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val); > + } > + > + for (k = 0; k < num_lanes; k = k + 1) { > + /* Power state machine registers */ > + cdns_phy_reg_write(cdns_phy, (XCVR_PSM_RCTRL | (k << 9)), 0xbefc); > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A0 | (k << 9)), 0x6799); > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A1 | (k << 9)), 0x6798); > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A2 | (k << 9)), 0x0098); > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A3 | (k << 9)), 0x0098); > + /* Receiver calibration power state definition register */ > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_CAL | (k << 9)); > + val &= 0xffbb; > + cdns_phy_reg_write(cdns_phy, (RX_PSC_CAL | (k << 9)), val); > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_A0 | (k << 9)); > + val &= 0xffbb; > + cdns_phy_reg_write(cdns_phy, (RX_PSC_A0 | (k << 9)), val); > + } > +} > + > +static void hdptx_dp_phy_ref_clock_type(struct cdns_hdptx_dp_phy *cdns_phy) > +{ > + u32 val; > + > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > + val &= ~CMA_REF_CLK_SEL_MASK; > + /* > + * single ended reference clock (val |= 0x0030); > + * differential clock (val |= 0x0000); > + * > + * for differential clock on the refclk_p and > + * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 > + * cdns_phy_reg_write(cdns_phy, CMN_DIAG_ACYA, 0x0100); > + */ > + val |= FIELD_PREP(CMA_REF_CLK_SEL_MASK, 3); > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); > +} > + > +static int wait_for_ack(struct cdns_hdptx_dp_phy *cdns_phy, u32 reg, u32 > mask, + const char *err_msg) > +{ > + u32 val, i; > + > + for (i = 0; i < 10; i++) { > + val = cdns_phy_reg_read(cdns_phy, reg); > + if (val & mask) > + return 0; > + msleep(20); > + } > + > + dev_err(cdns_phy->dev, "%s\n", err_msg); > + return -1; return -ETIMEDOUT? > +} > + > +static int wait_for_ack_clear(struct cdns_hdptx_dp_phy *cdns_phy, u32 reg, > u32 mask, + const char *err_msg) > +{ > + u32 val, i; > + > + for (i = 0; i < 10; i++) { > + val = cdns_phy_reg_read(cdns_phy, reg); > + if (!(val & mask)) > + return 0; > + msleep(20); > + } > + > + dev_err(cdns_phy->dev, "%s\n", err_msg); > + return -1; return -ETIMEDOUT? > +} > + > +static int hdptx_dp_phy_power_up(struct cdns_hdptx_dp_phy *cdns_phy) > +{ > + u32 val; > + > + /* Enable HDP PLL’s for high speed clocks */ > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > + val |= PLL_EN; > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > + if (wait_for_ack(cdns_phy, PHY_HDP_CLK_CTL, PLL_READY, > + "Wait PLL Ack failed")) > + return -1; > + > + /* Enable HDP PLL’s data rate and full rate clocks out of PMA. */ > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > + val |= PLL_CLK_EN; > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > + if (wait_for_ack(cdns_phy, PHY_HDP_CLK_CTL, PLL_CLK_EN_ACK, > + "Wait PLL clock enable ACK failed")) > + return -1; > + > + /* Configure PHY in A2 Mode */ > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A2); > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A2_ACK, > + "Wait A2 Ack failed")) > + return -1; > + > + /* Configure PHY in A0 mode (PHY must be in the A0 power > + * state in order to transmit data) > + */ > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A0); > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A0_ACK, > + "Wait A0 Ack failed")) > + return -1; Maybe you should just return the return value of wait_for_ack() in each error case. > + cdns_phy->power_up = true; > + > + return 0; > +} > + > +static void hdptx_dp_phy_power_down(struct cdns_hdptx_dp_phy *cdns_phy) > +{ > + u16 val; > + > + if (!cdns_phy->power_up) > + return; > + > + /* Place the PHY lanes in the A3 power state. */ > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A3); > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A3_ACK, > + "Wait A3 Ack failed")) > + return; > + > + /* Disable HDP PLL’s data rate and full rate clocks out of PMA. */ > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > + val &= ~PLL_CLK_EN; > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > + if (wait_for_ack_clear(cdns_phy, PHY_HDP_CLK_CTL, PLL_CLK_EN_ACK, > + "Wait PLL clock Ack clear failed")) > + return; > + > + /* Disable HDP PLL’s for high speed clocks */ > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > + val &= ~PLL_EN; > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > + if (wait_for_ack_clear(cdns_phy, PHY_HDP_CLK_CTL, PLL_READY, > + "Wait PLL Ack clear failed")) > + return; I would have expected cdns_phy->power_up = false somewhere in this function. > +} > + > +static int cdns_hdptx_dp_phy_on(struct phy *phy) > +{ > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > + > + return hdptx_dp_phy_power_up(cdns_phy); > +} > + > +static int cdns_hdptx_dp_phy_off(struct phy *phy) > +{ > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > + > + hdptx_dp_phy_power_down(cdns_phy); > + > + return 0; > +} > + > +static int cdns_hdptx_dp_phy_init(struct phy *phy) > +{ > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > + int ret; > + > + hdptx_dp_phy_ref_clock_type(cdns_phy); > + > + /* PHY power up */ > + ret = hdptx_dp_phy_power_up(cdns_phy); > + if (ret < 0) > + return ret; > + > + hdptx_dp_aux_cfg(cdns_phy); > + > + return ret; > +} > + > +static int cdns_hdptx_dp_configure(struct phy *phy, > + union phy_configure_opts *opts) > +{ > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > + int ret; > + > + cdns_phy->link_rate = opts->dp.link_rate; > + cdns_phy->num_lanes = opts->dp.lanes; > + > + if (cdns_phy->link_rate > MAX_LINK_RATE) { > + dev_err(cdns_phy->dev, "Link Rate(%d) Not supported\n", > cdns_phy->link_rate); + return false; > + } > + > + /* Disable phy clock if PHY in power up state */ > + hdptx_dp_phy_power_down(cdns_phy); > + > + if (cdns_phy->ref_clk_rate == REF_CLK_27MHZ) { > + hdptx_dp_phy_pma_cmn_cfg_27mhz(cdns_phy); > + hdptx_dp_phy_pma_cmn_pll0_27mhz(cdns_phy); > + } else { > + dev_err(cdns_phy->dev, "Not support ref clock rate\n"); > + } > + > + /* PHY power up */ > + ret = hdptx_dp_phy_power_up(cdns_phy); > + > + return ret; > +} > + > +static const struct phy_ops cdns_hdptx_dp_phy_ops = { > + .init = cdns_hdptx_dp_phy_init, > + .configure = cdns_hdptx_dp_configure, > + .power_on = cdns_hdptx_dp_phy_on, > + .power_off = cdns_hdptx_dp_phy_off, > + .owner = THIS_MODULE, > +}; > + > +static int cdns_hdptx_dp_phy_probe(struct platform_device *pdev) > +{ > + struct cdns_hdptx_dp_phy *cdns_phy; > + struct device *dev = &pdev->dev; > + struct device_node *node = dev->of_node; > + struct phy_provider *phy_provider; > + struct resource *res; > + struct phy *phy; > + int ret; > + > + cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL); > + if (!cdns_phy) > + return -ENOMEM; > + > + dev_set_drvdata(dev, cdns_phy); > + cdns_phy->dev = dev; > + mutex_init(&cdns_phy->mbox_mutex); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENODEV; > + cdns_phy->regs = devm_ioremap(dev, res->start, resource_size(res)); > + if (IS_ERR(cdns_phy->regs)) > + return PTR_ERR(cdns_phy->regs); > + > + phy = devm_phy_create(dev, node, &cdns_hdptx_dp_phy_ops); > + if (IS_ERR(phy)) > + return PTR_ERR(phy); > + > + phy->attrs.mode = PHY_MODE_DP; > + cdns_phy->phy = phy; > + phy_set_drvdata(phy, cdns_phy); > + > + /* init base struct for access mhdp mailbox */ > + cdns_phy->base.dev = cdns_phy->dev; > + cdns_phy->base.regs = cdns_phy->regs; > + cdns_phy->base.mbox_mutex = &cdns_phy->mbox_mutex; How is this mutex supposed to work? From the name cdns_phy->base.mbox_mutex is supposed to protect the mailbox access in the cdns-mhdp base, right? But this mutex is different, initialized separately and thus is independent from mhdp->mbox_mutex in cdns-mhdp8501-core.c. Best regards, Alexander > + > + ret = hdptx_dp_clk_enable(cdns_phy); > + if (ret) { > + dev_err(dev, "Init clk fail\n"); > + return -EINVAL; > + } > + > + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + if (IS_ERR(phy_provider)) { > + ret = PTR_ERR(phy_provider); > + goto clk_disable; > + } > + > + return 0; > + > +clk_disable: > + hdptx_dp_clk_disable(cdns_phy); > + > + return -EINVAL; > +} > + > +static int cdns_hdptx_dp_phy_remove(struct platform_device *pdev) > +{ > + struct cdns_hdptx_dp_phy *cdns_phy = platform_get_drvdata(pdev); > + > + hdptx_dp_clk_disable(cdns_phy); > + > + return 0; > +} > + > +static const struct of_device_id cdns_hdptx_dp_phy_of_match[] = { > + {.compatible = "fsl,imx8mq-dp-phy" }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, cdns_hdptx_dp_phy_of_match); > + > +static struct platform_driver cdns_hdptx_dp_phy_driver = { > + .probe = cdns_hdptx_dp_phy_probe, > + .remove = cdns_hdptx_dp_phy_remove, > + .driver = { > + .name = "cdns-hdptx-dp-phy", > + .of_match_table = cdns_hdptx_dp_phy_of_match, > + } > +}; > +module_platform_driver(cdns_hdptx_dp_phy_driver); > + > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > +MODULE_DESCRIPTION("Cadence HDP-TX DisplayPort PHY driver"); > +MODULE_LICENSE("GPL");
Hi Sandor, thanks for the patch. Am Dienstag, 17. Oktober 2023, 09:04:03 CEST schrieb Sandor Yu: > Add Cadence HDP-TX HDMI PHY driver for i.MX8MQ. > > Cadence HDP-TX PHY could be put in either DP mode or > HDMI mode base on the configuration chosen. > HDMI PHY mode is configurated in the driver. > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> > --- > v9->v11: > *No change. > > drivers/phy/freescale/Kconfig | 10 + > drivers/phy/freescale/Makefile | 1 + > drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c | 961 ++++++++++++++++++++ > 3 files changed, 972 insertions(+) > create mode 100644 drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c > > diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig > index c39709fd700ac..14f47b7cc77ab 100644 > --- a/drivers/phy/freescale/Kconfig > +++ b/drivers/phy/freescale/Kconfig > @@ -45,6 +45,16 @@ config PHY_FSL_IMX8MQ_DP > Enable this to support the Cadence HDPTX DP PHY driver > on i.MX8MQ SOC. > > +config PHY_FSL_IMX8MQ_HDMI > + tristate "Freescale i.MX8MQ HDMI PHY support" > + depends on OF && HAS_IOMEM > + depends on COMMON_CLK > + select GENERIC_PHY > + select CDNS_MHDP_HELPER > + help > + Enable this to support the Cadence HDPTX HDMI PHY driver > + on i.MX8MQ SOC. > + > endif > > config PHY_FSL_LYNX_28G > diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile > index 47e5285209fa8..1380ac31c2ead 100644 > --- a/drivers/phy/freescale/Makefile > +++ b/drivers/phy/freescale/Makefile > @@ -1,5 +1,6 @@ > # SPDX-License-Identifier: GPL-2.0-only > obj-$(CONFIG_PHY_FSL_IMX8MQ_DP) += phy-fsl-imx8mq-dp.o > +obj-$(CONFIG_PHY_FSL_IMX8MQ_HDMI) += phy-fsl-imx8mq-hdmi.o > obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o > obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o > obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o > diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c > b/drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c new file mode 100644 > index 0000000000000..9722b5e1803c7 > --- /dev/null > +++ b/drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c > @@ -0,0 +1,961 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Cadence High-Definition Multimedia Interface (HDMI) PHY driver > + * > + * Copyright (C) 2022,2023 NXP Semiconductor, Inc. > + */ > +#include <asm/unaligned.h> > +#include <drm/bridge/cdns-mhdp-helper.h> > +#include <linux/clk.h> > +#include <linux/kernel.h> > +#include <linux/phy/phy.h> > +#include <linux/platform_device.h> > +#include <linux/io.h> > + > +#define ADDR_PHY_AFE 0x80000 > + > +/* PHY registers */ > +#define CMN_SSM_BIAS_TMR 0x0022 > +#define CMN_PLLSM0_USER_DEF_CTRL 0x002f > +#define CMN_PSM_CLK_CTRL 0x0061 > +#define CMN_CDIAG_REFCLK_CTRL 0x0062 > +#define CMN_PLL0_VCOCAL_START 0x0081 > +#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084 > +#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085 > +#define CMN_TXPUCAL_CTRL 0x00e0 > +#define CMN_TXPDCAL_CTRL 0x00f0 > +#define CMN_TXPU_ADJ_CTRL 0x0108 > +#define CMN_TXPD_ADJ_CTRL 0x010c > +#define CMN_DIAG_PLL0_FBH_OVRD 0x01c0 > +#define CMN_DIAG_PLL0_FBL_OVRD 0x01c1 > +#define CMN_DIAG_PLL0_OVRD 0x01c2 > +#define CMN_DIAG_PLL0_TEST_MODE 0x01c4 > +#define CMN_DIAG_PLL0_V2I_TUNE 0x01c5 > +#define CMN_DIAG_PLL0_CP_TUNE 0x01c6 > +#define CMN_DIAG_PLL0_LF_PROG 0x01c7 > +#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01c8 > +#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01c9 > +#define CMN_DIAG_PLL0_INCLK_CTRL 0x01ca > +#define CMN_DIAG_PLL0_PXL_DIVH 0x01cb > +#define CMN_DIAG_PLL0_PXL_DIVL 0x01cc > +#define CMN_DIAG_HSCLK_SEL 0x01e0 > +#define XCVR_PSM_RCTRL 0x4001 > +#define TX_TXCC_CAL_SCLR_MULT_0 0x4047 > +#define TX_TXCC_CPOST_MULT_00_0 0x404c > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > +#define XCVR_DIAG_HSCLK_SEL 0x40e1 > +#define XCVR_DIAG_BIDI_CTRL 0x40e8 > +#define TX_PSC_A0 0x4100 > +#define TX_PSC_A1 0x4101 > +#define TX_PSC_A2 0x4102 > +#define TX_PSC_A3 0x4103 > +#define TX_DIAG_TX_CTRL 0x41e0 > +#define TX_DIAG_TX_DRV 0x41e1 > +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 > +#define TX_DIAG_ACYA_0 0x41ff > +#define TX_DIAG_ACYA_1 0x43ff > +#define TX_DIAG_ACYA_2 0x45ff > +#define TX_DIAG_ACYA_3 0x47ff > +#define TX_ANA_CTRL_REG_1 0x5020 > +#define TX_ANA_CTRL_REG_2 0x5021 > +#define TX_DIG_CTRL_REG_2 0x5024 > +#define TXDA_CYA_AUXDA_CYA 0x5025 > +#define TX_ANA_CTRL_REG_3 0x5026 > +#define TX_ANA_CTRL_REG_4 0x5027 > +#define TX_ANA_CTRL_REG_5 0x5029 > +#define RX_PSC_A0 0x8000 > +#define RX_PSC_CAL 0x8006 > +#define PHY_HDP_MODE_CTRL 0xc008 > +#define PHY_HDP_CLK_CTL 0xc009 > +#define PHY_ISO_CMN_CTRL 0xc010 > +#define PHY_PMA_CMN_CTRL1 0xc800 > +#define PHY_PMA_ISO_CMN_CTRL 0xc810 > +#define PHY_PMA_ISO_PLL_CTRL1 0xc812 > +#define PHY_PMA_ISOLATION_CTRL 0xc81f > + > +/* PHY_HDP_CLK_CTL */ > +#define PLL_DATA_RATE_CLK_DIV_MASK GENMASK(15, 8) > +#define PLL_DATA_RATE_CLK_DIV_HBR 0x24 > +#define PLL_DATA_RATE_CLK_DIV_HBR2 0x12 > +#define PLL_CLK_EN_ACK_EN BIT(3) > +#define PLL_CLK_EN BIT(2) > +#define PLL_READY BIT(1) > +#define PLL_EN BIT(0) > + > +/* PHY_PMA_CMN_CTRL1 */ > +#define CMA_REF_CLK_DIG_DIV_MASK GENMASK(13, 12) > +#define CMA_REF_CLK_SEL_MASK GENMASK(6, 4) > +#define CMA_REF_CLK_RCV_EN_MASK BIT(3) > +#define CMA_REF_CLK_RCV_EN 1 > +#define CMN_READY BIT(0) > + > +/* PHY_PMA_ISO_PLL_CTRL1 */ > +#define CMN_PLL0_CLK_DATART_DIV_MASK GENMASK(7, 0) > + > +/* TX_DIAG_TX_DRV */ > +#define TX_DRIVER_PROG_BOOST_ENABLE BIT(10) > +#define TX_DRIVER_PROG_BOOST_LEVEL_MASK GENMASK(9, 8) > +#define TX_DRIVER_LDO_BG_DEPENDENT_REF_ENABLE BIT(7) > +#define TX_DRIVER_LDO_BANDGAP_REF_ENABLE BIT(6) > + > +/* TX_TXCC_CAL_SCLR_MULT_0 */ > +#define SCALED_RESISTOR_CALIBRATION_CODE_ADD BIT(8) > +#define RESISTOR_CAL_MULT_VAL_32_128 BIT(5) > + > +/* CMN_CDIAG_REFCLK_CTRL */ > +#define DIG_REF_CLK_DIV_SCALER_MASK GENMASK(14, 12) > +#define REFCLK_TERMINATION_EN_OVERRIDE_EN BIT(7) > +#define REFCLK_TERMINATION_EN_OVERRIDE BIT(6) > + > +/* CMN_DIAG_HSCLK_SEL */ > +#define HSCLK1_SEL_MASK GENMASK(5, 4) > +#define HSCLK0_SEL_MASK GENMASK(1, 0) > + > +/* XCVR_DIAG_HSCLK_SEL */ > +#define HSCLK_SEL_MODE3_MASK GENMASK(13, 12) > +#define HSCLK_SEL_MODE3_HSCLK1 1 > + > +/* CMN_PLL0_VCOCAL_START */ > +#define VCO_CALIB_CODE_START_POINT_VAL_MASK GENMASK(8, 0) > + > +/* CMN_DIAG_PLL0_FBH_OVRD */ > +#define PLL_FEEDBACK_DIV_HI_OVERRIDE_EN BIT(15) > + > +/* CMN_DIAG_PLL0_FBL_OVRD */ > +#define PLL_FEEDBACK_DIV_LO_OVERRIDE_EN BIT(15) > + > +/* CMN_DIAG_PLL0_PXL_DIVH */ > +#define PLL_PCLK_DIV_EN BIT(15) > + > +/* XCVR_DIAG_PLLDRC_CTRL */ > +#define DPLL_CLK_SEL_MODE3 BIT(14) > + > +/* TX_DIAG_TX_CTRL */ > +#define TX_IF_SUBRATE_MODE3_MASK GENMASK(7, 6) > + > +/* PHY_HDP_MODE_CTRL */ > +#define POWER_STATE_A3_ACK BIT(7) > +#define POWER_STATE_A2_ACK BIT(6) > +#define POWER_STATE_A1_ACK BIT(5) > +#define POWER_STATE_A0_ACK BIT(4) > +#define POWER_STATE_A3 BIT(3) > +#define POWER_STATE_A2 BIT(2) > +#define POWER_STATE_A1 BIT(1) > +#define POWER_STATE_A0 BIT(0) > + > +/* PHY_PMA_ISO_CMN_CTRL */ > +#define CMN_MACRO_PWR_EN_ACK BIT(5) > + > +#define KEEP_ALIVE 0x18 > + > +#define REF_CLK_27MHZ 27000000 > + > +/* HDMI TX clock control settings */ > +struct hdptx_hdmi_ctrl { > + u32 pixel_clk_freq_min; > + u32 pixel_clk_freq_max; > + u32 feedback_factor; > + u32 data_range_kbps_min; > + u32 data_range_kbps_max; > + u32 cmnda_pll0_ip_div; > + u32 cmn_ref_clk_dig_div; > + u32 ref_clk_divider_scaler; > + u32 pll_fb_div_total; > + u32 cmnda_pll0_fb_div_low; > + u32 cmnda_pll0_fb_div_high; > + u32 pixel_div_total; > + u32 cmnda_pll0_pxdiv_low; > + u32 cmnda_pll0_pxdiv_high; > + u32 vco_freq_min; > + u32 vco_freq_max; > + u32 vco_ring_select; > + u32 cmnda_hs_clk_0_sel; > + u32 cmnda_hs_clk_1_sel; > + u32 hsclk_div_at_xcvr; > + u32 hsclk_div_tx_sub_rate; > + u32 cmnda_pll0_hs_sym_div_sel; > + u32 cmnda_pll0_clk_freq_min; > + u32 cmnda_pll0_clk_freq_max; > +}; > + > +struct cdns_hdptx_hdmi_phy { > + struct cdns_mhdp_base base; > + > + void __iomem *regs; /* DPTX registers base */ > + struct mutex mbox_mutex; /* mutex to protect mailbox */ > + struct device *dev; > + struct phy *phy; > + struct clk *ref_clk, *apb_clk; > + u32 ref_clk_rate; > + u32 pixel_clk_rate; > + enum hdmi_colorspace color_space; > + u32 bpc; > +}; > + > +/* HDMI TX clock control settings, pixel clock is output */ > +static const struct hdptx_hdmi_ctrl pixel_clk_output_ctrl_table[] = { > +/*Minclk Maxclk Fdbak DR_min DR_max ip_d dig DS Totl */ > +{ 27000, 27000, 1000, 270000, 270000, 0x03, 0x1, 0x1, 240, 0x0bc, > 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x3, 27000, > 27000}, +{ 27000, 27000, 1250, 337500, 337500, 0x03, 0x1, 0x1, 300, > 0x0ec, 0x03c, 100, 0x030, 0x030, 2700000, 2700000, 0, 2, 2, 2, 4, 0x3, > 33750, 33750}, +{ 27000, 27000, 1500, 405000, 405000, 0x03, 0x1, 0x1, > 360, 0x11c, 0x048, 120, 0x03a, 0x03a, 3240000, 3240000, 0, 2, 2, 2, 4, 0x3, > 40500, 40500}, +{ 27000, 27000, 2000, 540000, 540000, 0x03, 0x1, 0x1, > 240, 0x0bc, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x2, > 54000, 54000}, +{ 54000, 54000, 1000, 540000, 540000, 0x03, 0x1, 0x1, > 480, 0x17c, 0x060, 80, 0x026, 0x026, 4320000, 4320000, 1, 2, 2, 2, 4, 0x3, > 54000, 54000}, +{ 54000, 54000, 1250, 675000, 675000, 0x04, 0x1, 0x1, > 400, 0x13c, 0x050, 50, 0x017, 0x017, 2700000, 2700000, 0, 1, 1, 2, 4, 0x2, > 67500, 67500}, +{ 54000, 54000, 1500, 810000, 810000, 0x04, 0x1, 0x1, > 480, 0x17c, 0x060, 60, 0x01c, 0x01c, 3240000, 3240000, 0, 2, 2, 2, 2, 0x2, > 81000, 81000}, +{ 54000, 54000, 2000, 1080000, 1080000, 0x03, 0x1, 0x1, > 240, 0x0bc, 0x030, 40, 0x012, 0x012, 2160000, 2160000, 0, 2, 2, 2, 1, 0x1, > 108000, 108000}, +{ 74250, 74250, 1000, 742500, 742500, 0x03, 0x1, 0x1, > 660, 0x20c, 0x084, 80, 0x026, 0x026, 5940000, 5940000, 1, 2, 2, 2, 4, 0x3, > 74250, 74250}, +{ 74250, 74250, 1250, 928125, 928125, 0x04, 0x1, 0x1, > 550, 0x1b4, 0x06e, 50, 0x017, 0x017, 3712500, 3712500, 1, 1, 1, 2, 4, 0x2, > 92812, 92812}, +{ 74250, 74250, 1500, 1113750, 1113750, 0x04, 0x1, 0x1, > 660, 0x20c, 0x084, 60, 0x01c, 0x01c, 4455000, 4455000, 1, 2, 2, 2, 2, 0x2, > 111375, 111375}, +{ 74250, 74250, 2000, 1485000, 1485000, 0x03, 0x1, 0x1, > 330, 0x104, 0x042, 40, 0x012, 0x012, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, > 148500, 148500}, +{ 99000, 99000, 1000, 990000, 990000, 0x03, 0x1, 0x1, > 440, 0x15c, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 2, 0x2, > 99000, 99000}, +{ 99000, 99000, 1250, 1237500, 1237500, 0x03, 0x1, 0x1, > 275, 0x0d8, 0x037, 25, 0x00b, 0x00a, 2475000, 2475000, 0, 1, 1, 2, 2, 0x1, > 123750, 123750}, +{ 99000, 99000, 1500, 1485000, 1485000, 0x03, 0x1, 0x1, > 330, 0x104, 0x042, 30, 0x00d, 0x00d, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, > 148500, 148500}, +{ 99000, 99000, 2000, 1980000, 1980000, 0x03, 0x1, 0x1, > 440, 0x15c, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 1, 0x1, > 198000, 198000}, +{148500, 148500, 1000, 1485000, 1485000, 0x03, 0x1, 0x1, > 660, 0x20c, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 2, 0x2, > 148500, 148500}, +{148500, 148500, 1250, 1856250, 1856250, 0x04, 0x1, 0x1, > 550, 0x1b4, 0x06e, 25, 0x00b, 0x00a, 3712500, 3712500, 1, 1, 1, 2, 2, 0x1, > 185625, 185625}, +{148500, 148500, 1500, 2227500, 2227500, 0x03, 0x1, 0x1, > 495, 0x188, 0x063, 30, 0x00d, 0x00d, 4455000, 4455000, 1, 1, 1, 2, 2, 0x1, > 222750, 222750}, +{148500, 148500, 2000, 2970000, 2970000, 0x03, 0x1, 0x1, > 660, 0x20c, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 1, 0x1, > 297000, 297000}, +{198000, 198000, 1000, 1980000, 1980000, 0x03, 0x1, 0x1, > 220, 0x0ac, 0x02c, 10, 0x003, 0x003, 1980000, 1980000, 0, 1, 1, 2, 1, 0x0, > 198000, 198000}, +{198000, 198000, 1250, 2475000, 2475000, 0x03, 0x1, 0x1, > 550, 0x1b4, 0x06e, 25, 0x00b, 0x00a, 4950000, 4950000, 1, 1, 1, 2, 2, 0x1, > 247500, 247500}, +{198000, 198000, 1500, 2970000, 2970000, 0x03, 0x1, 0x1, > 330, 0x104, 0x042, 15, 0x006, 0x005, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, > 297000, 297000}, +{198000, 198000, 2000, 3960000, 3960000, 0x03, 0x1, 0x1, > 440, 0x15c, 0x058, 20, 0x008, 0x008, 3960000, 3960000, 1, 1, 1, 2, 1, 0x0, > 396000, 396000}, +{297000, 297000, 1000, 2970000, 2970000, 0x03, 0x1, 0x1, > 330, 0x104, 0x042, 10, 0x003, 0x003, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, > 297000, 297000}, +{297000, 297000, 1500, 4455000, 4455000, 0x03, 0x1, 0x1, > 495, 0x188, 0x063, 15, 0x006, 0x005, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, > 445500, 445500}, +{297000, 297000, 2000, 5940000, 5940000, 0x03, 0x1, 0x1, > 660, 0x20c, 0x084, 20, 0x008, 0x008, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, > 594000, 594000}, +{594000, 594000, 1000, 5940000, 5940000, 0x03, 0x1, 0x1, > 660, 0x20c, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, > 594000, 594000}, +{594000, 594000, 750, 4455000, 4455000, 0x03, 0x1, 0x1, > 495, 0x188, 0x063, 10, 0x003, 0x003, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, > 445500, 445500}, +{594000, 594000, 625, 3712500, 3712500, 0x04, 0x1, 0x1, > 550, 0x1b4, 0x06e, 10, 0x003, 0x003, 3712500, 3712500, 1, 1, 1, 2, 1, 0x0, > 371250, 371250}, +{594000, 594000, 500, 2970000, 2970000, 0x03, 0x1, 0x1, > 660, 0x20c, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 2, 0x1, > 297000, 297000}, +}; > + > +/* HDMI TX PLL tuning settings */ > +struct hdptx_hdmi_pll_tuning { > + u32 vco_freq_bin; > + u32 vco_freq_min; > + u32 vco_freq_max; > + u32 volt_to_current_coarse; > + u32 volt_to_current; > + u32 ndac_ctrl; > + u32 pmos_ctrl; > + u32 ptat_ndac_ctrl; > + u32 feedback_div_total; > + u32 charge_pump_gain; > + u32 coarse_code; > + u32 v2i_code; > + u32 vco_cal_code; > +}; > + > +/* HDMI TX PLL tuning settings, pixel clock is output */ > +static const struct hdptx_hdmi_pll_tuning pixel_clk_output_pll_table[] = { > +/*bin VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain Coa V2I CAL > */ +{ 1, 1980000, 1980000, 0x4, 0x3, 0x0, 0x09, 0x09, 220, 0x42, 160, 5, > 183 }, +{ 2, 2160000, 2160000, 0x4, 0x3, 0x0, 0x09, 0x09, 240, 0x42, 166, > 6, 208 }, +{ 3, 2475000, 2475000, 0x5, 0x3, 0x1, 0x00, 0x07, 275, 0x42, > 167, 6, 209 }, +{ 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 300, > 0x42, 188, 6, 230 }, +{ 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, > 400, 0x4c, 188, 6, 230 }, +{ 5, 2970000, 2970000, 0x6, 0x3, 0x1, 0x00, > 0x07, 330, 0x42, 183, 6, 225 }, +{ 6, 3240000, 3240000, 0x6, 0x3, 0x1, > 0x00, 0x07, 360, 0x42, 203, 7, 256 }, +{ 6, 3240000, 3240000, 0x6, 0x3, > 0x1, 0x00, 0x07, 480, 0x4c, 203, 7, 256 }, +{ 7, 3712500, 3712500, 0x4, > 0x3, 0x0, 0x07, 0x0F, 550, 0x4c, 212, 7, 257 }, +{ 8, 3960000, 3960000, > 0x5, 0x3, 0x0, 0x07, 0x0F, 440, 0x42, 184, 6, 226 }, +{ 9, 4320000, > 4320000, 0x5, 0x3, 0x1, 0x07, 0x0F, 480, 0x42, 205, 7, 258 }, +{ 10, > 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 495, 0x42, 219, 7, 272 }, +{ > 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 660, 0x4c, 219, 7, 272 }, > +{ 11, 4950000, 4950000, 0x6, 0x3, 0x1, 0x00, 0x07, 550, 0x42, 213, 7, 258 > }, +{ 12, 5940000, 5940000, 0x7, 0x3, 0x1, 0x00, 0x07, 660, 0x42, 244, 8, > 292 }, +}; > + > +static int cdns_phy_reg_write(struct cdns_hdptx_hdmi_phy *cdns_phy, u32 > addr, u32 val) +{ > + return cdns_mhdp_reg_write(&cdns_phy->base, ADDR_PHY_AFE + (addr << 2), > val); +} > + > +static u32 cdns_phy_reg_read(struct cdns_hdptx_hdmi_phy *cdns_phy, u32 > addr) +{ > + u32 reg32; > + > + cdns_mhdp_reg_read(&cdns_phy->base, ADDR_PHY_AFE + (addr << 2), ®32); > + > + return reg32; > +} > + > +static int wait_for_ack(struct cdns_hdptx_hdmi_phy *cdns_phy, u32 reg, u32 > mask, + const char *err_msg) > +{ > + u32 val, i; > + > + for (i = 0; i < 10; i++) { > + val = cdns_phy_reg_read(cdns_phy, reg); > + if (val & mask) > + return 0; > + msleep(20); > + } > + > + dev_err(cdns_phy->dev, "%s\n", err_msg); > + return -1; return -ETIMEDOUT? > +} > + > +static bool hdptx_phy_check_alive(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + u32 alive, newalive; > + u8 retries_left = 50; > + > + alive = readl(cdns_phy->regs + KEEP_ALIVE); > + > + while (retries_left--) { > + udelay(2); > + > + newalive = readl(cdns_phy->regs + KEEP_ALIVE); > + if (alive == newalive) > + continue; > + return true; > + } > + return false; > +} > + > +static int hdptx_hdmi_clk_enable(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + struct device *dev = cdns_phy->dev; > + u32 ref_clk_rate; > + int ret; > + > + cdns_phy->ref_clk = devm_clk_get(dev, "ref"); > + if (IS_ERR(cdns_phy->ref_clk)) { > + dev_err(dev, "phy ref clock not found\n"); > + return PTR_ERR(cdns_phy->ref_clk); > + } > + > + cdns_phy->apb_clk = devm_clk_get(dev, "apb"); > + if (IS_ERR(cdns_phy->apb_clk)) { > + dev_err(dev, "phy apb clock not found\n"); > + return PTR_ERR(cdns_phy->apb_clk); > + } > + > + ret = clk_prepare_enable(cdns_phy->ref_clk); > + if (ret) { > + dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); > + return ret; > + } > + > + ref_clk_rate = clk_get_rate(cdns_phy->ref_clk); > + if (!ref_clk_rate) { > + dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); > + goto err_ref_clk; > + } > + > + if (ref_clk_rate == REF_CLK_27MHZ) { > + cdns_phy->ref_clk_rate = ref_clk_rate; > + } else { > + dev_err(cdns_phy->dev, "Not support Ref Clock Rate(%dHz) \n", > ref_clk_rate); + goto err_ref_clk; > + } > + > + ret = clk_prepare_enable(cdns_phy->apb_clk); > + if (ret) { > + dev_err(cdns_phy->dev, "Failed to prepare apb clock\n"); > + goto err_ref_clk; > + } > + > + return 0; > + > +err_ref_clk: > + clk_disable_unprepare(cdns_phy->ref_clk); > + return -EINVAL; > +} > + > +static void hdptx_hdmi_clk_disable(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + clk_disable_unprepare(cdns_phy->ref_clk); > + clk_disable_unprepare(cdns_phy->apb_clk); Shouldn't the clocks be disabled in reverse order to enabling path? > +} > + > +static void hdptx_hdmi_arc_config(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + u16 txpu_calib_code; > + u16 txpd_calib_code; > + u16 txpu_adj_calib_code; > + u16 txpd_adj_calib_code; > + u16 prev_calib_code; > + u16 new_calib_code; > + u16 rdata; > + > + /* Power ARC */ > + cdns_phy_reg_write(cdns_phy, TXDA_CYA_AUXDA_CYA, 0x0001); > + > + prev_calib_code = cdns_phy_reg_read(cdns_phy, TX_DIG_CTRL_REG_2); > + txpu_calib_code = cdns_phy_reg_read(cdns_phy, CMN_TXPUCAL_CTRL); > + txpd_calib_code = cdns_phy_reg_read(cdns_phy, CMN_TXPDCAL_CTRL); > + txpu_adj_calib_code = cdns_phy_reg_read(cdns_phy, CMN_TXPU_ADJ_CTRL); > + txpd_adj_calib_code = cdns_phy_reg_read(cdns_phy, CMN_TXPD_ADJ_CTRL); > + > + new_calib_code = ((txpu_calib_code + txpd_calib_code) / 2) > + + txpu_adj_calib_code + txpd_adj_calib_code; > + > + if (new_calib_code != prev_calib_code) { > + rdata = cdns_phy_reg_read(cdns_phy, TX_ANA_CTRL_REG_1); > + rdata &= 0xdfff; > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, rdata); > + cdns_phy_reg_write(cdns_phy, TX_DIG_CTRL_REG_2, new_calib_code); > + mdelay(10); > + rdata |= 0x2000; > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, rdata); > + usleep_range(150, 250); > + } > + > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0100); > + usleep_range(100, 200); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0300); > + usleep_range(100, 200); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_3, 0x0000); > + usleep_range(100, 200); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2008); > + usleep_range(100, 200); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2018); > + usleep_range(100, 200); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2098); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030c); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_5, 0x0010); > + usleep_range(100, 200); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_4, 0x4001); > + mdelay(5); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2198); > + mdelay(5); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030d); > + usleep_range(100, 200); > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030f); > +} > + > +static void hdptx_hdmi_phy_set_vswing(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + u32 k; > + const u32 num_lanes = 4; > + > + for (k = 0; k < num_lanes; k++) { > + cdns_phy_reg_write(cdns_phy, (TX_DIAG_TX_DRV | (k << 9)), > + TX_DRIVER_PROG_BOOST_ENABLE | > + FIELD_PREP(TX_DRIVER_PROG_BOOST_LEVEL_MASK, 3) | > + TX_DRIVER_LDO_BG_DEPENDENT_REF_ENABLE | > + TX_DRIVER_LDO_BANDGAP_REF_ENABLE); > + cdns_phy_reg_write(cdns_phy, (TX_TXCC_CPOST_MULT_00_0 | (k << 9)), 0x0); > + cdns_phy_reg_write(cdns_phy, (TX_TXCC_CAL_SCLR_MULT_0 | (k << 9)), > + SCALED_RESISTOR_CALIBRATION_CODE_ADD | > + RESISTOR_CAL_MULT_VAL_32_128); > + } > +} > + > +static int hdptx_hdmi_feedback_factor(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + u32 feedback_factor; > + > + switch (cdns_phy->color_space) { > + case HDMI_COLORSPACE_YUV422: > + feedback_factor = 1000; > + break; > + > + case HDMI_COLORSPACE_YUV420: > + switch (cdns_phy->bpc) { > + case 8: > + feedback_factor = 500; > + break; > + case 10: > + feedback_factor = 625; > + break; > + case 12: > + feedback_factor = 750; > + break; > + case 16: > + feedback_factor = 1000; > + break; > + default: > + dev_dbg(cdns_phy->dev, "Invalid ColorDepth\n"); > + return 0; > + } > + break; > + > + default: > + /* Assume RGB/YUV444 */ > + switch (cdns_phy->bpc) { > + case 10: > + feedback_factor = 1250; > + break; > + case 12: > + feedback_factor = 1500; > + break; > + case 16: > + feedback_factor = 2000; > + break; > + default: > + feedback_factor = 1000; > + } > + } > + > + return feedback_factor; > +} > + > +static int hdptx_hdmi_phy_config(struct cdns_hdptx_hdmi_phy *cdns_phy, > + const struct hdptx_hdmi_ctrl *p_ctrl_table, > + const struct hdptx_hdmi_pll_tuning *p_pll_table, > + char pclk_in) bool pclk_in > +{ > + const u32 num_lanes = 4; > + u32 val, k; > + > + /* enable PHY isolation mode only for CMN */ > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISOLATION_CTRL, 0xd000); > + > + /* set cmn_pll0_clk_datart1_div/cmn_pll0_clk_datart0_div dividers */ > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_ISO_PLL_CTRL1); > + val &= ~CMN_PLL0_CLK_DATART_DIV_MASK; > + val |= FIELD_PREP(CMN_PLL0_CLK_DATART_DIV_MASK, 0x12); > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_PLL_CTRL1, val); > + > + /* assert PHY reset from isolation register */ > + cdns_phy_reg_write(cdns_phy, PHY_ISO_CMN_CTRL, 0x0000); > + /* assert PMA CMN reset */ > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_CMN_CTRL, 0x0000); > + > + /* register XCVR_DIAG_BIDI_CTRL */ > + for (k = 0; k < num_lanes; k++) > + cdns_phy_reg_write(cdns_phy, XCVR_DIAG_BIDI_CTRL | (k << 9), 0x00ff); > + > + /* Describing Task phy_cfg_hdp */ > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > + val &= ~CMA_REF_CLK_RCV_EN_MASK; > + val |= FIELD_PREP(CMA_REF_CLK_RCV_EN_MASK, CMA_REF_CLK_RCV_EN); > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); > + > + /* PHY Registers */ > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > + val &= ~CMA_REF_CLK_DIG_DIV_MASK; > + val |= FIELD_PREP(CMA_REF_CLK_DIG_DIV_MASK, > p_ctrl_table->cmn_ref_clk_dig_div); + cdns_phy_reg_write(cdns_phy, > PHY_PMA_CMN_CTRL1, val); > + > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > + val &= ~PLL_DATA_RATE_CLK_DIV_MASK; > + val |= FIELD_PREP(PLL_DATA_RATE_CLK_DIV_MASK, > + PLL_DATA_RATE_CLK_DIV_HBR2); > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > + > + /* Common control module control and diagnostic registers */ > + val = cdns_phy_reg_read(cdns_phy, CMN_CDIAG_REFCLK_CTRL); > + val &= ~DIG_REF_CLK_DIV_SCALER_MASK; > + val |= FIELD_PREP(DIG_REF_CLK_DIV_SCALER_MASK, > p_ctrl_table->ref_clk_divider_scaler); + val |= > REFCLK_TERMINATION_EN_OVERRIDE_EN | REFCLK_TERMINATION_EN_OVERRIDE; > + cdns_phy_reg_write(cdns_phy, CMN_CDIAG_REFCLK_CTRL, val); > + > + /* High speed clock used */ > + val = cdns_phy_reg_read(cdns_phy, CMN_DIAG_HSCLK_SEL); > + val &= ~(HSCLK1_SEL_MASK | HSCLK0_SEL_MASK); > + val |= FIELD_PREP(HSCLK1_SEL_MASK, (p_ctrl_table->cmnda_hs_clk_1_sel >> > 1)); + val |= FIELD_PREP(HSCLK0_SEL_MASK, (p_ctrl_table->cmnda_hs_clk_0_sel > >> 1)); + cdns_phy_reg_write(cdns_phy, CMN_DIAG_HSCLK_SEL, val); > + > + for (k = 0; k < num_lanes; k++) { > + val = cdns_phy_reg_read(cdns_phy, (XCVR_DIAG_HSCLK_SEL | (k << 9))); > + val &= ~HSCLK_SEL_MODE3_MASK; > + val |= FIELD_PREP(HSCLK_SEL_MODE3_MASK, > + (p_ctrl_table->cmnda_hs_clk_0_sel >> 1)); > + cdns_phy_reg_write(cdns_phy, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val); > + } > + > + /* PLL 0 control state machine registers */ > + val = p_ctrl_table->vco_ring_select << 12; > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_USER_DEF_CTRL, val); > + > + if (pclk_in) { > + val = 0x30a0; > + } else { > + val = cdns_phy_reg_read(cdns_phy, CMN_PLL0_VCOCAL_START); > + val &= ~VCO_CALIB_CODE_START_POINT_VAL_MASK; > + val |= FIELD_PREP(VCO_CALIB_CODE_START_POINT_VAL_MASK, > + p_pll_table->vco_cal_code); > + } > + cdns_phy_reg_write(cdns_phy, CMN_PLL0_VCOCAL_START, val); > + > + cdns_phy_reg_write(cdns_phy, CMN_PLL0_VCOCAL_INIT_TMR, 0x0064); > + cdns_phy_reg_write(cdns_phy, CMN_PLL0_VCOCAL_ITER_TMR, 0x000a); > + > + /* Common functions control and diagnostics registers */ > + val = p_ctrl_table->cmnda_pll0_hs_sym_div_sel << 8; > + val |= p_ctrl_table->cmnda_pll0_ip_div; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_INCLK_CTRL, val); > + > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_OVRD, 0x0000); > + > + val = p_ctrl_table->cmnda_pll0_fb_div_high; > + val |= PLL_FEEDBACK_DIV_HI_OVERRIDE_EN; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_FBH_OVRD, val); > + > + val = p_ctrl_table->cmnda_pll0_fb_div_low; > + val |= PLL_FEEDBACK_DIV_LO_OVERRIDE_EN; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_FBL_OVRD, val); > + > + if (!pclk_in) { > + val = p_ctrl_table->cmnda_pll0_pxdiv_low; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_PXL_DIVL, val); > + > + val = p_ctrl_table->cmnda_pll0_pxdiv_high; > + val |= PLL_PCLK_DIV_EN; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_PXL_DIVH, val); > + } > + > + val = p_pll_table->volt_to_current_coarse; > + val |= (p_pll_table->volt_to_current) << 4; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_V2I_TUNE, val); > + > + val = p_pll_table->charge_pump_gain; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_CP_TUNE, val); > + > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_LF_PROG, 0x0008); > + > + val = p_pll_table->pmos_ctrl; > + val |= (p_pll_table->ndac_ctrl) << 8; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_PTATIS_TUNE1, val); > + > + val = p_pll_table->ptat_ndac_ctrl; > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_PTATIS_TUNE2, val); > + > + if (pclk_in) > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_TEST_MODE, 0x0022); > + else > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_TEST_MODE, 0x0020); > + > + cdns_phy_reg_write(cdns_phy, CMN_PSM_CLK_CTRL, 0x0016); > + > + /* Transceiver control and diagnostic registers */ > + for (k = 0; k < num_lanes; k++) { > + val = cdns_phy_reg_read(cdns_phy, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); > + val &= ~DPLL_CLK_SEL_MODE3; > + cdns_phy_reg_write(cdns_phy, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val); > + } > + > + for (k = 0; k < num_lanes; k++) { > + val = cdns_phy_reg_read(cdns_phy, (TX_DIAG_TX_CTRL | (k << 9))); > + val &= ~TX_IF_SUBRATE_MODE3_MASK; > + val |= FIELD_PREP(TX_IF_SUBRATE_MODE3_MASK, > + (p_ctrl_table->hsclk_div_tx_sub_rate >> 1)); > + cdns_phy_reg_write(cdns_phy, (TX_DIAG_TX_CTRL | (k << 9)), val); > + } > + > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > + val &= ~CMA_REF_CLK_SEL_MASK; > + /* > + * single ended reference clock (val |= 0x0030); > + * differential clock (val |= 0x0000); > + * for differential clock on the refclk_p and > + * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 > + * cdns_phy_reg_write(cdns_phy, CMN_DIAG_ACYA, 0x0100); > + */ > + val |= FIELD_PREP(CMA_REF_CLK_SEL_MASK, 3); > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); > + > + /* Deassert PHY reset */ > + cdns_phy_reg_write(cdns_phy, PHY_ISO_CMN_CTRL, 0x0001); > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_CMN_CTRL, 0x0003); > + > + /* Power state machine registers */ > + for (k = 0; k < num_lanes; k++) > + cdns_phy_reg_write(cdns_phy, XCVR_PSM_RCTRL | (k << 9), 0xfefc); > + > + /* Assert cmn_macro_pwr_en */ > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_CMN_CTRL, 0x0013); > + > + /* wait for cmn_macro_pwr_en_ack */ > + if (wait_for_ack(cdns_phy, PHY_PMA_ISO_CMN_CTRL, CMN_MACRO_PWR_EN_ACK, > + "MA output macro power up failed")) > + return -1; Return the error value of wait_for_ack. > + /* wait for cmn_ready */ > + if (wait_for_ack(cdns_phy, PHY_PMA_CMN_CTRL1, CMN_READY, > + "PMA output ready failed")) > + return -1; Return the error value of wait_for_ack. > + for (k = 0; k < num_lanes; k++) { > + cdns_phy_reg_write(cdns_phy, TX_PSC_A0 | (k << 9), 0x6791); > + cdns_phy_reg_write(cdns_phy, TX_PSC_A1 | (k << 9), 0x6790); > + cdns_phy_reg_write(cdns_phy, TX_PSC_A2 | (k << 9), 0x0090); > + cdns_phy_reg_write(cdns_phy, TX_PSC_A3 | (k << 9), 0x0090); > + > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_CAL | (k << 9)); > + val &= 0xffbb; > + cdns_phy_reg_write(cdns_phy, RX_PSC_CAL | (k << 9), val); > + > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_A0 | (k << 9)); > + val &= 0xffbb; > + cdns_phy_reg_write(cdns_phy, RX_PSC_A0 | (k << 9), val); > + } > + return 0; > +} > + > +static int hdptx_hdmi_phy_cfg(struct cdns_hdptx_hdmi_phy *cdns_phy, u32 > rate) +{ > + const struct hdptx_hdmi_ctrl *p_ctrl_table; > + const struct hdptx_hdmi_pll_tuning *p_pll_table; > + const u32 refclk_freq_khz = cdns_phy->ref_clk_rate / 1000; > + const u8 pclk_in = false; const bool pclk_in = false; > + u32 pixel_freq = rate; > + u32 vco_freq, char_freq; > + u32 div_total, feedback_factor; > + u32 i, ret; > + > + feedback_factor = hdptx_hdmi_feedback_factor(cdns_phy); > + > + char_freq = pixel_freq * feedback_factor / 1000; > + > + dev_dbg(cdns_phy->dev, > + "Pixel clock: (%d KHz), character clock: %d, bpc is (%0d- bit)\n", > + pixel_freq, char_freq, cdns_phy->bpc); > + > + /* Get right row from the ctrl_table table. > + * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ column. > + * Consider only the rows with FEEDBACK_FACTOR column matching > feedback_factor. + */ > + for (i = 0; i < ARRAY_SIZE(pixel_clk_output_ctrl_table); i++) { > + if (feedback_factor == pixel_clk_output_ctrl_table[i].feedback_factor && > + pixel_freq == pixel_clk_output_ctrl_table[i].pixel_clk_freq_min) { > + p_ctrl_table = &pixel_clk_output_ctrl_table[i]; > + break; > + } > + } > + if (i == ARRAY_SIZE(pixel_clk_output_ctrl_table)) { > + dev_warn(cdns_phy->dev, > + "Pixel clk (%d KHz) not supported, bpc is (%0d- bit)\n", > + pixel_freq, cdns_phy->bpc); > + return 0; Returning 0 doesn't seem correct. The caller checks for small than 0. I suggest returning 0 if configuration was successful, and some error code otherwise. > + } > + > + div_total = p_ctrl_table->pll_fb_div_total; > + vco_freq = refclk_freq_khz * div_total / p_ctrl_table- >cmnda_pll0_ip_div; > + > + /* Get right row from the pixel_clk_output_pll_table table. > + * Check if vco_freq_khz and feedback_div_total > + * column matching with pixel_clk_output_pll_table. > + */ > + for (i = 0; i < ARRAY_SIZE(pixel_clk_output_pll_table); i++) { > + if (vco_freq == pixel_clk_output_pll_table[i].vco_freq_min && > + div_total == pixel_clk_output_pll_table[i].feedback_div_total) { > + p_pll_table = &pixel_clk_output_pll_table[i]; > + break; > + } > + } > + if (i == ARRAY_SIZE(pixel_clk_output_pll_table)) { > + dev_warn(cdns_phy->dev, "VCO (%d KHz) not supported\n", vco_freq); > + return -1; return -EINVAL? > + } > + dev_dbg(cdns_phy->dev, "VCO frequency is (%d KHz)\n", vco_freq); > + > + ret = hdptx_hdmi_phy_config(cdns_phy, p_ctrl_table, p_pll_table, pclk_in); > + if (ret < 0) > + return ret; > + > + return char_freq; See above. There is no need to return the character clock here. > +} > + > +static int hdptx_hdmi_phy_power_up(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + /* set Power State to A2 */ > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A2); > + > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_0, 1); > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_1, 1); > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_2, 1); > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_3, 1); > + > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A2_ACK, > + "Wait A2 Ack failed")) > + return -1; Return the error value of wait_for_ack. > + > + /* Power up ARC */ > + hdptx_hdmi_arc_config(cdns_phy); > + > + /* Configure PHY in A0 mode (PHY must be in the A0 power > + * state in order to transmit data) > + */ > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A0); > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A0_ACK, > + "Wait A0 Ack failed")) > + return -1; Return the error value of wait_for_ack. > + > + return 0; > +} > + > +static int hdptx_hdmi_phy_power_down(struct cdns_hdptx_hdmi_phy *cdns_phy) > +{ > + u32 val; > + > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_MODE_CTRL); > + val &= ~(POWER_STATE_A0 | POWER_STATE_A1 | POWER_STATE_A2 | > POWER_STATE_A3); + /* PHY_DP_MODE_CTL set to A3 power state */ > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, val | POWER_STATE_A3); > + > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, POWER_STATE_A3_ACK, > + "Wait A3 Ack failed")) > + return -1; Return the error value of wait_for_ack. > + > + return 0; > +} > + > +static int cdns_hdptx_hdmi_phy_on(struct phy *phy) > +{ > + struct cdns_hdptx_hdmi_phy *cdns_phy = phy_get_drvdata(phy); > + > + return hdptx_hdmi_phy_power_up(cdns_phy); > +} > + > +static int cdns_hdptx_hdmi_phy_off(struct phy *phy) > +{ > + struct cdns_hdptx_hdmi_phy *cdns_phy = phy_get_drvdata(phy); > + > + hdptx_hdmi_phy_power_down(cdns_phy); > + return 0; > +} > + > +int cdns_hdptx_hdmi_phy_valid(struct phy *phy, enum phy_mode mode, int > submode, + union phy_configure_opts *opts) > +{ > + u32 rate = opts->hdmi.pixel_clk_rate; > + int i; > + > + for (i = 0; i < ARRAY_SIZE(pixel_clk_output_ctrl_table); i++) > + if (rate == pixel_clk_output_ctrl_table[i].pixel_clk_freq_min) > + return 0; > + > + return -EINVAL; > +} > + > +static int cdns_hdptx_hdmi_phy_init(struct phy *phy) > +{ > + return 0; > +} > + > +static int cdns_hdptx_hdmi_configure(struct phy *phy, > + union phy_configure_opts *opts) > +{ > + struct cdns_hdptx_hdmi_phy *cdns_phy = phy_get_drvdata(phy); > + int ret; > + > + cdns_phy->pixel_clk_rate = opts->hdmi.pixel_clk_rate; > + cdns_phy->color_space = opts->hdmi.color_space; > + cdns_phy->bpc = opts->hdmi.bpc; > + > + /* Check HDMI FW alive before HDMI PHY init */ > + ret = hdptx_phy_check_alive(cdns_phy); > + if (!ret) { > + dev_err(cdns_phy->dev, "NO HDMI FW running\n"); > + return -ENXIO; > + } > + > + /* Configure PHY */ > + if (hdptx_hdmi_phy_cfg(cdns_phy, cdns_phy->pixel_clk_rate) < 0) { > + dev_err(cdns_phy->dev, "failed to set phy pclock\n"); > + return -EINVAL; > + } > + > + ret = hdptx_hdmi_phy_power_up(cdns_phy); > + if (ret < 0) > + return ret; > + > + hdptx_hdmi_phy_set_vswing(cdns_phy); > + > + return 0; > +} > + > +static const struct phy_ops cdns_hdptx_hdmi_phy_ops = { > + .init = cdns_hdptx_hdmi_phy_init, > + .configure = cdns_hdptx_hdmi_configure, > + .power_on = cdns_hdptx_hdmi_phy_on, > + .power_off = cdns_hdptx_hdmi_phy_off, > + .validate = cdns_hdptx_hdmi_phy_valid, > + .owner = THIS_MODULE, > +}; > + > +static int cdns_hdptx_hdmi_phy_probe(struct platform_device *pdev) > +{ > + struct cdns_hdptx_hdmi_phy *cdns_phy; > + struct device *dev = &pdev->dev; > + struct device_node *node = dev->of_node; > + struct phy_provider *phy_provider; > + struct resource *res; > + struct phy *phy; > + int ret; > + > + cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL); > + if (!cdns_phy) > + return -ENOMEM; > + > + dev_set_drvdata(dev, cdns_phy); > + cdns_phy->dev = dev; > + mutex_init(&cdns_phy->mbox_mutex); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENODEV; > + cdns_phy->regs = devm_ioremap(dev, res->start, resource_size(res)); > + if (IS_ERR(cdns_phy->regs)) > + return PTR_ERR(cdns_phy->regs); > + > + phy = devm_phy_create(dev, node, &cdns_hdptx_hdmi_phy_ops); > + if (IS_ERR(phy)) > + return PTR_ERR(phy); > + > + phy->attrs.mode = PHY_MODE_HDMI; > + > + cdns_phy->phy = phy; > + phy_set_drvdata(phy, cdns_phy); > + > + /* init base struct for access mhdp mailbox */ > + cdns_phy->base.dev = cdns_phy->dev; > + cdns_phy->base.regs = cdns_phy->regs; > + cdns_phy->base.mbox_mutex = &cdns_phy->mbox_mutex; The same as for the DP PHY driver applies here. Best regards, Alexander > + > + ret = hdptx_hdmi_clk_enable(cdns_phy); > + if (ret) { > + dev_err(dev, "Init clk fail\n"); > + return -EINVAL; > + } > + > + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); > + if (IS_ERR(phy_provider)) { > + ret = PTR_ERR(phy_provider); > + goto clk_disable; > + } > + > + dev_dbg(dev, "probe success!\n"); > + > + return 0; > + > +clk_disable: > + hdptx_hdmi_clk_disable(cdns_phy); > + > + return -EINVAL; > +} > + > +static int cdns_hdptx_hdmi_phy_remove(struct platform_device *pdev) > +{ > + struct cdns_hdptx_hdmi_phy *cdns_phy = platform_get_drvdata(pdev); > + > + hdptx_hdmi_clk_disable(cdns_phy); > + > + return 0; > +} > + > +static const struct of_device_id cdns_hdptx_hdmi_phy_of_match[] = { > + {.compatible = "fsl,imx8mq-hdmi-phy" }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, cdns_hdptx_hdmi_phy_of_match); > + > +static struct platform_driver cdns_hdptx_hdmi_phy_driver = { > + .probe = cdns_hdptx_hdmi_phy_probe, > + .remove = cdns_hdptx_hdmi_phy_remove, > + .driver = { > + .name = "cdns-hdptx-hdmi-phy", > + .of_match_table = cdns_hdptx_hdmi_phy_of_match, > + } > +}; > +module_platform_driver(cdns_hdptx_hdmi_phy_driver); > + > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > +MODULE_DESCRIPTION("Cadence HDP-TX HDMI PHY driver"); > +MODULE_LICENSE("GPL");
Hi Alexander, Thanks for your comments, > > Hi Sandor, > > thanks for the update. > > One small typo in the commit message: 'Creat' -> 'Create' > > Am Dienstag, 17. Oktober 2023, 09:03:57 CEST schrieb Sandor Yu: > > MHDP8546 mailbox access functions will be share to other mhdp driver > > and Cadence HDP-TX HDMI/DP PHY drivers. > > Create a new mhdp helper driver and move all those functions into. > > > > cdns_mhdp_reg_write() is renamed to cdns_mhdp_dp_reg_write(), because > > it use the DPTX command ID DPTX_WRITE_REGISTER. > > > > New cdns_mhdp_reg_write() is created with the general command ID > > GENERAL_REGISTER_WRITE. > > > > rewrite cdns_mhdp_set_firmware_active() in mhdp8546 core driver, use > > cdns_mhdp_mailbox_send() to replace cdns_mhdp_mailbox_write() same > as > > the other mailbox access functions. > > > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > > --- > > v10->v11: > > - rewrite cdns_mhdp_set_firmware_active() in mhdp8546 core driver, use > > cdns_mhdp_mailbox_send() to replace cdns_mhdp_mailbox_write() same > as > > the other mailbox access functions. > > - use static for cdns_mhdp_mailbox_write() and > > cdns_mhdp_mailbox_read() and remove them from > EXPORT_SYMBOL_GPL(). > > > > v9->v10: > > *use mhdp helper driver to replace macro functions, move maibox > > access function and mhdp hdmi/dp common API functions into the > > driver. > > > > drivers/gpu/drm/bridge/cadence/Kconfig | 4 + > > drivers/gpu/drm/bridge/cadence/Makefile | 1 + > > .../gpu/drm/bridge/cadence/cdns-mhdp-helper.c | 304 +++++++++++++ > > .../drm/bridge/cadence/cdns-mhdp8546-core.c | 403 +++--------------- > > .../drm/bridge/cadence/cdns-mhdp8546-core.h | 44 +- > > include/drm/bridge/cdns-mhdp-helper.h | 94 ++++ > > 6 files changed, 476 insertions(+), 374 deletions(-) create mode > > 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c > > create mode 100644 include/drm/bridge/cdns-mhdp-helper.h > > > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > > b/drivers/gpu/drm/bridge/cadence/Kconfig index > > ec35215a20034..0b7b4626a7af0 > > 100644 > > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig > > @@ -20,6 +20,9 @@ config DRM_CDNS_DSI_J721E > > the routing of the DSS DPI signal to the Cadence DSI. > > endif > > > > +config CDNS_MHDP_HELPER > > + tristate > > + > > config DRM_CDNS_MHDP8546 > > tristate "Cadence DPI/DP bridge" > > select DRM_DISPLAY_DP_HELPER > > @@ -27,6 +30,7 @@ config DRM_CDNS_MHDP8546 > > select DRM_DISPLAY_HELPER > > select DRM_KMS_HELPER > > select DRM_PANEL_BRIDGE > > + select CDNS_MHDP_HELPER > > depends on OF > > help > > Support Cadence DPI to DP bridge. This is an internal diff > > --git a/drivers/gpu/drm/bridge/cadence/Makefile > > b/drivers/gpu/drm/bridge/cadence/Makefile index > > c95fd5b81d137..087dc074820d7 100644 > > --- a/drivers/gpu/drm/bridge/cadence/Makefile > > +++ b/drivers/gpu/drm/bridge/cadence/Makefile > > @@ -2,6 +2,7 @@ > > obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o cdns-dsi-y := > > cdns-dsi-core.o > > cdns-dsi-$(CONFIG_DRM_CDNS_DSI_J721E) += cdns-dsi-j721e.o > > +obj-$(CONFIG_CDNS_MHDP_HELPER) += cdns-mhdp-helper.o > > obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o > cdns-mhdp8546-y > > := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o > > cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += > > cdns-mhdp8546-j721e.o diff --git > > a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c > > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c new file mode > > 100644 index 0000000000000..8cabd79ad9663 > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c > > @@ -0,0 +1,304 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Copyright (C) 2023 NXP Semiconductor, Inc. > > + * > > + */ > > +#include <drm/bridge/cdns-mhdp-helper.h> #include > > +<linux/dev_printk.h> #include <linux/module.h> > > + > > +/* Mailbox helper functions */ > > +static int cdns_mhdp_mailbox_read(struct cdns_mhdp_base *base) { > > + int ret, empty; > > + > > + WARN_ON(!mutex_is_locked(base->mbox_mutex)); > > Actually this should be moved to cdns_mhdp_mailbox_recv_header() and > cdns_mhdp_mailbox_recv_data(). Yes, it could be moved to cdns_mhdp_mailbox_send() only and removed from cdns_mhdp_mailbox_read() cdns_mhdp_mailbox_write(), but those mailbox access functions are move from cdns-mhdp8546-core.c, I think it had better keep the same implement in this patch set. Another patch may need to fix it. > > > + ret = readx_poll_timeout(readl, base->regs + > CDNS_MAILBOX_EMPTY, > > + empty, !empty, MAILBOX_RETRY_US, > > + MAILBOX_TIMEOUT_US); > > + if (ret < 0) > > + return ret; > > + > > + return readl(base->regs + CDNS_MAILBOX_RX_DATA) & 0xff; } > > + > > +static int cdns_mhdp_mailbox_write(struct cdns_mhdp_base *base, u8 > > +val) { > > + int ret, full; > > + > > + WARN_ON(!mutex_is_locked(base->mbox_mutex)); > > I think this should be moved to cdns_mhdp_mailbox_send() as well. Same reply as above. > > > + ret = readx_poll_timeout(readl, base->regs + CDNS_MAILBOX_FULL, > > + full, !full, MAILBOX_RETRY_US, > > + MAILBOX_TIMEOUT_US); > > + if (ret < 0) > > + return ret; > > + > > + writel(val, base->regs + CDNS_MAILBOX_TX_DATA); > > + > > + return 0; > > +} > > Nice, much better having these as static now. Thanks. > > > +int cdns_mhdp_mailbox_recv_header(struct cdns_mhdp_base *base, > > + u8 module_id, u8 opcode, > > + u16 req_size) { > > + u32 mbox_size, i; > > + u8 header[4]; > > + int ret; > > + > > + /* read the header of the message */ > > + for (i = 0; i < sizeof(header); i++) { > > + ret = cdns_mhdp_mailbox_read(base); > > + if (ret < 0) > > + return ret; > > + > > + header[i] = ret; > > + } > > + > > + mbox_size = get_unaligned_be16(header + 2); > > + > > + if (opcode != header[0] || module_id != header[1] || > > + req_size != mbox_size) { > > + /* > > + * If the message in mailbox is not what we want, we > > + need > to > > + * clear the mailbox by reading its contents. > > + */ > > + for (i = 0; i < mbox_size; i++) > > + if (cdns_mhdp_mailbox_read(base) < 0) > > + break; > > + > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_recv_header); > > + > > +int cdns_mhdp_mailbox_recv_data(struct cdns_mhdp_base *base, > > + u8 *buff, u16 buff_size) { > > + u32 i; > > + int ret; > > + > > + for (i = 0; i < buff_size; i++) { > > + ret = cdns_mhdp_mailbox_read(base); > > + if (ret < 0) > > + return ret; > > + > > + buff[i] = ret; > > + } > > + > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_recv_data); > > + > > +int cdns_mhdp_mailbox_send(struct cdns_mhdp_base *base, u8 > module_id, > > + u8 opcode, u16 size, u8 *message) { > > + u8 header[4]; > > + int ret, i; > > + > > + header[0] = opcode; > > + header[1] = module_id; > > + put_unaligned_be16(size, header + 2); > > + > > + for (i = 0; i < sizeof(header); i++) { > > + ret = cdns_mhdp_mailbox_write(base, header[i]); > > + if (ret) > > + return ret; > > + } > > + > > + for (i = 0; i < size; i++) { > > + ret = cdns_mhdp_mailbox_write(base, message[i]); > > + if (ret) > > + return ret; > > + } > > + > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_send); > > + > > +/* General helper functions */ > > +int cdns_mhdp_reg_read(struct cdns_mhdp_base *base, u32 addr, u32 > > +*value) { > > + u8 msg[4], resp[8]; > > + int ret; > > + > > + put_unaligned_be32(addr, msg); > > + > > + mutex_lock(base->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_GENERAL, > > + GENERAL_REGISTER_READ, > > + sizeof(msg), msg); > > + if (ret) > > + goto out; > > + > > + ret = cdns_mhdp_mailbox_recv_header(base, > MB_MODULE_ID_GENERAL, > > + > GENERAL_REGISTER_READ, > > + sizeof(resp)); > > + if (ret) > > + goto out; > > + > > + ret = cdns_mhdp_mailbox_recv_data(base, resp, sizeof(resp)); > > + if (ret) > > + goto out; > > + > > + /* Returned address value should be the same as requested */ > > + if (memcmp(msg, resp, sizeof(msg))) { > > + ret = -EINVAL; > > + goto out; > > + } > > + > > + *value = get_unaligned_be32(resp + 4); > > + > > +out: > > + mutex_unlock(base->mbox_mutex); > > + if (ret) { > > + dev_err(base->dev, "Failed to read register\n"); > > + *value = 0; > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_reg_read); > > + > > +int cdns_mhdp_reg_write(struct cdns_mhdp_base *base, u32 addr, u32 > > +val) { > > + u8 msg[8]; > > + int ret; > > + > > + put_unaligned_be32(addr, msg); > > + put_unaligned_be32(val, msg + 4); > > + > > + mutex_lock(base->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_GENERAL, > > + GENERAL_REGISTER_WRITE, > > + sizeof(msg), msg); > > + > > + mutex_unlock(base->mbox_mutex); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_reg_write); > > + > > +/* DPTX helper functions */ > > +int cdns_mhdp_dp_reg_write(struct cdns_mhdp_base *base, u16 addr, > u32 > > +val) { > > + u8 msg[6]; > > + int ret; > > + > > + put_unaligned_be16(addr, msg); > > + put_unaligned_be32(val, msg + 2); > > + > > + mutex_lock(base->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > > + DPTX_WRITE_REGISTER, > sizeof(msg), > msg); > > + > > + mutex_unlock(base->mbox_mutex); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_dp_reg_write); > > + > > +int cdns_mhdp_dp_reg_write_bit(struct cdns_mhdp_base *base, u16 addr, > > + u8 start_bit, u8 bits_no, u32 val) { > > + u8 field[8]; > > + int ret; > > + > > + put_unaligned_be16(addr, field); > > + field[2] = start_bit; > > + field[3] = bits_no; > > + put_unaligned_be32(val, field + 4); > > + > > + mutex_lock(base->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > > + DPTX_WRITE_FIELD, > sizeof(field), > field); > > + > > + mutex_unlock(base->mbox_mutex); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_dp_reg_write_bit); > > + > > +int cdns_mhdp_dpcd_read(struct cdns_mhdp_base *base, > > + u32 addr, u8 *data, u16 len) { > > + u8 msg[5], reg[5]; > > + int ret; > > + > > + put_unaligned_be16(len, msg); > > + put_unaligned_be24(addr, msg + 2); > > + > > + mutex_lock(base->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > > + DPTX_READ_DPCD, sizeof(msg), > msg); > > + if (ret) > > + goto out; > > + > > + ret = cdns_mhdp_mailbox_recv_header(base, > MB_MODULE_ID_DP_TX, > > + DPTX_READ_DPCD, > > + sizeof(reg) + len); > > + if (ret) > > + goto out; > > + > > + ret = cdns_mhdp_mailbox_recv_data(base, reg, sizeof(reg)); > > + if (ret) > > + goto out; > > + > > + ret = cdns_mhdp_mailbox_recv_data(base, data, len); > > + > > +out: > > + mutex_unlock(base->mbox_mutex); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_dpcd_read); > > + > > +int cdns_mhdp_dpcd_write(struct cdns_mhdp_base *base, u32 addr, u8 > > +value) { > > + u8 msg[6], reg[5]; > > + int ret; > > + > > + put_unaligned_be16(1, msg); > > + put_unaligned_be24(addr, msg + 2); > > + msg[5] = value; > > + > > + mutex_lock(base->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX, > > + DPTX_WRITE_DPCD, > sizeof(msg), > msg); > > + if (ret) > > + goto out; > > + > > + ret = cdns_mhdp_mailbox_recv_header(base, > MB_MODULE_ID_DP_TX, > > + DPTX_WRITE_DPCD, > sizeof(reg)); > > + if (ret) > > + goto out; > > + > > + ret = cdns_mhdp_mailbox_recv_data(base, reg, sizeof(reg)); > > + if (ret) > > + goto out; > > + > > + if (addr != get_unaligned_be24(reg + 2)) > > + ret = -EINVAL; > > No need to have the mutex locked while doing this check. This should be > moved below 'out' label. It is to check the recv data for cdns_mhdp_mailbox_recv_data() only, if moved below "out" label, it will run for every maibox failed. So it have to be keep in mutex lock. > > > +out: > > + mutex_unlock(base->mbox_mutex); > > + > > + if (ret) > > + dev_err(base->dev, "dpcd write failed: %d\n", ret); > > + return ret; > > +} > > +EXPORT_SYMBOL_GPL(cdns_mhdp_dpcd_write); > > + > > +MODULE_DESCRIPTION("Cadence MHDP Helper driver"); > > +MODULE_AUTHOR("Sandor Yu <Sandor.yu@nxp.com>"); > > +MODULE_LICENSE("GPL"); > > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index > > 6af565ac307ae..9d9dbb976868c 100644 > > --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > > @@ -73,298 +73,28 @@ static void cdns_mhdp_bridge_hpd_disable(struct > > drm_bridge *bridge) mhdp->regs + CDNS_APB_INT_MASK); } > > > > -static int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) -{ > > - int ret, empty; > > - > > - WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); > > - > > - ret = readx_poll_timeout(readl, mhdp->regs + > CDNS_MAILBOX_EMPTY, > > - empty, !empty, MAILBOX_RETRY_US, > > - MAILBOX_TIMEOUT_US); > > - if (ret < 0) > > - return ret; > > - > > - return readl(mhdp->regs + CDNS_MAILBOX_RX_DATA) & 0xff; > > -} > > - > > -static int cdns_mhdp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 > > val) -{ > > - int ret, full; > > - > > - WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); > > - > > - ret = readx_poll_timeout(readl, mhdp->regs + > CDNS_MAILBOX_FULL, > > - full, !full, MAILBOX_RETRY_US, > > - MAILBOX_TIMEOUT_US); > > - if (ret < 0) > > - return ret; > > - > > - writel(val, mhdp->regs + CDNS_MAILBOX_TX_DATA); > > - > > - return 0; > > -} > > - > > -static int cdns_mhdp_mailbox_recv_header(struct cdns_mhdp_device > *mhdp, > > - u8 module_id, u8 opcode, > > - u16 req_size) > > -{ > > - u32 mbox_size, i; > > - u8 header[4]; > > - int ret; > > - > > - /* read the header of the message */ > > - for (i = 0; i < sizeof(header); i++) { > > - ret = cdns_mhdp_mailbox_read(mhdp); > > - if (ret < 0) > > - return ret; > > - > > - header[i] = ret; > > - } > > - > > - mbox_size = get_unaligned_be16(header + 2); > > - > > - if (opcode != header[0] || module_id != header[1] || > > - req_size != mbox_size) { > > - /* > > - * If the message in mailbox is not what we want, we need > to > > - * clear the mailbox by reading its contents. > > - */ > > - for (i = 0; i < mbox_size; i++) > > - if (cdns_mhdp_mailbox_read(mhdp) < 0) > > - break; > > - > > - return -EINVAL; > > - } > > - > > - return 0; > > -} > > - > > -static int cdns_mhdp_mailbox_recv_data(struct cdns_mhdp_device > *mhdp, > > - u8 *buff, u16 buff_size) > > -{ > > - u32 i; > > - int ret; > > - > > - for (i = 0; i < buff_size; i++) { > > - ret = cdns_mhdp_mailbox_read(mhdp); > > - if (ret < 0) > > - return ret; > > - > > - buff[i] = ret; > > - } > > - > > - return 0; > > -} > > - > > -static int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 > > module_id, - u8 opcode, u16 size, u8 > *message) > > -{ > > - u8 header[4]; > > - int ret, i; > > - > > - header[0] = opcode; > > - header[1] = module_id; > > - put_unaligned_be16(size, header + 2); > > - > > - for (i = 0; i < sizeof(header); i++) { > > - ret = cdns_mhdp_mailbox_write(mhdp, header[i]); > > - if (ret) > > - return ret; > > - } > > - > > - for (i = 0; i < size; i++) { > > - ret = cdns_mhdp_mailbox_write(mhdp, message[i]); > > - if (ret) > > - return ret; > > - } > > - > > - return 0; > > -} > > - > > -static > > -int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr, u32 > > *value) -{ > > - u8 msg[4], resp[8]; > > - int ret; > > - > > - put_unaligned_be32(addr, msg); > > - > > - mutex_lock(&mhdp->mbox_mutex); > > - > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, > > - GENERAL_REGISTER_READ, > > - sizeof(msg), msg); > > - if (ret) > > - goto out; > > - > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_GENERAL, > > - > GENERAL_REGISTER_READ, > > - sizeof(resp)); > > - if (ret) > > - goto out; > > - > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, resp, sizeof(resp)); > > - if (ret) > > - goto out; > > - > > - /* Returned address value should be the same as requested */ > > - if (memcmp(msg, resp, sizeof(msg))) { > > - ret = -EINVAL; > > - goto out; > > - } > > - > > - *value = get_unaligned_be32(resp + 4); > > - > > -out: > > - mutex_unlock(&mhdp->mbox_mutex); > > - if (ret) { > > - dev_err(mhdp->dev, "Failed to read register\n"); > > - *value = 0; > > - } > > - > > - return ret; > > -} > > - > > -static > > -int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u16 addr, u32 > > val) -{ > > - u8 msg[6]; > > - int ret; > > - > > - put_unaligned_be16(addr, msg); > > - put_unaligned_be32(val, msg + 2); > > - > > - mutex_lock(&mhdp->mbox_mutex); > > - > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > > - DPTX_WRITE_REGISTER, > sizeof(msg), > msg); > > - > > - mutex_unlock(&mhdp->mbox_mutex); > > - > > - return ret; > > -} > > - > > static > > -int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr, > > - u8 start_bit, u8 bits_no, u32 val) > > -{ > > - u8 field[8]; > > - int ret; > > - > > - put_unaligned_be16(addr, field); > > - field[2] = start_bit; > > - field[3] = bits_no; > > - put_unaligned_be32(val, field + 4); > > - > > - mutex_lock(&mhdp->mbox_mutex); > > - > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > > - DPTX_WRITE_FIELD, > sizeof(field), > field); > > - > > - mutex_unlock(&mhdp->mbox_mutex); > > - > > - return ret; > > -} > > - > > -static > > -int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp, > > - u32 addr, u8 *data, u16 len) > > -{ > > - u8 msg[5], reg[5]; > > - int ret; > > - > > - put_unaligned_be16(len, msg); > > - put_unaligned_be24(addr, msg + 2); > > - > > - mutex_lock(&mhdp->mbox_mutex); > > - > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > > - DPTX_READ_DPCD, sizeof(msg), > msg); > > - if (ret) > > - goto out; > > - > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_DP_TX, > > - DPTX_READ_DPCD, > > - sizeof(reg) + len); > > - if (ret) > > - goto out; > > - > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > > - if (ret) > > - goto out; > > - > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, data, len); > > - > > -out: > > - mutex_unlock(&mhdp->mbox_mutex); > > - > > - return ret; > > -} > > - > > -static > > -int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 > > value) > > +int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, > bool > > enable) { > > - u8 msg[6], reg[5]; > > + u8 status; > > int ret; > > > > - put_unaligned_be16(1, msg); > > - put_unaligned_be24(addr, msg + 2); > > - msg[5] = value; > > - > > mutex_lock(&mhdp->mbox_mutex); > > > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > > - DPTX_WRITE_DPCD, > sizeof(msg), > msg); > > - if (ret) > > - goto out; > > - > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_DP_TX, > > - DPTX_WRITE_DPCD, > sizeof(reg)); > > - if (ret) > > - goto out; > > + status = enable ? FW_ACTIVE : FW_STANDBY; > > Initialising status can be done outside of the locked mutex. OK. > > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, sizeof(reg)); > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_GENERAL, > > + GENERAL_MAIN_CONTROL, > sizeof(status), &status); > > if (ret) > > goto out; > > > > - if (addr != get_unaligned_be24(reg + 2)) > > - ret = -EINVAL; > > - > > -out: > > - mutex_unlock(&mhdp->mbox_mutex); > > - > > - if (ret) > > - dev_err(mhdp->dev, "dpcd write failed: %d\n", ret); > > - return ret; > > -} > > - > > -static > > -int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, > bool > > enable) -{ > > - u8 msg[5]; > > - int ret, i; > > - > > - msg[0] = GENERAL_MAIN_CONTROL; > > - msg[1] = MB_MODULE_ID_GENERAL; > > - msg[2] = 0; > > - msg[3] = 1; > > - msg[4] = enable ? FW_ACTIVE : FW_STANDBY; > > - > > - mutex_lock(&mhdp->mbox_mutex); > > - > > - for (i = 0; i < sizeof(msg); i++) { > > - ret = cdns_mhdp_mailbox_write(mhdp, msg[i]); > > - if (ret) > > - goto out; > > - } > > - > > - /* read the firmware state */ > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, msg, sizeof(msg)); > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_GENERAL, > > + > GENERAL_MAIN_CONTROL, > > + sizeof(status)); > > if (ret) > > goto out; > > > > - ret = 0; > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &status, > sizeof(status)); > > > > out: > > mutex_unlock(&mhdp->mbox_mutex); @@ -382,18 +112,18 @@ > int > > cdns_mhdp_get_hpd_status(struct cdns_mhdp_device > > *mhdp) > > > > mutex_lock(&mhdp->mbox_mutex); > > > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > DPTX_HPD_STATE, 0, NULL); > > if (ret) > > goto err_get_hpd; > > > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > > + MB_MODULE_ID_DP_TX, > > DPTX_HPD_STATE, > > sizeof(status)); > > if (ret) > > goto err_get_hpd; > > > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, &status, sizeof(status)); > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &status, > sizeof(status)); > > if (ret) > > goto err_get_hpd; > > > > @@ -424,22 +154,22 @@ int cdns_mhdp_get_edid_block(void *data, u8 > *edid, > > msg[0] = block / 2; > > msg[1] = block % 2; > > > > - ret = cdns_mhdp_mailbox_send(mhdp, > MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > DPTX_GET_EDID, > sizeof(msg), msg); > > if (ret) > > continue; > > > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_DP_TX, > > > DPTX_GET_EDID, > > sizeof(reg) + > length); > > if (ret) > > continue; > > > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, reg, > sizeof(reg)); > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, reg, > sizeof(reg)); > > if (ret) > > continue; > > > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, edid, length); > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, edid, > length); > > if (ret) > > continue; > > > > @@ -464,17 +194,17 @@ int cdns_mhdp_read_hpd_event(struct > > cdns_mhdp_device > > *mhdp) > > > > mutex_lock(&mhdp->mbox_mutex); > > > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > DPTX_READ_EVENT, 0, NULL); > > if (ret) > > goto out; > > > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > > + MB_MODULE_ID_DP_TX, > > DPTX_READ_EVENT, > sizeof(event)); > > if (ret < 0) > > goto out; > > > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, &event, sizeof(event)); > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &event, > sizeof(event)); > > out: > > mutex_unlock(&mhdp->mbox_mutex); > > > > @@ -512,20 +242,20 @@ int cdns_mhdp_adjust_lt(struct > cdns_mhdp_device > > *mhdp, unsigned int nlanes, > > > > mutex_lock(&mhdp->mbox_mutex); > > > > - ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > DPTX_ADJUST_LT, > > sizeof(payload), payload); > > if (ret) > > goto out; > > > > /* Yes, read the DPCD read command response */ > > - ret = cdns_mhdp_mailbox_recv_header(mhdp, > MB_MODULE_ID_DP_TX, > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > > + MB_MODULE_ID_DP_TX, > > DPTX_READ_DPCD, > > sizeof(hdr) + > DP_LINK_STATUS_SIZE); > > if (ret) > > goto out; > > > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, hdr, sizeof(hdr)); > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, hdr, > > + sizeof(hdr)); > > if (ret) > > goto out; > > > > @@ -533,7 +263,7 @@ int cdns_mhdp_adjust_lt(struct cdns_mhdp_device > > *mhdp, unsigned int nlanes, if (addr != DP_LANE0_1_STATUS) > > goto out; > > > > - ret = cdns_mhdp_mailbox_recv_data(mhdp, link_status, > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, link_status, > > DP_LINK_STATUS_SIZE); > > > > out: > > @@ -847,7 +577,7 @@ static ssize_t cdns_mhdp_transfer(struct > > drm_dp_aux *aux, unsigned int i; > > > > for (i = 0; i < msg->size; ++i) { > > - ret = cdns_mhdp_dpcd_write(mhdp, > > + ret = cdns_mhdp_dpcd_write(&mhdp->base, > > msg->address > + > i, buf[i]); > > if (!ret) > > continue; @@ -859,7 +589,7 @@ > static > > ssize_t cdns_mhdp_transfer(struct drm_dp_aux *aux, return ret; > > } > > } else { > > - ret = cdns_mhdp_dpcd_read(mhdp, msg->address, > > + ret = cdns_mhdp_dpcd_read(&mhdp->base, > msg->address, > > msg->buffer, msg->size); > > if (ret) { > > dev_err(mhdp->dev, @@ -887,12 +617,12 @@ > static > > int cdns_mhdp_link_training_init(struct > > cdns_mhdp_device *mhdp) if (!mhdp->host.scrambler) > > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, > reg32); > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_ENHNCD, > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_ENHNCD, > > mhdp->sink.enhanced & > mhdp->host.enhanced); > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_LANE_EN, > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_LANE_EN, > > CDNS_DP_LANE_EN_LANES(mhdp- > >link.num_lanes)); > > > > cdns_mhdp_link_configure(&mhdp->aux, &mhdp->link); @@ -913,7 > > +643,7 @@ static int cdns_mhdp_link_training_init(struct > > cdns_mhdp_device *mhdp) return ret; > > } > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, > > CDNS_PHY_COMMON_CONFIG | > > CDNS_PHY_TRAINING_EN | > > CDNS_PHY_TRAINING_TYPE(1) | @@ > -1058,7 > > +788,7 @@ static bool cdns_mhdp_link_training_channel_eq(struct > > cdns_mhdp_device *mhdp, CDNS_PHY_TRAINING_TYPE(eq_tps); > > if (eq_tps != 4) > > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, > reg32); > > > > drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET, > > (eq_tps != 4) ? eq_tps | > DP_LINK_SCRAMBLING_DISABLE : > > @@ -1322,7 +1052,7 @@ static int cdns_mhdp_link_training(struct > > cdns_mhdp_device *mhdp, mhdp->host.scrambler ? 0 : > > DP_LINK_SCRAMBLING_DISABLE); > > > > - ret = cdns_mhdp_reg_read(mhdp, > CDNS_DP_FRAMER_GLOBAL_CONFIG, > ®32); > > + ret = cdns_mhdp_reg_read(&mhdp->base, > > + CDNS_DP_FRAMER_GLOBAL_CONFIG, > > ®32); if (ret < 0) { > > dev_err(mhdp->dev, > > "Failed to read > CDNS_DP_FRAMER_GLOBAL_CONFIG > %d\n", > > @@ -1333,13 +1063,13 @@ static int cdns_mhdp_link_training(struct > > cdns_mhdp_device *mhdp, reg32 |= > CDNS_DP_NUM_LANES(mhdp->link.num_lanes); > > reg32 |= CDNS_DP_WR_FAILING_EDGE_VSYNC; > > reg32 |= CDNS_DP_FRAMER_EN; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, > reg32); > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_FRAMER_GLOBAL_CONFIG, > reg32); > > > > /* Reset PHY config */ > > reg32 = CDNS_PHY_COMMON_CONFIG | > CDNS_PHY_TRAINING_TYPE(1); > > if (!mhdp->host.scrambler) > > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, > reg32); > > > > return 0; > > err: > > @@ -1347,7 +1077,7 @@ static int cdns_mhdp_link_training(struct > > cdns_mhdp_device *mhdp, reg32 = CDNS_PHY_COMMON_CONFIG | > > CDNS_PHY_TRAINING_TYPE(1); > > if (!mhdp->host.scrambler) > > reg32 |= CDNS_PHY_SCRAMBLER_BYPASS; > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_PHY_CONFIG, reg32); > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_PHY_CONFIG, > reg32); > > > > drm_dp_dpcd_writeb(&mhdp->aux, DP_TRAINING_PATTERN_SET, > > DP_TRAINING_PATTERN_DISABLE); @@ > -1461,7 > > +1191,7 @@ static int cdns_mhdp_link_up(struct cdns_mhdp_device > > *mhdp) mhdp->link.num_lanes = cdns_mhdp_max_num_lanes(mhdp); > > > > /* Disable framer for link training */ > > - err = cdns_mhdp_reg_read(mhdp, > CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp); > > + err = cdns_mhdp_reg_read(&mhdp->base, > > + CDNS_DP_FRAMER_GLOBAL_CONFIG, > > &resp); if (err < 0) { > > dev_err(mhdp->dev, > > "Failed to read > CDNS_DP_FRAMER_GLOBAL_CONFIG > %d\n", > > @@ -1470,7 +1200,7 @@ static int cdns_mhdp_link_up(struct > > cdns_mhdp_device > > *mhdp) } > > > > resp &= ~CDNS_DP_FRAMER_EN; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, > resp); > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_FRAMER_GLOBAL_CONFIG, > resp); > > > > /* Spread AMP if required, enable 8b/10b coding */ > > amp[0] = cdns_mhdp_get_ssc_supported(mhdp) ? > DP_SPREAD_AMP_0_5 : > > 0; @@ -1837,7 +1567,7 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, if (mode->flags & > DRM_MODE_FLAG_INTERLACE) > > bnd_hsync2vsync |= > CDNS_IP_DET_INTERLACE_FORMAT; > > > > - cdns_mhdp_reg_write(mhdp, > CDNS_BND_HSYNC2VSYNC(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > > + CDNS_BND_HSYNC2VSYNC(stream_id), > > bnd_hsync2vsync); > > > > hsync2vsync_pol_ctrl = 0; > > @@ -1845,10 +1575,10 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, hsync2vsync_pol_ctrl |= > > CDNS_H2V_HSYNC_POL_ACTIVE_LOW; > > if (mode->flags & DRM_MODE_FLAG_NVSYNC) > > hsync2vsync_pol_ctrl |= > CDNS_H2V_VSYNC_POL_ACTIVE_LOW; > > - cdns_mhdp_reg_write(mhdp, > CDNS_HSYNC2VSYNC_POL_CTRL(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_HSYNC2VSYNC_POL_CTRL(stream_id), > > hsync2vsync_pol_ctrl); > > > > - cdns_mhdp_reg_write(mhdp, > CDNS_DP_FRAMER_PXL_REPR(stream_id), > pxl_repr); > > + cdns_mhdp_reg_write(&mhdp->base, > > + CDNS_DP_FRAMER_PXL_REPR(stream_id), > > pxl_repr); > > > > if (mode->flags & DRM_MODE_FLAG_INTERLACE) > > dp_framer_sp |= CDNS_DP_FRAMER_INTERLACE; @@ > -1856,19 > > +1586,19 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, dp_framer_sp |= > CDNS_DP_FRAMER_HSYNC_POL_LOW; > > if (mode->flags & DRM_MODE_FLAG_NVSYNC) > > dp_framer_sp |= CDNS_DP_FRAMER_VSYNC_POL_LOW; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_SP(stream_id), > dp_framer_sp); > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_FRAMER_SP(stream_id), > > dp_framer_sp); > > > > front_porch = mode->crtc_hsync_start - mode->crtc_hdisplay; > > back_porch = mode->crtc_htotal - mode->crtc_hsync_end; > > - cdns_mhdp_reg_write(mhdp, > CDNS_DP_FRONT_BACK_PORCH(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_FRONT_BACK_PORCH(stream_id), > > CDNS_DP_FRONT_PORCH(front_porch) | > > CDNS_DP_BACK_PORCH(back_porch)); > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_BYTE_COUNT(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_BYTE_COUNT(stream_id), > > mode->crtc_hdisplay * bpp / 8); > > > > msa_h0 = mode->crtc_htotal - mode->crtc_hsync_start; > > - cdns_mhdp_reg_write(mhdp, > CDNS_DP_MSA_HORIZONTAL_0(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_MSA_HORIZONTAL_0(stream_id), > > > CDNS_DP_MSAH0_H_TOTAL(mode->crtc_htotal) | > > > CDNS_DP_MSAH0_HSYNC_START(msa_h0)); > > > > @@ -1877,11 +1607,11 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, > CDNS_DP_MSAH1_HDISP_WIDTH(mode->crtc_hdisplay); > > if (mode->flags & DRM_MODE_FLAG_NHSYNC) > > msa_horizontal_1 |= > CDNS_DP_MSAH1_HSYNC_POL_LOW; > > - cdns_mhdp_reg_write(mhdp, > CDNS_DP_MSA_HORIZONTAL_1(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_MSA_HORIZONTAL_1(stream_id), > > msa_horizontal_1); > > > > msa_v0 = mode->crtc_vtotal - mode->crtc_vsync_start; > > - cdns_mhdp_reg_write(mhdp, > CDNS_DP_MSA_VERTICAL_0(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > > + CDNS_DP_MSA_VERTICAL_0(stream_id), > > > CDNS_DP_MSAV0_V_TOTAL(mode->crtc_vtotal) | > > > CDNS_DP_MSAV0_VSYNC_START(msa_v0)); > > > > @@ -1890,7 +1620,7 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, > CDNS_DP_MSAV1_VDISP_WIDTH(mode->crtc_vdisplay); > > if (mode->flags & DRM_MODE_FLAG_NVSYNC) > > msa_vertical_1 |= CDNS_DP_MSAV1_VSYNC_POL_LOW; > > - cdns_mhdp_reg_write(mhdp, > CDNS_DP_MSA_VERTICAL_1(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > > + CDNS_DP_MSA_VERTICAL_1(stream_id), > > msa_vertical_1); > > > > if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && @@ > -1902,14 > > +1632,14 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, if (pxlfmt == DRM_COLOR_FORMAT_YCBCR420) > > misc1 = CDNS_DP_TEST_VSC_SDP; > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_MSA_MISC(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_MSA_MISC(stream_id), > > misc0 | (misc1 << 8)); > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_HORIZONTAL(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_HORIZONTAL(stream_id), > > CDNS_DP_H_HSYNC_WIDTH(hsync) | > > > CDNS_DP_H_H_TOTAL(mode->crtc_hdisplay)); > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_VERTICAL_0(stream_id), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_VERTICAL_0(stream_id), > > > CDNS_DP_V0_VHEIGHT(mode->crtc_vdisplay) | > > CDNS_DP_V0_VSTART(msa_v0)); > > > > @@ -1918,13 +1648,13 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, mode->crtc_vtotal % 2 == 0) > > dp_vertical_1 |= CDNS_DP_V1_VTOTAL_EVEN; > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_VERTICAL_1(stream_id), > dp_vertical_1); > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_VERTICAL_1(stream_id), > > dp_vertical_1); > > > > - cdns_mhdp_reg_write_bit(mhdp, CDNS_DP_VB_ID(stream_id), 2, 1, > > - (mode->flags & > DRM_MODE_FLAG_INTERLACE) ? > > - CDNS_DP_VB_ID_INTERLACED : 0); > > + cdns_mhdp_dp_reg_write_bit(&mhdp->base, > > + CDNS_DP_VB_ID(stream_id), 2, > 1, > > + (mode->flags & > DRM_MODE_FLAG_INTERLACE) ? > > + CDNS_DP_VB_ID_INTERLACED : > 0); > > > > - ret = cdns_mhdp_reg_read(mhdp, > CDNS_DP_FRAMER_GLOBAL_CONFIG, > &framer); > > + ret = cdns_mhdp_reg_read(&mhdp->base, > > + CDNS_DP_FRAMER_GLOBAL_CONFIG, > > &framer); if (ret < 0) { > > dev_err(mhdp->dev, > > "Failed to read > CDNS_DP_FRAMER_GLOBAL_CONFIG > %d\n", > > @@ -1933,7 +1663,7 @@ static void cdns_mhdp_configure_video(struct > > cdns_mhdp_device *mhdp, } > > framer |= CDNS_DP_FRAMER_EN; > > framer &= ~CDNS_DP_NO_VIDEO_MODE; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, > framer); > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_FRAMER_GLOBAL_CONFIG, > framer); > > } > > > > static void cdns_mhdp_sst_enable(struct cdns_mhdp_device *mhdp, @@ > > -1966,15 +1696,15 @@ static void cdns_mhdp_sst_enable(struct > > cdns_mhdp_device *mhdp, > > > > mhdp->stream_id = 0; > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_TU, > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_FRAMER_TU, > > CDNS_DP_FRAMER_TU_VS(vs) | > > CDNS_DP_FRAMER_TU_SIZE(tu_size) | > > CDNS_DP_FRAMER_TU_CNT_RST_EN); > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_LINE_THRESH(0), > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DP_LINE_THRESH(0), > > line_thresh & GENMASK(5, 0)); > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_STREAM_CONFIG_2(0), > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_STREAM_CONFIG_2(0), > > CDNS_DP_SC2_TU_VS_DIFF((tu_size - vs > > 3) ? > > 0 : tu_size - > vs)); > > > > @@ -2009,13 +1739,13 @@ static void cdns_mhdp_atomic_enable(struct > > drm_bridge *bridge, mhdp->info->ops->enable(mhdp); > > > > /* Enable VIF clock for stream 0 */ > > - ret = cdns_mhdp_reg_read(mhdp, CDNS_DPTX_CAR, &resp); > > + ret = cdns_mhdp_reg_read(&mhdp->base, CDNS_DPTX_CAR, > &resp); > > if (ret < 0) { > > dev_err(mhdp->dev, "Failed to read > CDNS_DPTX_CAR %d\n", > ret); > > goto out; > > } > > > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_CAR, > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_CAR, > > resp | CDNS_VIF_CLK_EN | > CDNS_VIF_CLK_RSTN); > > > > connector = drm_atomic_get_new_connector_for_encoder(state, > > @@ -2083,16 +1813,16 @@ static void cdns_mhdp_atomic_disable(struct > > drm_bridge *bridge, cdns_mhdp_hdcp_disable(mhdp); > > > > mhdp->bridge_enabled = false; > > - cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, > &resp); > > + cdns_mhdp_reg_read(&mhdp->base, > CDNS_DP_FRAMER_GLOBAL_CONFIG, > &resp); > > resp &= ~CDNS_DP_FRAMER_EN; > > resp |= CDNS_DP_NO_VIDEO_MODE; > > - cdns_mhdp_reg_write(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, > resp); > > + cdns_mhdp_reg_write(&mhdp->base, > CDNS_DP_FRAMER_GLOBAL_CONFIG, > resp); > > > > cdns_mhdp_link_down(mhdp); > > > > /* Disable VIF clock for stream 0 */ > > - cdns_mhdp_reg_read(mhdp, CDNS_DPTX_CAR, &resp); > > - cdns_mhdp_reg_write(mhdp, CDNS_DPTX_CAR, > > + cdns_mhdp_reg_read(&mhdp->base, CDNS_DPTX_CAR, &resp); > > + cdns_mhdp_reg_write(&mhdp->base, CDNS_DPTX_CAR, > > resp & ~(CDNS_VIF_CLK_EN | > CDNS_VIF_CLK_RSTN)); > > > > if (mhdp->info && mhdp->info->ops && mhdp->info->ops->disable) > > @@ -2502,6 +2232,11 @@ static int cdns_mhdp_probe(struct > > platform_device > > *pdev) > > > > platform_set_drvdata(pdev, mhdp); > > > > + /* init base struct for access mailbox */ > > + mhdp->base.dev = mhdp->dev; > > + mhdp->base.regs = mhdp->regs; > > + mhdp->base.mbox_mutex = &mhdp->mbox_mutex; > > + > > mhdp->info = of_device_get_match_data(dev); > > > > clk_prepare_enable(clk); > > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h index > > bad2fc0c73066..f08db38c82bbd 100644 > > --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > > @@ -15,6 +15,7 @@ > > #include <linux/mutex.h> > > #include <linux/spinlock.h> > > > > +#include <drm/bridge/cdns-mhdp-helper.h> > > #include <drm/display/drm_dp_helper.h> #include <drm/drm_bridge.h> > > #include <drm/drm_connector.h> @@ -27,10 +28,6 @@ struct phy; > > #define CDNS_APB_CTRL 0x00000 > > #define CDNS_CPU_STALL BIT(3) > > > > -#define CDNS_MAILBOX_FULL 0x00008 > > -#define CDNS_MAILBOX_EMPTY 0x0000c > > -#define CDNS_MAILBOX_TX_DATA 0x00010 > > -#define CDNS_MAILBOX_RX_DATA 0x00014 > > #define CDNS_KEEP_ALIVE 0x00018 > > #define CDNS_KEEP_ALIVE_MASK GENMASK(7, 0) > > > > @@ -198,45 +195,10 @@ struct phy; > > #define CDNS_DP_BYTE_COUNT(s) > (CDNS_DPTX_STREAM(s) + 0x7c) > > #define CDNS_DP_BYTE_COUNT_BYTES_IN_CHUNK_SHIFT 16 > > > > -/* mailbox */ > > -#define MAILBOX_RETRY_US 1000 > > -#define MAILBOX_TIMEOUT_US 2000000 > > - > > -#define MB_OPCODE_ID 0 > > -#define MB_MODULE_ID 1 > > -#define MB_SIZE_MSB_ID 2 > > -#define MB_SIZE_LSB_ID 3 > > -#define MB_DATA_ID 4 > > - > > -#define MB_MODULE_ID_DP_TX 0x01 > > -#define MB_MODULE_ID_HDCP_TX 0x07 > > -#define MB_MODULE_ID_HDCP_RX 0x08 > > -#define MB_MODULE_ID_HDCP_GENERAL 0x09 > > -#define MB_MODULE_ID_GENERAL 0x0a > > - > > -/* firmware and opcodes */ > > +/* firmware */ > > #define FW_NAME > "cadence/ > mhdp8546.bin" > > #define CDNS_MHDP_IMEM > 0x10000 > > > > -#define GENERAL_MAIN_CONTROL 0x01 > > -#define GENERAL_TEST_ECHO 0x02 > > -#define GENERAL_BUS_SETTINGS 0x03 > > -#define GENERAL_TEST_ACCESS 0x04 > > -#define GENERAL_REGISTER_READ 0x07 > > - > > -#define DPTX_SET_POWER_MNG 0x00 > > -#define DPTX_GET_EDID 0x02 > > -#define DPTX_READ_DPCD 0x03 > > -#define DPTX_WRITE_DPCD 0x04 > > -#define DPTX_ENABLE_EVENT 0x05 > > -#define DPTX_WRITE_REGISTER 0x06 > > -#define DPTX_READ_REGISTER 0x07 > > -#define DPTX_WRITE_FIELD 0x08 > > -#define DPTX_READ_EVENT 0x0a > > -#define DPTX_GET_LAST_AUX_STAUS 0x0e > > -#define DPTX_HPD_STATE 0x11 > > -#define DPTX_ADJUST_LT 0x12 > > - > > #define FW_STANDBY 0 > > #define FW_ACTIVE 1 > > > > @@ -352,6 +314,8 @@ struct cdns_mhdp_hdcp { }; > > > > struct cdns_mhdp_device { > > + struct cdns_mhdp_base base; > > + > > void __iomem *regs; > > void __iomem *sapb_regs; > > void __iomem *j721e_regs; > > diff --git a/include/drm/bridge/cdns-mhdp-helper.h > > b/include/drm/bridge/cdns-mhdp-helper.h new file mode 100644 index > > 0000000000000..477e67601ee5f > > --- /dev/null > > +++ b/include/drm/bridge/cdns-mhdp-helper.h > > @@ -0,0 +1,94 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Copyright (C) 2023 NXP Semiconductor, Inc. > > + */ > > +#ifndef __CDNS_MHDP_HELPER_H__ > > +#define __CDNS_MHDP_HELPER_H__ > > + > > +#include <asm/unaligned.h> > > +#include <linux/iopoll.h> > > + > > +/* mailbox regs offset */ > > +#define CDNS_MAILBOX_FULL 0x00008 > > +#define CDNS_MAILBOX_EMPTY 0x0000c > > +#define CDNS_MAILBOX_TX_DATA 0x00010 > > +#define CDNS_MAILBOX_RX_DATA 0x00014 > > + > > +#define MAILBOX_RETRY_US 1000 > > +#define MAILBOX_TIMEOUT_US 2000000 > > + > > +/* Module ID Code */ > > +#define MB_MODULE_ID_DP_TX 0x01 > > +#define MB_MODULE_ID_HDMI_TX 0x03 > > +#define MB_MODULE_ID_HDCP_TX 0x07 > > +#define MB_MODULE_ID_HDCP_RX 0x08 > > +#define MB_MODULE_ID_HDCP_GENERAL 0x09 > > +#define MB_MODULE_ID_GENERAL 0x0A > > + > > +/* General Commands */ > > +#define GENERAL_MAIN_CONTROL 0x01 > > +#define GENERAL_TEST_ECHO 0x02 > > +#define GENERAL_BUS_SETTINGS 0x03 > > +#define GENERAL_TEST_ACCESS 0x04 > > +#define GENERAL_REGISTER_WRITE 0x05 > > +#define GENERAL_WRITE_FIELD 0x06 > > +#define GENERAL_REGISTER_READ 0x07 > > +#define GENERAL_GET_HPD_STATE 0x11 > > + > > +/* DPTX Commands */ > > +#define DPTX_SET_POWER_MNG 0x00 > > +#define DPTX_SET_HOST_CAPABILITIES 0x01 > > +#define DPTX_GET_EDID 0x02 > > +#define DPTX_READ_DPCD 0x03 > > +#define DPTX_WRITE_DPCD 0x04 > > +#define DPTX_ENABLE_EVENT 0x05 > > +#define DPTX_WRITE_REGISTER 0x06 > > +#define DPTX_READ_REGISTER 0x07 > > +#define DPTX_WRITE_FIELD 0x08 > > +#define DPTX_TRAINING_CONTROL 0x09 > > +#define DPTX_READ_EVENT 0x0a > > +#define DPTX_READ_LINK_STAT 0x0b > > +#define DPTX_SET_VIDEO 0x0c > > +#define DPTX_SET_AUDIO 0x0d > > +#define DPTX_GET_LAST_AUX_STAUS 0x0e > > +#define DPTX_SET_LINK_BREAK_POINT 0x0f > > +#define DPTX_FORCE_LANES 0x10 > > +#define DPTX_HPD_STATE 0x11 > > +#define DPTX_ADJUST_LT 0x12 > > + > > +/* HDMI TX Commands */ > > +#define HDMI_TX_READ 0x00 > > +#define HDMI_TX_WRITE 0x01 > > +#define HDMI_TX_UPDATE_READ 0x02 > > +#define HDMI_TX_EDID 0x03 > > +#define HDMI_TX_EVENTS 0x04 > > +#define HDMI_TX_HPD_STATUS 0x05 > > + > > +struct cdns_mhdp_base { > > + struct device *dev; > > + void __iomem *regs; > > + /* protect mailbox communications with the firmware */ > > + struct mutex *mbox_mutex; > > +}; > > + > > +/* Mailbox helper functions */ > > +int cdns_mhdp_mailbox_send(struct cdns_mhdp_base *base, u8 > module_id, > > + u8 opcode, u16 size, u8 *message); > > Any reason to move the declaration for send before recv? It seems reasonable > to have the in alphabetical order. OK. > > > +int cdns_mhdp_mailbox_recv_header(struct cdns_mhdp_base *base, > > + u8 module_id, u8 opcode, u16 > req_size); > > +int cdns_mhdp_mailbox_recv_data(struct cdns_mhdp_base *base, > > + u8 *buff, u16 buff_size); > > AFAICS while calling a sequence of these 3 functions mhdp->mbox_mutex is > locked. This should be noted here. OK, I will add notes in the next version. Best regards, Sandor > > Best regards, > Alexander > > > +/* General commands helper functions */ int cdns_mhdp_reg_read(struct > > +cdns_mhdp_base *base, u32 addr, u32 *value); int > > +cdns_mhdp_reg_write(struct cdns_mhdp_base *base, u32 addr, u32 val); > > + > > +/* DPTX commands helper functions */ > > +int cdns_mhdp_dp_reg_write(struct cdns_mhdp_base *base, u16 addr, > u32 > > +val); int cdns_mhdp_dp_reg_write_bit(struct cdns_mhdp_base *base, u16 > > +addr, + > > > u8 start_bit, u8 bits_no, u32 val); > > +int cdns_mhdp_dpcd_read(struct cdns_mhdp_base *base, > > + u32 addr, u8 *data, u16 len); int > > +cdns_mhdp_dpcd_write(struct cdns_mhdp_base *base, u32 addr, u8 > > +value); > > + > > +#endif /* __CDNS_MHDP_HELPER_H__ */ > > > -- > TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany > Amtsgericht München, HRB 105018 > Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider > http://www.tq/ > -group.com%2F&data=05%7C01%7CSandor.yu%40nxp.com%7C7dae84b9c73 > 944b20c8308dbcef85e20%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7 > C0%7C638331338392509262%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4 > wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000% > 7C%7C%7C&sdata=enWq7M9EPSL8b6P%2BlNDyeoQ9oceS%2FkVtW7CqakrO > GU0%3D&reserved=0 >
Hi Alexander, Thanks for your comments, > > Hi Sandor, > > thanks for the patch. > > Am Dienstag, 17. Oktober 2023, 09:04:00 CEST schrieb Sandor Yu: > > Add a new DRM DisplayPort and HDMI bridge driver for Candence > MHDP8501 > > used in i.MX8MQ SOC. MHDP8501 could support HDMI or DisplayPort > > standards according embedded Firmware running in the uCPU. > > > > For iMX8MQ SOC, the DisplayPort/HDMI FW was loaded and activated by > > SOC's ROM code. Bootload binary included respective specific firmware > > is required. > > > > Driver will check display connector type and > > then load the corresponding driver. > > > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > > Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> > > --- > > v10->v11: > > - remove MODULE_ALIAS() from mhdp8501 driver. > > > > v9->v10: > > - struct cdns_mhdp_device is renamed to cdns_mhdp8501_device. > > - update for mhdp helper driver is introduced. > > Remove head file cdns-mhdp-mailbox.h and add cdns-mhdp-helper.h > > Add struct cdns_mhdp_base base to struct cdns_mhdp8501_device. > > Init struct cdns_mhdp_base base when driver probe. > > > > drivers/gpu/drm/bridge/cadence/Kconfig | 16 + > > drivers/gpu/drm/bridge/cadence/Makefile | 2 + > > .../drm/bridge/cadence/cdns-mhdp8501-core.c | 315 ++++++++ > > .../drm/bridge/cadence/cdns-mhdp8501-core.h | 365 +++++++++ > > .../gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c | 708 > ++++++++++++++++++ > > .../drm/bridge/cadence/cdns-mhdp8501-hdmi.c | 673 > +++++++++++++++++ > > 6 files changed, 2079 insertions(+) > > create mode 100644 > drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c > > create mode 100644 > drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h > > create mode 100644 > drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c > > create mode 100644 > drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c > > > > diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig > > b/drivers/gpu/drm/bridge/cadence/Kconfig index > 0b7b4626a7af0..81685ab4e874a > > 100644 > > --- a/drivers/gpu/drm/bridge/cadence/Kconfig > > +++ b/drivers/gpu/drm/bridge/cadence/Kconfig > > @@ -50,3 +50,19 @@ config DRM_CDNS_MHDP8546_J721E > > initializes the J721E Display Port and sets up the > > clock and data muxes. > > endif > > + > > +config DRM_CDNS_MHDP8501 > > + tristate "Cadence MHDP8501 DP/HDMI bridge" > > + select DRM_KMS_HELPER > > + select DRM_PANEL_BRIDGE > > + select DRM_DISPLAY_DP_HELPER > > + select DRM_DISPLAY_HELPER > > + select CDNS_MHDP_HELPER > > + select DRM_CDNS_AUDIO > > + depends on OF > > + help > > + Support Cadence MHDP8501 DisplayPort/HDMI bridge. > > + Cadence MHDP8501 support one or more protocols, > > + including DisplayPort and HDMI. > > + To use the DP and HDMI drivers, their respective > > + specific firmware is required. > > diff --git a/drivers/gpu/drm/bridge/cadence/Makefile > > b/drivers/gpu/drm/bridge/cadence/Makefile index > > 087dc074820d7..02c1a9f3cf6fc 100644 > > --- a/drivers/gpu/drm/bridge/cadence/Makefile > > +++ b/drivers/gpu/drm/bridge/cadence/Makefile > > @@ -6,3 +6,5 @@ obj-$(CONFIG_CDNS_MHDP_HELPER) += > cdns-mhdp-helper.o > > obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o > > cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o > > cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += > cdns-mhdp8546-j721e.o > > +obj-$(CONFIG_DRM_CDNS_MHDP8501) += cdns-mhdp8501.o > > +cdns-mhdp8501-y := cdns-mhdp8501-core.o cdns-mhdp8501-dp.o > > cdns-mhdp8501-hdmi.o diff --git > > a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c > > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c new file mode > 100644 > > index 0000000000000..23860a260e637 > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.c > > @@ -0,0 +1,315 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Cadence Display Port Interface (DP) driver > > + * > > + * Copyright (C) 2023 NXP Semiconductor, Inc. > > + * > > + */ > > +#include <drm/drm_of.h> > > +#include <drm/drm_print.h> > > +#include <linux/clk.h> > > +#include <linux/irq.h> > > +#include <linux/mutex.h> > > +#include <linux/of_device.h> > > +#include <linux/phy/phy.h> > > + > > +#include "cdns-mhdp8501-core.h" > > + > > +static int cdns_mhdp8501_read_hpd(struct cdns_mhdp8501_device > *mhdp) > > +{ > > + u8 status; > > + int ret; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_GENERAL, > > + GENERAL_GET_HPD_STATE, 0, > NULL); > > + if (ret) > > + goto err_get_hpd; > > + > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_GENERAL, > > + > GENERAL_GET_HPD_STATE, > > + sizeof(status)); > > + if (ret) > > + goto err_get_hpd; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, &status, > sizeof(status)); > > + if (ret) > > + goto err_get_hpd; > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + return status; > > + > > +err_get_hpd: > > + DRM_ERROR("read hpd failed: %d\n", ret); > > Use dev_err() instead, there is a device pointer available. OK. > > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + return ret; > > +} > > + > > +enum drm_connector_status cdns_mhdp8501_detect(struct > cdns_mhdp8501_device > > *mhdp) +{ > > + u8 hpd = 0xf; > > + > > + hpd = cdns_mhdp8501_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); > > I suppose this is a somewhat unexpected return value. Shouldn't this be > DRM_WARN instead to indicate something went wrong? > Despite that dev_warn (or dev_info) should be used. OK, will use dev_warn. > > > + return connector_status_unknown; > > +} > > + > > +static void hotplug_work_func(struct work_struct *work) > > +{ > > + struct cdns_mhdp8501_device *mhdp = container_of(work, > > + struct > cdns_mhdp8501_device, > > + > hotplug_work.work); > > + enum drm_connector_status status = > cdns_mhdp8501_detect(mhdp); > > + > > + drm_bridge_hpd_notify(&mhdp->bridge, status); > > + > > + if (status == connector_status_connected) { > > + /* Cable connedted */ > > Small typo: Cable connected OK, thanks. > > > + DRM_INFO("HDMI/DP Cable Plug In\n"); > > drm_info() > > > + enable_irq(mhdp->irq[IRQ_OUT]); > > + } else if (status == connector_status_disconnected) { > > + /* Cable Disconnedted */ > > Small typo: Cable Disconnected OK. > > > + DRM_INFO("HDMI/DP Cable Plug Out\n"); > > drm_info() > > > + enable_irq(mhdp->irq[IRQ_IN]); > > + } > > +} > > + > > +static irqreturn_t cdns_mhdp8501_irq_thread(int irq, void *data) > > +{ > > + struct cdns_mhdp8501_device *mhdp = data; > > + > > + disable_irq_nosync(irq); > > Is it really necessary to enable/disable the IRQ_IN and IRQ_OUT interrupts > upon each IRQ event? There are no mask and status registers for IRQ_IN and IRQ_OUT, so they should not enable at the same time. We has to disable one IRQ here and enable another in hotplug_work_func(). > The actual status is returned by the firmware using > cdns_mhdp8501_read_hpd() > anyway. There is a small window between the IRQ happening here and > enabling > the other one in hotplug_work_func() where an IRQ event is lost. Yes, IRQ event will lost in the small window, but the HPD status is not missing, HDMI/DP firmware will keep updating latest HPD status in the windows, then driver will get the correct status from FW by function cdns_mhdp8501_read_hpd() before enable IRQ. > IMHO both IRQs should be enabled at all time and let > cdns_mhdp8501_read_hpd() > return whether the connector is connected or disconnected. > > > + > > + mod_delayed_work(system_wq, &mhdp->hotplug_work, > > + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int cdns_mhdp8501_dt_parse(struct cdns_mhdp8501_device > *mhdp, > > + struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct device_node *np = dev->of_node; > > + struct device_node *remote; > > + > > + remote = of_graph_get_remote_node(np, 1, 0); > > + if (!remote) { > > + dev_err(dev, "fail to get remote node\n"); > > + of_node_put(remote); > > + return -EINVAL; > > + } > > + > > + /* get connector type */ > > + if (of_device_is_compatible(remote, "hdmi-connector")) { > > + mhdp->connector_type = > DRM_MODE_CONNECTOR_HDMIA; > > + > > + } else if (of_device_is_compatible(remote, "dp-connector")) { > > + mhdp->connector_type = > DRM_MODE_CONNECTOR_DisplayPort; > > + > > + } else { > > + dev_err(dev, "Unknown connector type\n"); > > + of_node_put(remote); > > + return -EINVAL; > > + } > > + > > + of_node_put(remote); > > + return true; > > +} > > + > > +static void cdns_mhdp8501_add_bridge(struct cdns_mhdp8501_device > *mhdp) > > +{ > > + if (mhdp->connector_type == DRM_MODE_CONNECTOR_HDMIA) { > > + mhdp->bridge.funcs = &cdns_hdmi_bridge_funcs; > > + } else if (mhdp->connector_type == > DRM_MODE_CONNECTOR_DisplayPort) { > > + mhdp->bridge.funcs = &cdns_dp_bridge_funcs; > > + } else { > > + dev_err(mhdp->dev, "Unsupported connector type!\n"); > > + return; > > + } > > + > > + mhdp->bridge.type = mhdp->connector_type; > > + mhdp->bridge.driver_private = mhdp; > > + mhdp->bridge.of_node = mhdp->dev->of_node; > > + mhdp->bridge.ops = DRM_BRIDGE_OP_DETECT | > DRM_BRIDGE_OP_EDID | > > + DRM_BRIDGE_OP_HPD; > > + drm_bridge_add(&mhdp->bridge); > > +} > > + > > +static int cdns_mhdp8501_probe(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct cdns_mhdp8501_device *mhdp; > > + struct resource *res; > > + u32 reg; > > + int ret; > > + > > + mhdp = devm_kzalloc(dev, sizeof(*mhdp), GFP_KERNEL); > > + if (!mhdp) > > + return -ENOMEM; > > + > > + mutex_init(&mhdp->mbox_mutex); > > + mhdp->dev = dev; > > + > > + 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); > > You can use devm_platform_ioremap_resource instead, no? No, this base address should be share with PHY drivers. > > > + > > + ret = cdns_mhdp8501_dt_parse(mhdp, pdev); > > + if (ret < 0) > > + return -EINVAL; > > + > > + mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, > 0); > > + if (IS_ERR(mhdp->phy)) > > + return dev_err_probe(dev, PTR_ERR(mhdp->phy), "no PHY > configured\n"); > > + > > + mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in"); > > + if (mhdp->irq[IRQ_IN] < 0) > > + return dev_err_probe(dev, mhdp->irq[IRQ_IN], "No > plug_in > irq number\n"); > > + > > + mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, > "plug_out"); > > + if (mhdp->irq[IRQ_OUT] < 0) > > + return dev_err_probe(dev, mhdp->irq[IRQ_OUT], "No > plug_out > irq > > number\n"); + > > + irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN); > > As mentioned above both interrupts should be enabled at all the time. Same reason as above. > > > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN], > > + NULL, > cdns_mhdp8501_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); > > See above. Same reason as above. > > > + ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT], > > + NULL, > cdns_mhdp8501_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; > > + } > > + > > + /* set default lane mapping */ > > + mhdp->lane_mapping = LANE_MAPPING_NORMAL; > > + > > + mhdp->plat_data = of_device_get_match_data(dev); > > + if (mhdp->plat_data) { > > + if (mhdp->connector_type == > DRM_MODE_CONNECTOR_DisplayPort) > > + mhdp->lane_mapping = mhdp->plat_data- > >dp_lane_mapping; > > + else if (mhdp->connector_type == > DRM_MODE_CONNECTOR_HDMIA) > > + mhdp->lane_mapping = mhdp->plat_data- > >hdmi_lane_mapping; > > + } > > + > > + dev_set_drvdata(dev, mhdp); > > + > > + /* init base struct for access mhdp mailbox */ > > + mhdp->base.dev = mhdp->dev; > > + mhdp->base.regs = mhdp->regs; > > + mhdp->base.mbox_mutex = &mhdp->mbox_mutex; > > + > > + if (mhdp->connector_type == > DRM_MODE_CONNECTOR_DisplayPort) { > > + drm_dp_aux_init(&mhdp->dp.aux); > > + mhdp->dp.aux.name = "mhdp8501_dp_aux"; > > + mhdp->dp.aux.dev = dev; > > + mhdp->dp.aux.transfer = cdns_dp_aux_transfer; > > + } > > + > > + /* Enable APB clock */ > > + mhdp->apb_clk = devm_clk_get(dev, NULL); > > + if (IS_ERR(mhdp->apb_clk)) > > + return dev_err_probe(dev, PTR_ERR(mhdp->apb_clk), > > + "couldn't get apb clk\n"); > > + > > + clk_prepare_enable(mhdp->apb_clk); > > + > > + /* > > + * 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); > > + if (ret) { > > + dev_err(dev, "device didn't give any life sign: reg %d\n", > reg); > > + goto clk_disable; > > + } > > + > > + /* Mailbox protect for HDMI PHY access */ > > + mutex_lock(&mhdp->mbox_mutex); > > + ret = phy_init(mhdp->phy); > > + mutex_unlock(&mhdp->mbox_mutex); > > + if (ret) { > > + dev_err(dev, "Failed to initialize PHY: %d\n", ret); > > + goto clk_disable; > > + } > > + > > + /* Enable cable hotplug detect */ > > + if (cdns_mhdp8501_read_hpd(mhdp)) > > + enable_irq(mhdp->irq[IRQ_OUT]); > > + else > > + enable_irq(mhdp->irq[IRQ_IN]); > > + > > + cdns_mhdp8501_add_bridge(mhdp); > > + > > + return 0; > > + > > +clk_disable: > > + clk_disable_unprepare(mhdp->apb_clk); > > + > > + return -EINVAL; > > +} > > + > > +static int cdns_mhdp8501_remove(struct platform_device *pdev) > > +{ > > + struct cdns_mhdp8501_device *mhdp = > platform_get_drvdata(pdev); > > + > > + if (mhdp->connector_type == > DRM_MODE_CONNECTOR_DisplayPort) > > + cdns_dp_aux_destroy(mhdp); > > + > > + drm_bridge_remove(&mhdp->bridge); > > + clk_disable_unprepare(mhdp->apb_clk); > > + > > + return 0; > > +} > > + > > +static struct mhdp8501_plat_data imx8mq_mhdp_drv_data = { > > + .hdmi_lane_mapping = LANE_MAPPING_FLIPPED, > > + .dp_lane_mapping = LANE_MAPPING_IMX8MQ_DP, > > +}; > > + > > +static const struct of_device_id cdns_mhdp8501_dt_ids[] = { > > + { .compatible = "fsl,imx8mq-mhdp8501", > > + .data = &imx8mq_mhdp_drv_data > > + }, > > + { }, > > +}; > > +MODULE_DEVICE_TABLE(of, cdns_mhdp8501_dt_ids); > > + > > +static struct platform_driver cdns_mhdp8501_driver = { > > + .probe = cdns_mhdp8501_probe, > > + .remove = cdns_mhdp8501_remove, > > + .driver = { > > + .name = "cdns-mhdp8501", > > + .of_match_table = cdns_mhdp8501_dt_ids, > > + }, > > +}; > > + > > +module_platform_driver(cdns_mhdp8501_driver); > > + > > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > > +MODULE_DESCRIPTION("Cadence MHDP8501 bridge driver"); > > +MODULE_LICENSE("GPL"); > > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h > > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h new file mode > 100644 > > index 0000000000000..97170be57ffcb > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-core.h > > @@ -0,0 +1,365 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Cadence MHDP 8501 Common head file > > + * > > + * Copyright (C) 2019-2023 NXP Semiconductor, Inc. > > + * > > + */ > > + > > +#ifndef _CDNS_MHDP8501_CORE_H_ > > +#define _CDNS_MHDP8501_CORE_H_ > > + > > +#include <drm/bridge/cdns-mhdp-helper.h> > > +#include <drm/drm_bridge.h> > > +#include <drm/drm_connector.h> > > +#include <drm/display/drm_dp_helper.h> > > +#include <linux/bitops.h> > > + > > +#define ADDR_IMEM 0x10000 > > +#define ADDR_DMEM 0x20000 > > + > > +/* APB CFG addr */ > > +#define APB_CTRL 0 > > +#define XT_INT_CTRL 0x04 > > +#define MAILBOX_FULL_ADDR 0x08 > > +#define MAILBOX_EMPTY_ADDR 0x0c > > +#define MAILBOX0_WR_DATA 0x10 > > +#define MAILBOX0_RD_DATA 0x14 > > +#define KEEP_ALIVE 0x18 > > +#define VER_L 0x1c > > +#define VER_H 0x20 > > +#define VER_LIB_L_ADDR 0x24 > > +#define VER_LIB_H_ADDR 0x28 > > +#define SW_DEBUG_L 0x2c > > +#define SW_DEBUG_H 0x30 > > +#define MAILBOX_INT_MASK 0x34 > > +#define MAILBOX_INT_STATUS 0x38 > > +#define SW_CLK_L 0x3c > > +#define SW_CLK_H 0x40 > > +#define SW_EVENTS0 0x44 > > +#define SW_EVENTS1 0x48 > > +#define SW_EVENTS2 0x4c > > +#define SW_EVENTS3 0x50 > > +#define XT_OCD_CTRL 0x60 > > +#define APB_INT_MASK 0x6c > > +#define APB_STATUS_MASK 0x70 > > + > > +/* Source phy comp */ > > +#define PHY_DATA_SEL 0x0818 > > +#define LANES_CONFIG 0x0814 > > + > > +/* Source CAR Addr */ > > +#define SOURCE_HDTX_CAR 0x0900 > > +#define SOURCE_DPTX_CAR 0x0904 > > +#define SOURCE_PHY_CAR 0x0908 > > +#define SOURCE_CEC_CAR 0x090c > > +#define SOURCE_CBUS_CAR 0x0910 > > +#define SOURCE_PKT_CAR 0x0918 > > +#define SOURCE_AIF_CAR 0x091c > > +#define SOURCE_CIPHER_CAR 0x0920 > > +#define SOURCE_CRYPTO_CAR 0x0924 > > + > > +/* clock meters addr */ > > +#define CM_CTRL 0x0a00 > > +#define CM_I2S_CTRL 0x0a04 > > +#define CM_SPDIF_CTRL 0x0a08 > > +#define CM_VID_CTRL 0x0a0c > > +#define CM_LANE_CTRL 0x0a10 > > +#define I2S_NM_STABLE 0x0a14 > > +#define I2S_NCTS_STABLE 0x0a18 > > +#define SPDIF_NM_STABLE 0x0a1c > > +#define SPDIF_NCTS_STABLE 0x0a20 > > +#define NMVID_MEAS_STABLE 0x0a24 > > +#define I2S_MEAS 0x0a40 > > +#define SPDIF_MEAS 0x0a80 > > +#define NMVID_MEAS 0x0ac0 > > + > > +/* source vif addr */ > > +#define BND_HSYNC2VSYNC 0x0b00 > > +#define HSYNC2VSYNC_F1_L1 0x0b04 > > +#define HSYNC2VSYNC_STATUS 0x0b0c > > +#define HSYNC2VSYNC_POL_CTRL 0x0b10 > > + > > +/* MHDP TX_top_comp */ > > +#define SCHEDULER_H_SIZE 0x1000 > > +#define SCHEDULER_V_SIZE 0x1004 > > +#define HDTX_SIGNAL_FRONT_WIDTH 0x100c > > +#define HDTX_SIGNAL_SYNC_WIDTH 0x1010 > > +#define HDTX_SIGNAL_BACK_WIDTH 0x1014 > > +#define HDTX_CONTROLLER 0x1018 > > +#define HDTX_HPD 0x1020 > > +#define HDTX_CLOCK_REG_0 0x1024 > > +#define HDTX_CLOCK_REG_1 0x1028 > > + > > +/* DPTX hpd addr */ > > +#define HPD_IRQ_DET_MIN_TIMER 0x2100 > > +#define HPD_IRQ_DET_MAX_TIMER 0x2104 > > +#define HPD_UNPLGED_DET_MIN_TIMER 0x2108 > > +#define HPD_STABLE_TIMER 0x210c > > +#define HPD_FILTER_TIMER 0x2110 > > +#define HPD_EVENT_MASK 0x211c > > +#define HPD_EVENT_DET 0x2120 > > + > > +/* DPTX framer addr */ > > +#define DP_FRAMER_GLOBAL_CONFIG 0x2200 > > +#define DP_SW_RESET 0x2204 > > +#define DP_FRAMER_TU 0x2208 > > +#define DP_FRAMER_PXL_REPR 0x220c > > +#define DP_FRAMER_SP 0x2210 > > +#define AUDIO_PACK_CONTROL 0x2214 > > +#define DP_VC_TABLE(x) (0x2218 + ((x) << 2)) > > +#define DP_VB_ID 0x2258 > > +#define DP_MTPH_LVP_CONTROL 0x225c > > +#define DP_MTPH_SYMBOL_VALUES 0x2260 > > +#define DP_MTPH_ECF_CONTROL 0x2264 > > +#define DP_MTPH_ACT_CONTROL 0x2268 > > +#define DP_MTPH_STATUS 0x226c > > +#define DP_INTERRUPT_SOURCE 0x2270 > > +#define DP_INTERRUPT_MASK 0x2274 > > +#define DP_FRONT_BACK_PORCH 0x2278 > > +#define DP_BYTE_COUNT 0x227c > > + > > +/* DPTX stream addr */ > > +#define MSA_HORIZONTAL_0 0x2280 > > +#define MSA_HORIZONTAL_1 0x2284 > > +#define MSA_VERTICAL_0 0x2288 > > +#define MSA_VERTICAL_1 0x228c > > +#define MSA_MISC 0x2290 > > +#define STREAM_CONFIG 0x2294 > > +#define AUDIO_PACK_STATUS 0x2298 > > +#define VIF_STATUS 0x229c > > +#define PCK_STUFF_STATUS_0 0x22a0 > > +#define PCK_STUFF_STATUS_1 0x22a4 > > +#define INFO_PACK_STATUS 0x22a8 > > +#define RATE_GOVERNOR_STATUS 0x22ac > > +#define DP_HORIZONTAL 0x22b0 > > +#define DP_VERTICAL_0 0x22b4 > > +#define DP_VERTICAL_1 0x22b8 > > +#define DP_BLOCK_SDP 0x22bc > > + > > +/* DPTX glbl addr */ > > +#define DPTX_LANE_EN 0x2300 > > +#define DPTX_ENHNCD 0x2304 > > +#define DPTX_INT_MASK 0x2308 > > +#define DPTX_INT_STATUS 0x230c > > + > > +/* DP AUX Addr */ > > +#define DP_AUX_HOST_CONTROL 0x2800 > > +#define DP_AUX_INTERRUPT_SOURCE 0x2804 > > +#define DP_AUX_INTERRUPT_MASK 0x2808 > > +#define DP_AUX_SWAP_INVERSION_CONTROL 0x280c > > +#define DP_AUX_SEND_NACK_TRANSACTION 0x2810 > > +#define DP_AUX_CLEAR_RX 0x2814 > > +#define DP_AUX_CLEAR_TX 0x2818 > > +#define DP_AUX_TIMER_STOP 0x281c > > +#define DP_AUX_TIMER_CLEAR 0x2820 > > +#define DP_AUX_RESET_SW 0x2824 > > +#define DP_AUX_DIVIDE_2M 0x2828 > > +#define DP_AUX_TX_PREACHARGE_LENGTH 0x282c > > +#define DP_AUX_FREQUENCY_1M_MAX 0x2830 > > +#define DP_AUX_FREQUENCY_1M_MIN 0x2834 > > +#define DP_AUX_RX_PRE_MIN 0x2838 > > +#define DP_AUX_RX_PRE_MAX 0x283c > > +#define DP_AUX_TIMER_PRESET 0x2840 > > +#define DP_AUX_NACK_FORMAT 0x2844 > > +#define DP_AUX_TX_DATA 0x2848 > > +#define DP_AUX_RX_DATA 0x284c > > +#define DP_AUX_TX_STATUS 0x2850 > > +#define DP_AUX_RX_STATUS 0x2854 > > +#define DP_AUX_RX_CYCLE_COUNTER 0x2858 > > +#define DP_AUX_MAIN_STATES 0x285c > > +#define DP_AUX_MAIN_TIMER 0x2860 > > +#define DP_AUX_AFE_OUT 0x2864 > > + > > +/* source pif addr */ > > +#define SOURCE_PIF_WR_ADDR 0x30800 > > +#define SOURCE_PIF_WR_REQ 0x30804 > > +#define SOURCE_PIF_RD_ADDR 0x30808 > > +#define SOURCE_PIF_RD_REQ 0x3080c > > +#define SOURCE_PIF_DATA_WR 0x30810 > > +#define SOURCE_PIF_DATA_RD 0x30814 > > +#define SOURCE_PIF_FIFO1_FLUSH 0x30818 > > +#define SOURCE_PIF_FIFO2_FLUSH 0x3081c > > +#define SOURCE_PIF_STATUS 0x30820 > > +#define SOURCE_PIF_INTERRUPT_SOURCE 0x30824 > > +#define SOURCE_PIF_INTERRUPT_MASK 0x30828 > > +#define SOURCE_PIF_PKT_ALLOC_REG 0x3082c > > +#define SOURCE_PIF_PKT_ALLOC_WR_EN 0x30830 > > +#define SOURCE_PIF_SW_RESET 0x30834 > > + > > +#define LINK_TRAINING_NOT_ACTIV 0 > > +#define LINK_TRAINING_RUN 1 > > +#define LINK_TRAINING_RESTART 2 > > + > > +#define CONTROL_VIDEO_IDLE 0 > > +#define CONTROL_VIDEO_VALID 1 > > + > > +#define INTERLACE_FMT_DET BIT(12) > > +#define VIF_BYPASS_INTERLACE BIT(13) > > +#define TU_CNT_RST_EN BIT(15) > > +#define INTERLACE_DTCT_WIN 0x20 > > + > > +#define DP_FRAMER_SP_INTERLACE_EN BIT(2) > > +#define DP_FRAMER_SP_HSP BIT(1) > > +#define DP_FRAMER_SP_VSP BIT(0) > > + > > +/* Capability */ > > +#define AUX_HOST_INVERT 3 > > +#define FAST_LT_SUPPORT 1 > > +#define FAST_LT_NOT_SUPPORT 0 > > +#define LANE_MAPPING_NORMAL 0x1b > > +#define LANE_MAPPING_FLIPPED 0xe4 > > +#define LANE_MAPPING_IMX8MQ_DP 0xc6 > > +#define ENHANCED 1 > > +#define SCRAMBLER_EN BIT(4) > > + > > +#define FULL_LT_STARTED BIT(0) > > +#define FASE_LT_STARTED BIT(1) > > +#define CLK_RECOVERY_FINISHED BIT(2) > > +#define EQ_PHASE_FINISHED BIT(3) > > +#define FASE_LT_START_FINISHED BIT(4) > > +#define CLK_RECOVERY_FAILED BIT(5) > > +#define EQ_PHASE_FAILED BIT(6) > > +#define FASE_LT_FAILED BIT(7) > > + > > +#define TU_SIZE 30 > > +#define CDNS_DP_MAX_LINK_RATE 540000 > > + > > +#define F_HDMI_ENCODING(x) (((x) & ((1 << 2) - 1)) << 16) > > +#define F_VIF_DATA_WIDTH(x) (((x) & ((1 << 2) - 1)) << 2) > > +#define F_HDMI_MODE(x) (((x) & ((1 << 2) - 1)) << 0) > > +#define F_GCP_EN(x) (((x) & ((1 << 1) - 1)) << 12) > > +#define F_DATA_EN(x) (((x) & ((1 << 1) - 1)) << 15) > > +#define F_HDMI2_PREAMBLE_EN(x) (((x) & ((1 << 1) - 1)) << 18) > > +#define F_PIC_3D(x) (((x) & ((1 << 4) - 1)) << 7) > > +#define F_BCH_EN(x) (((x) & ((1 << 1) - 1)) << 11) > > +#define F_SOURCE_PHY_MHDP_SEL(x) (((x) & ((1 << 2) - 1)) << 3) > > +#define F_HPD_VALID_WIDTH(x) (((x) & ((1 << 12) - 1)) << 0) > > +#define F_HPD_GLITCH_WIDTH(x) (((x) & ((1 << 8) - 1)) << 12) > > +#define F_HDMI2_CTRL_IL_MODE(x) (((x) & ((1 << 1) - 1)) << 19) > > +#define F_SOURCE_PHY_LANE0_SWAP(x) (((x) & ((1 << 2) - 1)) << 0) > > +#define F_SOURCE_PHY_LANE1_SWAP(x) (((x) & ((1 << 2) - 1)) << 2) > > +#define F_SOURCE_PHY_LANE2_SWAP(x) (((x) & ((1 << 2) - 1)) << 4) > > +#define F_SOURCE_PHY_LANE3_SWAP(x) (((x) & ((1 << 2) - 1)) << 6) > > +#define F_SOURCE_PHY_COMB_BYPASS(x) (((x) & ((1 << 1) - 1)) << 21) > > +#define F_SOURCE_PHY_20_10(x) (((x) & ((1 << 1) - 1)) << 22) > > +#define F_PKT_ALLOC_ADDRESS(x) (((x) & ((1 << 4) - 1)) << 0) > > +#define F_ACTIVE_IDLE_TYPE(x) (((x) & ((1 << 1) - 1)) << 17) > > +#define F_FIFO1_FLUSH(x) (((x) & ((1 << 1) - 1)) << 0) > > +#define F_PKT_ALLOC_WR_EN(x) (((x) & ((1 << 1) - 1)) << 0) > > +#define F_DATA_WR(x) (x) > > +#define F_WR_ADDR(x) (((x) & ((1 << 4) - 1)) << 0) > > +#define F_HOST_WR(x) (((x) & ((1 << 1) - 1)) << 0) > > +#define F_TYPE_VALID(x) (((x) & ((1 << 1) - 1)) << 16) > > +#define F_PACKET_TYPE(x) (((x) & ((1 << 8) - 1)) << 8) > > + > > +/* Reference cycles when using lane clock as reference */ > > +#define LANE_REF_CYC 0x8000 > > + > > +/* HPD Debounce */ > > +#define HOTPLUG_DEBOUNCE_MS 200 > > + > > +/* HPD IRQ Index */ > > +#define IRQ_IN 0 > > +#define IRQ_OUT 1 > > +#define IRQ_NUM 2 > > + > > +/* FW check alive timeout */ > > +#define CDNS_KEEP_ALIVE_TIMEOUT 2000 > > +#define CDNS_KEEP_ALIVE_MASK GENMASK(7, 0) > > + > > +enum voltage_swing_level { > > + VOLTAGE_LEVEL_0, > > + VOLTAGE_LEVEL_1, > > + VOLTAGE_LEVEL_2, > > + VOLTAGE_LEVEL_3, > > +}; > > + > > +enum pre_emphasis_level { > > + PRE_EMPHASIS_LEVEL_0, > > + PRE_EMPHASIS_LEVEL_1, > > + PRE_EMPHASIS_LEVEL_2, > > + PRE_EMPHASIS_LEVEL_3, > > +}; > > + > > +enum pattern_set { > > + PTS1 = BIT(0), > > + PTS2 = BIT(1), > > + PTS3 = BIT(2), > > + PTS4 = BIT(3), > > + DP_NONE = BIT(4) > > +}; > > + > > +enum vic_color_depth { > > + BCS_6 = 0x1, > > + BCS_8 = 0x2, > > + BCS_10 = 0x4, > > + BCS_12 = 0x8, > > + BCS_16 = 0x10, > > +}; > > + > > +enum vic_bt_type { > > + BT_601 = 0x0, > > + BT_709 = 0x1, > > +}; > > + > > +enum { > > + MODE_DVI, > > + MODE_HDMI_1_4, > > + MODE_HDMI_2_0, > > +}; > > + > > +struct video_info { > > + int bpc; > > + int color_fmt; > > +}; > > + > > +struct mhdp8501_plat_data { > > + int hdmi_lane_mapping; > > + int dp_lane_mapping; > > +}; > > + > > +struct cdns_mhdp8501_device { > > + struct cdns_mhdp_base base; > > + > > + struct device *dev; > > + void __iomem *regs; > > + struct drm_connector *curr_conn; > > + struct drm_bridge bridge; > > + struct clk *apb_clk; > > + struct phy *phy; > > + > > + struct video_info video_info; > > + struct drm_display_mode mode; > > + > > + int irq[IRQ_NUM]; > > + struct delayed_work hotplug_work; > > + int connector_type; > > + u32 lane_mapping; > > + > > + /* protect mailbox communications with the firmware */ > > + struct mutex mbox_mutex; > > + > > + const struct mhdp8501_plat_data *plat_data; > > + > > + union { > > + struct _dp_data { > > + u32 rate; > > + u8 num_lanes; > > + struct drm_dp_aux aux; > > + u8 dpcd[DP_RECEIVER_CAP_SIZE]; > > + } dp; > > + struct _hdmi_data { > > + u32 hdmi_type; > > + } hdmi; > > + }; > > +}; > > + > > +extern const struct drm_bridge_funcs cdns_dp_bridge_funcs; > > +extern const struct drm_bridge_funcs cdns_hdmi_bridge_funcs; > > + > > +ssize_t cdns_dp_aux_transfer(struct drm_dp_aux *aux, struct > drm_dp_aux_msg > > *msg); +enum drm_connector_status cdns_mhdp8501_detect(struct > > cdns_mhdp8501_device *mhdp); +int cdns_dp_aux_destroy(struct > > cdns_mhdp8501_device *mhdp); > > + > > +#endif > > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c > > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c new file mode > 100644 > > index 0000000000000..5576db967cac6 > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-dp.c > > @@ -0,0 +1,708 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Cadence MHDP8501 DisplayPort(DP) bridge driver > > + * > > + * Copyright (C) 2019-2023 NXP Semiconductor, Inc. > > + * > > + */ > > +#include <drm/drm_atomic_helper.h> > > +#include <drm/drm_edid.h> > > +#include <drm/drm_print.h> > > +#include <linux/phy/phy.h> > > +#include <linux/phy/phy-dp.h> > > + > > +#include "cdns-mhdp8501-core.h" > > + > > +#define LINK_TRAINING_TIMEOUT_MS 500 > > +#define LINK_TRAINING_RETRY_MS 20 > > + > > +ssize_t cdns_dp_aux_transfer(struct drm_dp_aux *aux, > > + struct drm_dp_aux_msg *msg) > > +{ > > + struct cdns_mhdp8501_device *mhdp = > dev_get_drvdata(aux->dev); > > + bool native = msg->request & (DP_AUX_NATIVE_WRITE & > DP_AUX_NATIVE_READ); > > + int ret; > > + > > + /* Ignore address only message */ > > + if (!msg->size || !msg->buffer) { > > + msg->reply = native ? > > + DP_AUX_NATIVE_REPLY_ACK : > DP_AUX_I2C_REPLY_ACK; > > + return msg->size; > > + } > > + > > + if (!native) { > > + dev_err(mhdp->dev, "%s: only native messages > supported\n", > __func__); > > + return -EINVAL; > > + } > > + > > + /* msg sanity check */ > > + if (msg->size > DP_AUX_MAX_PAYLOAD_BYTES) { > > + dev_err(mhdp->dev, "%s: invalid msg: size(%zu), > request(%x)\n", > > + __func__, msg->size, (unsigned int)msg- > >request); > > + return -EINVAL; > > + } > > + > > + if (msg->request == DP_AUX_NATIVE_WRITE) { > > + const u8 *buf = msg->buffer; > > + int i; > > + > > + for (i = 0; i < msg->size; ++i) { > > + ret = cdns_mhdp_dpcd_write(&mhdp->base, > > + msg->address > + > i, buf[i]); > > + if (!ret) > > + continue; > > I personally don't like this style. I would prefer checking for 'if (ret)' to > bail out in error case. OK. > > > + DRM_DEV_ERROR(mhdp->dev, "Failed to write > DPCD\n"); > > Please replace all DRM_ macros, see [1] for details. As you don't have a > drm_dev you will most probably need a dev_err & friends. OK I will do it. > > > + > > + return ret; > > + } > > + msg->reply = DP_AUX_NATIVE_REPLY_ACK; > > + return msg->size; > > + } > > + > > + if (msg->request == DP_AUX_NATIVE_READ) { > > + ret = cdns_mhdp_dpcd_read(&mhdp->base, > msg->address, > > + msg->buffer, msg->size); > > + if (ret < 0) > > + return -EIO; > > Any specific reason to return -EIO instead of ret? You return ret in case of > error from cdns_mhdp_dpcd_write as well. No, will return ret in next version. > > > + msg->reply = DP_AUX_NATIVE_REPLY_ACK; > > + return msg->size; > > + } > > + return 0; > > +} > > + > > +int cdns_dp_aux_destroy(struct cdns_mhdp8501_device *mhdp) > > +{ > > + drm_dp_aux_unregister(&mhdp->dp.aux); > > + > > + return 0; > > +} > > + > > +static int cdns_dp_get_msa_misc(struct video_info *video, > > + struct drm_display_mode *mode) > > mode is unused. OK will remove it. > > > +{ > > + u32 msa_misc; > > + u8 val[2] = {0}; > > Please use two separate variables for color space and bpc. OK. > > > + > > + switch (video->color_fmt) { > > + /* set YUV default color space conversion to BT601 */ > > + case DRM_COLOR_FORMAT_YCBCR444: > > + val[0] = 6 + BT_601 * 8; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR422: > > + val[0] = 5 + BT_601 * 8; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR420: > > + val[0] = 5; > > + break; > > + case DRM_COLOR_FORMAT_RGB444: > > + default: > > + val[0] = 0; > > + break; > > + }; > > + > > + switch (video->bpc) { > > + case 6: > > + val[1] = 0; > > + break; > > + case 10: > > + val[1] = 2; > > + break; > > + case 12: > > + val[1] = 3; > > + break; > > + case 16: > > + val[1] = 4; > > + break; > > + case 8: > > + default: > > + val[1] = 1; > > + break; > > + }; > > + > > + msa_misc = 2 * val[0] + 32 * val[1]; > > Is this multiplication intended to do bit shifting? Yes, it will replace by bit shifting. > > > + > > + return msa_misc; > > +} > > + > > +static int cdns_dp_config_video(struct cdns_mhdp8501_device *mhdp) > > +{ > > + struct video_info *video = &mhdp->video_info; > > + struct drm_display_mode *mode = &mhdp->mode; > > + bool h_sync_polarity, v_sync_polarity; > > + u64 symbol; > > + u32 val, link_rate, rem; > > + u8 bit_per_pix, tu_size_reg = TU_SIZE; > > + int ret; > > + > > + bit_per_pix = (video->color_fmt == > DRM_COLOR_FORMAT_YCBCR422) ? > > + (video->bpc * 2) : (video->bpc * 3); > > + > > + link_rate = mhdp->dp.rate / 1000; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, BND_HSYNC2VSYNC, > > VIF_BYPASS_INTERLACE); + if (ret) > > + goto err_config_video; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, > HSYNC2VSYNC_POL_CTRL, 0); > > + if (ret) > > + goto err_config_video; > > + > > + /* > > + * get a best tu_size and valid symbol: > > + * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32 > > + * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes) > > + * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set > > + * TU += 2 and repeat 2nd step. > > + */ > > + do { > > + tu_size_reg += 2; > > + symbol = tu_size_reg * mode->clock * bit_per_pix; > > + do_div(symbol, mhdp->dp.num_lanes * link_rate * 8); > > + rem = do_div(symbol, 1000); > > + if (tu_size_reg > 64) { > > + ret = -EINVAL; > > + DRM_DEV_ERROR(mhdp->dev, > > + "tu error, clk:%d, lanes:%d, > rate:%d\n", > > + mode->clock, > mhdp->dp.num_lanes, > link_rate); > > + goto err_config_video; > > + } > > + } while ((symbol <= 1) || (tu_size_reg - symbol < 4) || > > + (rem > 850) || (rem < 100)); > > + > > + val = symbol + (tu_size_reg << 8); > > + val |= TU_CNT_RST_EN; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_FRAMER_TU, val); > > + if (ret) > > + goto err_config_video; > > + > > + /* set the FIFO Buffer size */ > > + val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate; > > + val /= (mhdp->dp.num_lanes * link_rate); > > + val = div_u64(8 * (symbol + 1), bit_per_pix) - val; > > + val += 2; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_VC_TABLE(15), val); > > + > > + switch (video->bpc) { > > + case 6: > > + val = BCS_6; > > + break; > > + case 10: > > + val = BCS_10; > > + break; > > + case 12: > > + val = BCS_12; > > + break; > > + case 16: > > + val = BCS_16; > > + break; > > + case 8: > > + default: > > + val = BCS_8; > > + break; > > + }; > > + > > + val += video->color_fmt << 8; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_FRAMER_PXL_REPR, > val); > > + if (ret) > > + goto err_config_video; > > + > > + v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); > > + h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); > > + > > + val = h_sync_polarity ? DP_FRAMER_SP_HSP : 0; > > + val |= v_sync_polarity ? DP_FRAMER_SP_VSP : 0; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_FRAMER_SP, val); > > + if (ret) > > + goto err_config_video; > > + > > + val = (mode->hsync_start - mode->hdisplay) << 16; > > + val |= mode->htotal - mode->hsync_end; > > + ret = cdns_mhdp_reg_write(&mhdp->base, > DP_FRONT_BACK_PORCH, val); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->hdisplay * bit_per_pix / 8; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_BYTE_COUNT, val); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16); > > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_HORIZONTAL_0, > val); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->hsync_end - mode->hsync_start; > > + val |= (mode->hdisplay << 16) | (h_sync_polarity << 15); > > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_HORIZONTAL_1, > val); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->vtotal; > > + val |= (mode->vtotal - mode->vsync_start) << 16; > > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_VERTICAL_0, val); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->vsync_end - mode->vsync_start; > > + val |= (mode->vdisplay << 16) | (v_sync_polarity << 15); > > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_VERTICAL_1, val); > > + if (ret) > > + goto err_config_video; > > + > > + val = cdns_dp_get_msa_misc(video, mode); > > + ret = cdns_mhdp_reg_write(&mhdp->base, MSA_MISC, val); > > + if (ret) > > + goto err_config_video; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, STREAM_CONFIG, 1); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->hsync_end - mode->hsync_start; > > + val |= mode->hdisplay << 16; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_HORIZONTAL, val); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->vdisplay; > > + val |= (mode->vtotal - mode->vsync_start) << 16; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_VERTICAL_0, val); > > + if (ret) > > + goto err_config_video; > > + > > + val = mode->vtotal; > > + ret = cdns_mhdp_reg_write(&mhdp->base, DP_VERTICAL_1, val); > > + if (ret) > > + goto err_config_video; > > + > > + ret = cdns_mhdp_dp_reg_write_bit(&mhdp->base, DP_VB_ID, 2, 1, > 0); > > + > > +err_config_video: > > + if (ret) > > + DRM_DEV_ERROR(mhdp->dev, "config video > failed: %d\n", > ret); > > + return ret; > > +} > > + > > +static void cdns_dp_pixel_clk_reset(struct cdns_mhdp8501_device *mhdp) > > +{ > > + u32 val; > > + > > + /* reset pixel clk */ > > + cdns_mhdp_reg_read(&mhdp->base, SOURCE_HDTX_CAR, &val); > > + cdns_mhdp_reg_write(&mhdp->base, SOURCE_HDTX_CAR, val & > 0xFD); > > + cdns_mhdp_reg_write(&mhdp->base, SOURCE_HDTX_CAR, val); > > +} > > + > > +static int cdns_dp_set_video_status(struct cdns_mhdp8501_device *mhdp, > int > > active) +{ > > + u8 msg; > > + int ret; > > + > > + msg = !!active; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > DPTX_SET_VIDEO, + sizeof(msg), > &msg); > > + if (ret) > > + DRM_DEV_ERROR(mhdp->dev, "set video status > failed: %d\n", > ret); > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + return ret; > > +} > > + > > +static int cdns_dp_training_start(struct cdns_mhdp8501_device *mhdp) > > +{ > > + unsigned long timeout; > > + u8 msg, event[2]; > > + int ret; > > + > > + msg = LINK_TRAINING_RUN; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + /* start training */ > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + DPTX_TRAINING_CONTROL, > sizeof(msg), &msg); > > + if (ret) > > + goto err_training_start; > > + > > + timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS); > > + while (time_before(jiffies, timeout)) { > > + msleep(LINK_TRAINING_RETRY_MS); > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + DPTX_READ_EVENT, > 0, > NULL); > > + if (ret) > > + goto err_training_start; > > + > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + > DPTX_READ_EVENT, sizeof(event)); > > + if (ret) > > + goto err_training_start; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, > event, > sizeof(event)); > > + if (ret) > > + goto err_training_start; > > + > > + if (event[1] & CLK_RECOVERY_FAILED) { > > + DRM_DEV_ERROR(mhdp->dev, "clock recovery > failed\n"); > > + } else if (event[1] & EQ_PHASE_FINISHED) { > > + mutex_unlock(&mhdp->mbox_mutex); > > + return 0; > > + } > > + } > > + > > + ret = -ETIMEDOUT; > > + > > +err_training_start: > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret); > > + return ret; > > +} > > + > > +static int cdns_dp_get_training_status(struct cdns_mhdp8501_device > *mhdp) > > +{ > > + u8 status[13]; > > + int ret; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + DPTX_READ_LINK_STAT, 0, > NULL); > > + if (ret) > > + goto err_get_training_status; > > + > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + > DPTX_READ_LINK_STAT, > > + sizeof(status)); > > + if (ret) > > + goto err_get_training_status; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, status, > sizeof(status)); > > + if (ret) > > + goto err_get_training_status; > > + > > + mhdp->dp.rate = drm_dp_bw_code_to_link_rate(status[0]); > > + mhdp->dp.num_lanes = status[1]; > > + > > +err_get_training_status: > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + if (ret) > > + DRM_DEV_ERROR(mhdp->dev, "get training status failed: > %d\n", > > + ret); > > + return ret; > > +} > > + > > +static int cdns_dp_train_link(struct cdns_mhdp8501_device *mhdp) > > +{ > > + int ret; > > + > > + ret = cdns_dp_training_start(mhdp); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed to start > training %d\n", > > + ret); > > + return ret; > > + } > > + > > + ret = cdns_dp_get_training_status(mhdp); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat > %d\n", > > + ret); > > + return ret; > > + } > > + > > + DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", > mhdp->dp.rate, > > + mhdp->dp.num_lanes); > > + return ret; > > +} > > + > > +int cdns_dp_set_host_cap(struct cdns_mhdp8501_device *mhdp) > > +{ > > + u8 msg[8]; > > + int ret; > > + > > + msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate); > > + msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN; > > + msg[2] = VOLTAGE_LEVEL_2; > > + msg[3] = PRE_EMPHASIS_LEVEL_3; > > + msg[4] = PTS1 | PTS2 | PTS3 | PTS4; > > + msg[5] = FAST_LT_NOT_SUPPORT; > > + msg[6] = mhdp->lane_mapping; > > + msg[7] = ENHANCED; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + DPTX_SET_HOST_CAPABILITIES, > > + sizeof(msg), msg); > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + > > + if (ret) > > + DRM_DEV_ERROR(mhdp->dev, "set host cap > failed: %d\n", > ret); > > + > > + return ret; > > +} > > + > > +static int cdns_dp_get_edid_block(void *data, u8 *edid, > > + unsigned int block, size_t length) > > +{ > > + struct cdns_mhdp8501_device *mhdp = data; > > + u8 msg[2], reg[2], i; > > + int ret; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + for (i = 0; i < 4; i++) { > > + msg[0] = block / 2; > > + msg[1] = block % 2; > > + > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + DPTX_GET_EDID, > sizeof(msg), msg); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_DP_TX, > > + > DPTX_GET_EDID, > > + sizeof(reg) + > length); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, reg, > sizeof(reg)); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, edid, > length); > > + if (ret) > > + continue; > > + > > + if (reg[0] == length && reg[1] == block / 2) > > + break; > > + } > > + > > + if (ret) > > + DRM_DEV_ERROR(mhdp->dev, "get block[%d] edid failed: > %d\n", > > + block, ret); > > + > > + mutex_unlock(&mhdp->mbox_mutex); > > + return ret; > > +} > > + > > +static void cdns_dp_mode_set(struct cdns_mhdp8501_device *mhdp) > > +{ > > + union phy_configure_opts phy_cfg; > > + int ret; > > + > > + cdns_dp_pixel_clk_reset(mhdp); > > + > > + /* Get DP Caps */ > > + ret = drm_dp_dpcd_read(&mhdp->dp.aux, DP_DPCD_REV, > mhdp->dp.dpcd, > > + DP_RECEIVER_CAP_SIZE); > > + if (ret < 0) { > > + DRM_ERROR("Failed to get caps %d\n", ret); > > + return; > > + } > > + > > + mhdp->dp.rate = drm_dp_max_link_rate(mhdp->dp.dpcd); > > + mhdp->dp.num_lanes = drm_dp_max_lane_count(mhdp->dp.dpcd); > > + > > + /* check the max link rate */ > > + if (mhdp->dp.rate > CDNS_DP_MAX_LINK_RATE) > > + mhdp->dp.rate = CDNS_DP_MAX_LINK_RATE; > > + > > + phy_cfg.dp.lanes = mhdp->dp.num_lanes; > > + phy_cfg.dp.link_rate = mhdp->dp.rate; > > + phy_cfg.dp.set_lanes = false; > > + phy_cfg.dp.set_rate = false; > > + phy_cfg.dp.set_voltages = true; > > + > > + /* Mailbox protect for DP PHY access */ > > + mutex_lock(&mhdp->mbox_mutex); > > + ret = phy_configure(mhdp->phy, &phy_cfg); > > + mutex_unlock(&mhdp->mbox_mutex); > > + if (ret) { > > + dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n", > > + __func__, ret); > > + return; > > + } > > + > > + /* Video off */ > > + ret = cdns_dp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed to valid > video %d\n", > ret); > > + return; > > + } > > + > > + /* Line swapping */ > > + cdns_mhdp_reg_write(&mhdp->base, LANES_CONFIG, 0x00400000 > | > > mhdp->lane_mapping); + > > + /* Set DP host capability */ > > + ret = cdns_dp_set_host_cap(mhdp); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed to set host > cap %d\n", > ret); > > + return; > > + } > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, > DP_AUX_SWAP_INVERSION_CONTROL, > > + AUX_HOST_INVERT); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed to set host > invert %d\n", > ret); > > + return; > > + } > > + > > + ret = cdns_dp_config_video(mhdp); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed to config > video %d\n", > ret); > > + return; > > + } > > +} > > + > > +static int cdns_dp_bridge_attach(struct drm_bridge *bridge, > > + enum drm_bridge_attach_flags flags) > > +{ > > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > > + > > + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { > > + DRM_ERROR("do not support creating a > drm_connector\n"); > > + return -EINVAL; > > + } > > + > > + mhdp->dp.aux.drm_dev = bridge->dev; > > + > > + return drm_dp_aux_register(&mhdp->dp.aux); > > +} > > + > > +static enum drm_mode_status > > +cdns_dp_bridge_mode_valid(struct drm_bridge *bridge, > > + const struct drm_display_info *info, > > + const struct drm_display_mode *mode) > > +{ > > + enum drm_mode_status mode_status = MODE_OK; > > + > > + /* We don't support double-clocked 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) > > Comment does not match code. OK. > > > + return MODE_BAD_HVALUE; > > + > > + if (mode->vdisplay > 2160) > > + return MODE_BAD_VVALUE; > > + > > + return mode_status; > > +} > > + > > +static enum drm_connector_status > > +cdns_dp_bridge_detect(struct drm_bridge *bridge) > > +{ > > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > > + > > + return cdns_mhdp8501_detect(mhdp); > > +} > > + > > +static struct edid *cdns_dp_bridge_get_edid(struct drm_bridge *bridge, > > + struct drm_connector > *connector) > > +{ > > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > > + > > + return drm_do_get_edid(connector, cdns_dp_get_edid_block, > mhdp); > > +} > > + > > +static void cdns_dp_bridge_atomic_disable(struct drm_bridge *bridge, > > + struct drm_bridge_state > *old_state) > > +{ > > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > > + > > + cdns_dp_set_video_status(mhdp, CONTROL_VIDEO_IDLE); > > + mhdp->curr_conn = NULL; > > + > > + /* Mailbox protect for DP PHY access */ > > + mutex_lock(&mhdp->mbox_mutex); > > + phy_power_off(mhdp->phy); > > + mutex_unlock(&mhdp->mbox_mutex); > > +} > > + > > +static void cdns_dp_bridge_atomic_enable(struct drm_bridge *bridge, > > + struct drm_bridge_state > *old_state) > > +{ > > + struct cdns_mhdp8501_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; > > + int ret; > > + > > + 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; > > + > > + switch (connector->display_info.bpc) { > > + case 10: > > + video->bpc = 10; > > + break; > > + case 6: > > + video->bpc = 6; > > + break; > > + default: > > + video->bpc = 8; > > + break; > > + } > > + > > + /* The only currently supported format */ > > + video->color_fmt = DRM_COLOR_FORMAT_RGB444; > > + > > + DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, > mode- > >clock); > > + memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode)); > > + > > + cdns_dp_mode_set(mhdp); > > + > > + /* Link trainning */ > > + ret = cdns_dp_train_link(mhdp); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed link train %d\n", > ret); > > + return; > > + } > > + > > + ret = cdns_dp_set_video_status(mhdp, CONTROL_VIDEO_VALID); > > + if (ret) { > > + DRM_DEV_ERROR(mhdp->dev, "Failed to valid > video %d\n", > ret); > > + return; > > + } > > +} > > + > > +const struct drm_bridge_funcs cdns_dp_bridge_funcs = { > > + .attach = cdns_dp_bridge_attach, > > + .mode_valid = cdns_dp_bridge_mode_valid, > > + .detect = cdns_dp_bridge_detect, > > + .get_edid = cdns_dp_bridge_get_edid, > > + .atomic_enable = cdns_dp_bridge_atomic_enable, > > + .atomic_disable = cdns_dp_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, > > Can you please sort the entries in the same order as > cdns_hdmi_bridge_funcs? > This makes it easier to detect which entries are provided in both files. > OK. > > +}; > > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c > > b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c new file mode > 100644 > > index 0000000000000..73d1c35a74599 > > --- /dev/null > > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8501-hdmi.c > > @@ -0,0 +1,673 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Cadence MHDP8501 HDMI bridge driver > > + * > > + * Copyright (C) 2019-2023 NXP Semiconductor, Inc. > > + * > > + */ > > +#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_print.h> > > +#include <linux/phy/phy.h> > > +#include <linux/phy/phy-hdmi.h> > > + > > +#include "cdns-mhdp8501-core.h" > > + > > +/** > > + * cdns_hdmi_infoframe_set() - fill the HDMI AVI infoframe > > + * @mhdp: phandle to mhdp device. > > + * @entry_id: The packet memory address in which the data is written. > > + * @packet_len: 32, only 32 bytes now. > > + * @packet: point to InfoFrame Packet. > > + * packet[0] = 0 > > + * packet[1-3] = HB[0-2] InfoFrame Packet Header > > + * packet[4-31 = PB[0-27] InfoFrame Packet Contents > > + * @packet_type: Packet Type of InfoFrame in HDMI Specification. > > + * > > + */ > > +static void cdns_hdmi_infoframe_set(struct cdns_mhdp8501_device > *mhdp, > > + u8 entry_id, u8 packet_len, > > + u8 *packet, u8 packet_type) > > +{ > > + u32 packet32, len32; > > + u32 val, i; > > + > > + /* only support 32 bytes now */ > > + if (packet_len != 32) > > + return; > > + > > + /* 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 */ > > + len32 = packet_len / 4; > > + for (i = 0; i < len32; i++) { > > + packet32 = get_unaligned_le32(packet + 4 * i); > > + writel(F_DATA_WR(packet32), 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_mhdp8501_device *mhdp = data; > > + u8 msg[2], reg[5], i; > > + int ret; > > + > > + mutex_lock(&mhdp->mbox_mutex); > > + > > + for (i = 0; i < 4; i++) { > > + msg[0] = block / 2; > > + msg[1] = block % 2; > > + > > + ret = cdns_mhdp_mailbox_send(&mhdp->base, > MB_MODULE_ID_HDMI_TX, > > HDMI_TX_EDID, + > sizeof(msg), > msg); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_HDMI_TX, > > + > HDMI_TX_EDID, > sizeof(reg) + length); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, reg, > sizeof(reg)); > > + if (ret) > > + continue; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, 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); > > Please replace all DRM_ macros, see [1] for details. As you don't have a > drm_dev you will most probably need a dev_err & friends. > > [1] > https://lore.ke/ > rnel.org%2Flinux-arm-kernel%2F285db5bc-f901-e09f-7f86-6638d260c283%4 > 0linaro.org%2FT%2F%23ma30715ccd9004ad19a6741c3f6b3dfd68d526018& > data=05%7C01%7CSandor.yu%40nxp.com%7Cf09434a5d5fd48d090e308dbcf > 0deb19%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C638331430 > 937142084%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIj > oiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata > =h72fs%2BkA2pTNbM1MuAPYSHbkw0ScLLYu2zeeSmdKE5I%3D&reserved=0 > OK > > + return ret; > > +} > > + > > +static int cdns_hdmi_scdc_write(struct cdns_mhdp8501_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->base, > MB_MODULE_ID_HDMI_TX, > > HDMI_TX_WRITE, + sizeof(msg), > msg); > > + if (ret) > > + goto err_scdc_write; > > + > > + ret = cdns_mhdp_mailbox_recv_header(&mhdp->base, > MB_MODULE_ID_HDMI_TX, > > + HDMI_TX_WRITE, > sizeof(reg)); > > + if (ret) > > + goto err_scdc_write; > > + > > + ret = cdns_mhdp_mailbox_recv_data(&mhdp->base, 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; > > +} > > + > > +static int cdns_hdmi_ctrl_init(struct cdns_mhdp8501_device *mhdp, int > > protocol) +{ > > + u32 reg0, reg1, val; > > + int ret; > > + > > + /* Set PHY to HDMI data */ > > + ret = cdns_mhdp_reg_write(&mhdp->base, PHY_DATA_SEL, > > F_SOURCE_PHY_MHDP_SEL(1)); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, 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->base, SOURCE_PHY_CAR, > 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_HDTX_CAR, > 0xFF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_PKT_CAR, 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_AIF_CAR, 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_CIPHER_CAR, > 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_CRYPTO_CAR, > 0xF); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(&mhdp->base, SOURCE_CEC_CAR, 3); > > + if (ret < 0) > > + return ret; > > + > > + reg0 = 0x7c1f; > > + reg1 = 0x7c1f; > > + if (protocol == MODE_HDMI_2_0) { > > + reg0 = 0; > > + reg1 = 0xFFFFF; > > + } > > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_CLOCK_REG_0, > reg0); > > + if (ret < 0) > > + return ret; > > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_CLOCK_REG_1, > reg1); > > + if (ret < 0) > > + return ret; > > + > > + /* set hdmi mode and preemble mode data enable */ > > Please stick to consistent uppercase when naming HDMI. > OK. > > + 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->base, HDTX_CONTROLLER, > val); > > + > > + return ret; > > +} > > + > > +static int cdns_hdmi_mode_config(struct cdns_mhdp8501_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); > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, SCHEDULER_H_SIZE, > (hactive << > 16) + > > hblank); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, SCHEDULER_V_SIZE, > (vactive << > 16) + > > vblank); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, > HDTX_SIGNAL_FRONT_WIDTH, > (vfront << > > 16) + hfront); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, > HDTX_SIGNAL_SYNC_WIDTH, > (vsync << > > 16) + hsync); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, > HDTX_SIGNAL_BACK_WIDTH, > (vback << > > 16) + hback); + if (ret < 0) > > + return ret; > > + > > + ret = cdns_mhdp_reg_write(&mhdp->base, > HSYNC2VSYNC_POL_CTRL, > > v_h_polarity); + if (ret < 0) > > + return ret; > > + > > + /* Reset Data Enable */ > > + cdns_mhdp_reg_read(&mhdp->base, HDTX_CONTROLLER, &val); > > + val &= ~F_DATA_EN(1); > > + ret = cdns_mhdp_reg_write(&mhdp->base, 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->base, HDTX_CONTROLLER, > val); > > + if (ret < 0) > > + return ret; > > + > > + /* set data enable */ > > + val |= F_DATA_EN(1); > > + ret = cdns_mhdp_reg_write(&mhdp->base, HDTX_CONTROLLER, > val); > > + > > + return ret; > > +} > > + > > +static int cdns_hdmi_disable_gcp(struct cdns_mhdp8501_device *mhdp) > > +{ > > + u32 val; > > + > > + cdns_mhdp_reg_read(&mhdp->base, HDTX_CONTROLLER, &val); > > + val &= ~F_GCP_EN(1); > > + > > + return cdns_mhdp_reg_write(&mhdp->base, HDTX_CONTROLLER, > val); > > +} > > + > > +static int cdns_hdmi_enable_gcp(struct cdns_mhdp8501_device *mhdp) > > +{ > > + u32 val; > > + > > + cdns_mhdp_reg_read(&mhdp->base, HDTX_CONTROLLER, &val); > > + val |= F_GCP_EN(1); > > + > > + return cdns_mhdp_reg_write(&mhdp->base, HDTX_CONTROLLER, > val); > > +} > > + > > +static void cdns_hdmi_sink_config(struct cdns_mhdp8501_device *mhdp) > > +{ > > + struct drm_scdc *scdc = > &mhdp->curr_conn->display_info.hdmi.scdc; > > + u32 char_rate = mhdp->mode.clock * mhdp->video_info.bpc / 8; > > + 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) { > > + DRM_INFO("Sink Not Support SCDC\n"); > > + return; > > + } > > + > > + if (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 cdns_hdmi_lanes_config(struct cdns_mhdp8501_device *mhdp) > > +{ > > + /* Line swapping */ > > + cdns_mhdp_reg_write(&mhdp->base, LANES_CONFIG, 0x00400000 > | > > mhdp->lane_mapping); +} > > + > > +static int cdns_hdmi_colorspace(int color_fmt) > > +{ > > + int color_space; > > + > > + switch (color_fmt) { > > + case DRM_COLOR_FORMAT_YCBCR444: > > + color_space = HDMI_COLORSPACE_YUV444; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR422: > > + color_space = HDMI_COLORSPACE_YUV422; > > + break; > > + case DRM_COLOR_FORMAT_YCBCR420: > > + color_space = HDMI_COLORSPACE_YUV420; > > + break; > > + case DRM_COLOR_FORMAT_RGB444: > > + default: > > + color_space = HDMI_COLORSPACE_RGB; > > + break; > > + } > > + > > + return color_space; > > +} > > + > > +static int cdns_hdmi_avi_info_set(struct cdns_mhdp8501_device *mhdp, > > + struct drm_display_mode *mode) > > +{ > > + struct hdmi_avi_infoframe frame; > > + 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); > > + > > + frame.colorspace = > cdns_hdmi_colorspace(mhdp->video_info.color_fmt); > > + > > + 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_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_hdmi_infoframe_set(mhdp, 0, sizeof(buf), buf, > > HDMI_INFOFRAME_TYPE_AVI); + > > + return 0; > > +} > > + > > +static void cdns_hdmi_vendor_info_set(struct cdns_mhdp8501_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_hdmi_infoframe_set(mhdp, 3, sizeof(buf), buf, > > HDMI_INFOFRAME_TYPE_VENDOR); +} > > + > > +static void cdns_hdmi_drm_info_set(struct cdns_mhdp8501_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_hdmi_infoframe_set(mhdp, 3, sizeof(buf), buf, > > HDMI_INFOFRAME_TYPE_DRM); +} > > + > > +static void cdns_hdmi_mode_set(struct cdns_mhdp8501_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; > > + > > + cdns_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 = > > cdns_hdmi_colorspace(mhdp->video_info.color_fmt); + > > + /* Mailbox protect for HDMI PHY access */ > > + mutex_lock(&mhdp->mbox_mutex); > > + ret = phy_configure(mhdp->phy, &phy_cfg); > > + mutex_unlock(&mhdp->mbox_mutex); > > + if (ret) { > > + dev_err(mhdp->dev, "%s: phy_configure() failed: %d\n", > > + __func__, ret); > > + return; > > + } > > + > > + cdns_hdmi_sink_config(mhdp); > > + > > + ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type); > > + 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 = cdns_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 */ > > + cdns_hdmi_vendor_info_set(mhdp, mode); > > + > > + cdns_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 int cdns_hdmi_bridge_attach(struct drm_bridge *bridge, > > + enum drm_bridge_attach_flags > flags) > > +{ > > + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { > > + DRM_ERROR("do not support creating a > drm_connector\n"); > > + return -EINVAL; > > + } > > + > > + 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_mhdp8501_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) > > Comment does not match code. Despite that separately check for vdisplay > and > return MODE_BAD_VVALUE if it's too big. > OK, thanks your detail review comments. Sandor > Best regards, > Alexander > > > + return MODE_BAD_HVALUE; > > + > > + /* Check modes supported by PHY */ > > + phy_cfg.hdmi.pixel_clk_rate = mode->clock; > > + > > + /* Mailbox protect for HDMI PHY access */ > > + mutex_lock(&mhdp->mbox_mutex); > > + ret = phy_validate(mhdp->phy, PHY_MODE_HDMI, 0, &phy_cfg); > > + mutex_unlock(&mhdp->mbox_mutex); > > + 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_mhdp8501_device *mhdp = bridge->driver_private; > > + struct video_info *video = &mhdp->video_info; > > + > > + /* The only currently supported format */ > > + video->bpc = 8; > > + video->color_fmt = DRM_COLOR_FORMAT_RGB444; > > + > > + return true; > > +} > > + > > +static enum drm_connector_status > > +cdns_hdmi_bridge_detect(struct drm_bridge *bridge) > > +{ > > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > > + > > + return cdns_mhdp8501_detect(mhdp); > > +} > > + > > +static struct edid *cdns_hdmi_bridge_get_edid(struct drm_bridge *bridge, > > + struct > drm_connector > *connector) > > +{ > > + struct cdns_mhdp8501_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_mhdp8501_device *mhdp = bridge->driver_private; > > + > > + mhdp->curr_conn = NULL; > > + > > + /* Mailbox protect for HDMI PHY access */ > > + mutex_lock(&mhdp->mbox_mutex); > > + phy_power_off(mhdp->phy); > > + mutex_unlock(&mhdp->mbox_mutex); > > +} > > + > > +static void cdns_hdmi_bridge_atomic_enable(struct drm_bridge *bridge, > > + struct drm_bridge_state > *old_state) > > +{ > > + struct cdns_mhdp8501_device *mhdp = bridge->driver_private; > > + struct drm_atomic_state *state = old_state->base.state; > > + struct drm_connector *connector; > > + 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)); > > + > > + cdns_hdmi_mode_set(mhdp); > > +} > > + > > +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, > > +}; > > > -- > TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany > Amtsgericht München, HRB 105018 > Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider > http://www.tq/ > -group.com%2F&data=05%7C01%7CSandor.yu%40nxp.com%7Cf09434a5d5fd > 48d090e308dbcf0deb19%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C > 0%7C638331430937142084%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4w > LjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C > %7C%7C&sdata=dsINd06yxEdG84HIvu5Fjj3rl6VyDVKb26gsr7i5MiU%3D&rese > rved=0 >
Hi Alexander, Thanks for your comments, > -----Original Message----- > From: Alexander Stein <alexander.stein@ew.tq-group.com> > > Hi Sandor, > > thanks for the patch. > > Am Dienstag, 17. Oktober 2023, 09:04:02 CEST schrieb Sandor Yu: > > Add Cadence HDP-TX DisplayPort PHY driver for i.MX8MQ > > > > Cadence HDP-TX PHY could be put in either DP mode or HDMI mode base on > > the configuration chosen. > > DisplayPort PHY mode is configurated in the driver. > > > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > > --- > > v9->v11: > > *No change. > > > > drivers/phy/freescale/Kconfig | 10 + > > drivers/phy/freescale/Makefile | 1 + > > drivers/phy/freescale/phy-fsl-imx8mq-dp.c | 720 > > ++++++++++++++++++++++ > > 3 files changed, 731 insertions(+) > > create mode 100644 drivers/phy/freescale/phy-fsl-imx8mq-dp.c > > > > diff --git a/drivers/phy/freescale/Kconfig > > b/drivers/phy/freescale/Kconfig index 853958fb2c063..c39709fd700ac > > 100644 > > --- a/drivers/phy/freescale/Kconfig > > +++ b/drivers/phy/freescale/Kconfig > > @@ -35,6 +35,16 @@ config PHY_FSL_IMX8M_PCIE > > Enable this to add support for the PCIE PHY as found on > > i.MX8M family of SOCs. > > > > +config PHY_FSL_IMX8MQ_DP > > + tristate "Freescale i.MX8MQ DP PHY support" > > + depends on OF && HAS_IOMEM > > + depends on COMMON_CLK > > + select GENERIC_PHY > > + select CDNS_MHDP_HELPER > > + help > > + Enable this to support the Cadence HDPTX DP PHY driver > > + on i.MX8MQ SOC. > > + > > endif > > > > config PHY_FSL_LYNX_28G > > diff --git a/drivers/phy/freescale/Makefile > > b/drivers/phy/freescale/Makefile index cedb328bc4d28..47e5285209fa8 > > 100644 > > --- a/drivers/phy/freescale/Makefile > > +++ b/drivers/phy/freescale/Makefile > > @@ -1,4 +1,5 @@ > > # SPDX-License-Identifier: GPL-2.0-only > > +obj-$(CONFIG_PHY_FSL_IMX8MQ_DP) += > phy-fsl-imx8mq-dp.o > > obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o > > obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o > > obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o > > diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-dp.c > > b/drivers/phy/freescale/phy-fsl-imx8mq-dp.c new file mode 100644 index > > 0000000000000..5f0d7da16b422 > > --- /dev/null > > +++ b/drivers/phy/freescale/phy-fsl-imx8mq-dp.c > > @@ -0,0 +1,720 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Cadence HDP-TX Display Port Interface (DP) PHY driver > > + * > > + * Copyright (C) 2022, 2023 NXP Semiconductor, Inc. > > + */ > > +#include <asm/unaligned.h> > > +#include <drm/bridge/cdns-mhdp-helper.h> #include <linux/clk.h> > > +#include <linux/kernel.h> #include <linux/phy/phy.h> #include > > +<linux/platform_device.h> #include <linux/io.h> #include > > +<linux/iopoll.h> > > + > > +#define ADDR_PHY_AFE 0x80000 > > + > > +/* PHY registers */ > > +#define CMN_SSM_BIAS_TMR 0x0022 > > +#define CMN_PLLSM0_PLLEN_TMR 0x0029 > > +#define CMN_PLLSM0_PLLPRE_TMR 0x002a > > +#define CMN_PLLSM0_PLLVREF_TMR 0x002b > > +#define CMN_PLLSM0_PLLLOCK_TMR 0x002c > > +#define CMN_PLLSM0_USER_DEF_CTRL 0x002f > > +#define CMN_PSM_CLK_CTRL 0x0061 > > +#define CMN_PLL0_VCOCAL_START 0x0081 > > +#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084 > > +#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085 > > +#define CMN_PLL0_INTDIV 0x0094 > > +#define CMN_PLL0_FRACDIV 0x0095 > > +#define CMN_PLL0_HIGH_THR 0x0096 > > +#define CMN_PLL0_DSM_DIAG 0x0097 > > +#define CMN_PLL0_SS_CTRL2 0x0099 > > +#define CMN_ICAL_INIT_TMR 0x00c4 > > +#define CMN_ICAL_ITER_TMR 0x00c5 > > +#define CMN_RXCAL_INIT_TMR 0x00d4 > > +#define CMN_RXCAL_ITER_TMR 0x00d5 > > +#define CMN_TXPUCAL_INIT_TMR 0x00e4 > > +#define CMN_TXPUCAL_ITER_TMR 0x00e5 > > +#define CMN_TXPDCAL_INIT_TMR 0x00f4 > > +#define CMN_TXPDCAL_ITER_TMR 0x00f5 > > +#define CMN_ICAL_ADJ_INIT_TMR 0x0102 > > +#define CMN_ICAL_ADJ_ITER_TMR 0x0103 > > +#define CMN_RX_ADJ_INIT_TMR 0x0106 > > +#define CMN_RX_ADJ_ITER_TMR 0x0107 > > +#define CMN_TXPU_ADJ_INIT_TMR 0x010a > > +#define CMN_TXPU_ADJ_ITER_TMR 0x010b > > +#define CMN_TXPD_ADJ_INIT_TMR 0x010e > > +#define CMN_TXPD_ADJ_ITER_TMR 0x010f > > +#define CMN_DIAG_PLL0_FBH_OVRD 0x01c0 > > +#define CMN_DIAG_PLL0_FBL_OVRD 0x01c1 > > +#define CMN_DIAG_PLL0_OVRD 0x01c2 > > +#define CMN_DIAG_PLL0_TEST_MODE 0x01c4 > > +#define CMN_DIAG_PLL0_V2I_TUNE 0x01c5 > > +#define CMN_DIAG_PLL0_CP_TUNE 0x01c6 > > +#define CMN_DIAG_PLL0_LF_PROG 0x01c7 > > +#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01c8 > > +#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01c9 > > +#define CMN_DIAG_HSCLK_SEL 0x01e0 > > +#define CMN_DIAG_PER_CAL_ADJ 0x01ec > > +#define CMN_DIAG_CAL_CTRL 0x01ed > > +#define CMN_DIAG_ACYA 0x01ff > > +#define XCVR_PSM_RCTRL 0x4001 > > +#define XCVR_PSM_CAL_TMR 0x4002 > > +#define XCVR_PSM_A0IN_TMR 0x4003 > > +#define TX_TXCC_CAL_SCLR_MULT_0 0x4047 > > +#define TX_TXCC_CPOST_MULT_00_0 0x404c > > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > > +#define XCVR_DIAG_HSCLK_SEL 0x40e1 > > +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2 > > +#define TX_PSC_A0 0x4100 > > +#define TX_PSC_A1 0x4101 > > +#define TX_PSC_A2 0x4102 > > +#define TX_PSC_A3 0x4103 > > +#define TX_RCVDET_EN_TMR 0x4122 > > +#define TX_RCVDET_ST_TMR 0x4123 > > +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 > > +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 > > +#define TX_DIAG_ACYA_0 0x41ff > > +#define TX_DIAG_ACYA_1 0x43ff > > +#define TX_DIAG_ACYA_2 0x45ff > > +#define TX_DIAG_ACYA_3 0x47ff > > +#define TX_ANA_CTRL_REG_1 0x5020 > > +#define TX_ANA_CTRL_REG_2 0x5021 > > +#define TX_DIG_CTRL_REG_1 0x5023 > > +#define TX_DIG_CTRL_REG_2 0x5024 > > +#define TXDA_CYA_AUXDA_CYA 0x5025 > > +#define TX_ANA_CTRL_REG_3 0x5026 > > +#define TX_ANA_CTRL_REG_4 0x5027 > > +#define TX_ANA_CTRL_REG_5 0x5029 > > +#define RX_PSC_A0 0x8000 > > +#define RX_PSC_CAL 0x8006 > > +#define PHY_HDP_MODE_CTRL 0xc008 > > +#define PHY_HDP_CLK_CTL 0xc009 > > +#define PHY_PMA_CMN_CTRL1 0xc800 > > + > > +/* PHY_PMA_CMN_CTRL1 */ > > +#define CMA_REF_CLK_SEL_MASK GENMASK(6, 4) > > +#define CMA_REF_CLK_RCV_EN_MASK BIT(3) > > +#define CMA_REF_CLK_RCV_EN 1 > > + > > +/* PHY_HDP_CLK_CTL */ > > +#define PLL_DATA_RATE_CLK_DIV_MASK GENMASK(15, 8) > > +#define PLL_DATA_RATE_CLK_DIV_HBR 0x24 > > +#define PLL_DATA_RATE_CLK_DIV_HBR2 0x12 > > +#define PLL_CLK_EN_ACK BIT(3) > > +#define PLL_CLK_EN BIT(2) > > +#define PLL_READY BIT(1) > > +#define PLL_EN BIT(0) > > + > > +/* CMN_DIAG_HSCLK_SEL */ > > +#define HSCLK1_SEL_MASK GENMASK(5, > 4) > > +#define HSCLK0_SEL_MASK GENMASK(1, > 0) > > +#define HSCLK_PLL0_DIV2 1 > > + > > +/* XCVR_DIAG_HSCLK_SEL */ > > +#define HSCLK_SEL_MODE3_MASK GENMASK(13, 12) > > +#define HSCLK_SEL_MODE3_HSCLK1 1 > > + > > +/* XCVR_DIAG_PLLDRC_CTRL */ > > +#define DPLL_CLK_SEL_MODE3 BIT(14) > > +#define DPLL_DATA_RATE_DIV_MODE3_MASK > GENMASK(13, 12) > > + > > +/* PHY_HDP_MODE_CTRL */ > > +#define POWER_STATE_A3_ACK BIT(7) > > +#define POWER_STATE_A2_ACK BIT(6) > > +#define POWER_STATE_A1_ACK BIT(5) > > +#define POWER_STATE_A0_ACK BIT(4) > > +#define POWER_STATE_A3 BIT(3) > > +#define POWER_STATE_A2 BIT(2) > > +#define POWER_STATE_A1 BIT(1) > > +#define POWER_STATE_A0 BIT(0) > > + > > +#define REF_CLK_27MHZ 27000000 > > + > > +enum dp_link_rate { > > + RATE_1_6 = 162000, > > + RATE_2_1 = 216000, > > + RATE_2_4 = 243000, > > + RATE_2_7 = 270000, > > + RATE_3_2 = 324000, > > + RATE_4_3 = 432000, > > + RATE_5_4 = 540000, > > + RATE_8_1 = 810000, > > RATE_8_1 is unused. OK, will remove it. > > > +}; > > + > > +#define MAX_LINK_RATE RATE_5_4 > > + > > +struct phy_pll_reg { > > + u16 val[7]; > > + u32 addr; > > +}; > > + > > +static const struct phy_pll_reg phy_pll_27m_cfg[] = { > > + /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 > register > > address */ + {{ 0x010e, 0x010e, 0x010e, 0x010e, 0x010e, 0x010e, > > 0x010e > }, > > CMN_PLL0_VCOCAL_INIT_TMR }, + {{ 0x001b, 0x001b, 0x001b, 0x001b, > 0x001b, > > 0x001b, 0x001b }, CMN_PLL0_VCOCAL_ITER_TMR }, + {{ 0x30b9, > 0x3087, 0x3096, > > 0x30b4, 0x30b9, 0x3087, 0x30b4 }, CMN_PLL0_VCOCAL_START }, + {{ > 0x0077, > > 0x009f, 0x00b3, 0x00c7, 0x0077, 0x009f, 0x00c7 }, CMN_PLL0_INTDIV }, + > {{ > > 0xf9da, 0xf7cd, 0xf6c7, 0xf5c1, 0xf9da, 0xf7cd, 0xf5c1 }, > > CMN_PLL0_FRACDIV }, + {{ 0x001e, 0x0028, 0x002d, 0x0032, 0x001e, > 0x0028, 0x0032 }, > > CMN_PLL0_HIGH_THR }, + {{ 0x0020, 0x0020, 0x0020, 0x0020, > 0x0020, > 0x0020, > > 0x0020 }, CMN_PLL0_DSM_DIAG }, + {{ 0x0000, 0x1000, 0x1000, > 0x1000, > 0x0000, > > 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL }, + {{ 0x0000, > 0x0000, 0x0000, > > 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD }, + > {{ 0x0000, > > 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, > > CMN_DIAG_PLL0_FBH_OVRD }, > > + {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, > > CMN_DIAG_PLL0_FBL_OVRD }, + {{ 0x0006, 0x0007, 0x0007, 0x0007, > 0x0006, > > 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE }, + {{ 0x0043, 0x0043, > > 0x0043, 0x0042, 0x0043, 0x0043, 0x0042 }, CMN_DIAG_PLL0_CP_TUNE }, + > > {{ > 0x0008, > > 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, > > CMN_DIAG_PLL0_LF_PROG }, > > + {{ 0x0100, 0x0001, 0x0001, 0x0001, 0x0100, 0x0001, 0x0001 }, > > CMN_DIAG_PLL0_PTATIS_TUNE1 }, + {{ 0x0007, 0x0001, 0x0001, > 0x0001, > 0x0007, > > 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE2 }, + {{ 0x0020, > 0x0020, > > 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_DIAG_PLL0_TEST_MODE}, > + > > {{ 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, > > CMN_PSM_CLK_CTRL } +}; > > + > > +struct cdns_hdptx_dp_phy { > > + struct cdns_mhdp_base base; > > + > > + void __iomem *regs; /* DPTX registers base */ > > + struct device *dev; > > + struct phy *phy; > > + struct mutex mbox_mutex; /* mutex to protect mailbox */ > > + struct clk *ref_clk, *apb_clk; > > + u32 ref_clk_rate; > > + u32 num_lanes; > > + u32 link_rate; > > + bool power_up; > > +}; > > + > > +static int cdns_phy_reg_write(struct cdns_hdptx_dp_phy *cdns_phy, u32 > > +addr, > > u32 val) +{ > > + return cdns_mhdp_reg_write(&cdns_phy->base, ADDR_PHY_AFE + > (addr > > + << > 2), > > val); +} > > + > > +static u32 cdns_phy_reg_read(struct cdns_hdptx_dp_phy *cdns_phy, u32 > > +addr) { > > + u32 reg32; > > + > > + cdns_mhdp_reg_read(&cdns_phy->base, ADDR_PHY_AFE + (addr << > 2), > ®32); > > + return reg32; > > +} > > + > > +static int link_rate_index(u32 rate) > > +{ > > + switch (rate) { > > + case RATE_1_6: > > + return 0; > > + case RATE_2_1: > > + return 1; > > + case RATE_2_4: > > + return 2; > > + case RATE_2_7: > > + return 3; > > + case RATE_3_2: > > + return 4; > > + case RATE_4_3: > > + return 5; > > + case RATE_5_4: > > + return 6; > > + default: > > + return -1; > > + } > > +} > > + > > +static int hdptx_dp_clk_enable(struct cdns_hdptx_dp_phy *cdns_phy) { > > + struct device *dev = cdns_phy->dev; > > + u32 ref_clk_rate; > > + int ret; > > + > > + cdns_phy->ref_clk = devm_clk_get(dev, "ref"); > > + if (IS_ERR(cdns_phy->ref_clk)) { > > + dev_err(dev, "phy ref clock not found\n"); > > + return PTR_ERR(cdns_phy->ref_clk); > > + } > > + > > + cdns_phy->apb_clk = devm_clk_get(dev, "apb"); > > + if (IS_ERR(cdns_phy->apb_clk)) { > > + dev_err(dev, "phy apb clock not found\n"); > > + return PTR_ERR(cdns_phy->apb_clk); > > + } > > + > > + ret = clk_prepare_enable(cdns_phy->ref_clk); > > + if (ret) { > > + dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); > > + return ret; > > + } > > + > > + ref_clk_rate = clk_get_rate(cdns_phy->ref_clk); > > + if (!ref_clk_rate) { > > + dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); > > + goto err_ref_clk; > > + } > > + > > + if (ref_clk_rate == REF_CLK_27MHZ) { > > + cdns_phy->ref_clk_rate = ref_clk_rate; > > + } else { > > + dev_err(cdns_phy->dev, "Not support Ref Clock Rate(%dHz) > \n", > > ref_clk_rate); + goto err_ref_clk; > > + } > > + > > + ret = clk_prepare_enable(cdns_phy->apb_clk); > > + if (ret) { > > + dev_err(cdns_phy->dev, "Failed to prepare apb clock\n"); > > + goto err_ref_clk; > > + } > > + > > + return 0; > > + > > +err_ref_clk: > > + clk_disable_unprepare(cdns_phy->ref_clk); > > + return -EINVAL; > > +} > > + > > +static void hdptx_dp_clk_disable(struct cdns_hdptx_dp_phy *cdns_phy) > > +{ > > + clk_disable_unprepare(cdns_phy->ref_clk); > > + clk_disable_unprepare(cdns_phy->apb_clk); > > +} > > + > > +static void hdptx_dp_aux_cfg(struct cdns_hdptx_dp_phy *cdns_phy) { > > + /* Power up Aux */ > > + cdns_phy_reg_write(cdns_phy, TXDA_CYA_AUXDA_CYA, 1); > > + > > + cdns_phy_reg_write(cdns_phy, TX_DIG_CTRL_REG_1, 0x3); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_DIG_CTRL_REG_2, 36); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0100); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0300); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_3, 0x0000); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2008); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2018); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0xa018); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030c); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_5, 0x0000); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_4, 0x1001); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0xa098); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0xa198); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030d); > > + ndelay(150); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030f); } > > + > > +/* PMA common configuration for 27MHz */ static void > > +hdptx_dp_phy_pma_cmn_cfg_27mhz(struct cdns_hdptx_dp_phy > > *cdns_phy) +{ > > + u32 num_lanes = cdns_phy->num_lanes; > > + u16 val; > > + int k; > > + > > + /* Enable PMA input ref clk(CMN_REF_CLK_RCV_EN) */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > > + val &= ~CMA_REF_CLK_RCV_EN_MASK; > > + val |= FIELD_PREP(CMA_REF_CLK_RCV_EN_MASK, > CMA_REF_CLK_RCV_EN); > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); > > + > > + /* Startup state machine registers */ > > + cdns_phy_reg_write(cdns_phy, CMN_SSM_BIAS_TMR, 0x0087); > > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLEN_TMR, 0x001b); > > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLPRE_TMR, 0x0036); > > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLVREF_TMR, > 0x001b); > > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_PLLLOCK_TMR, > 0x006c); > > + > > + /* Current calibration registers */ > > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_INIT_TMR, 0x0044); > > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_ITER_TMR, 0x0006); > > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_ADJ_INIT_TMR, 0x0022); > > + cdns_phy_reg_write(cdns_phy, CMN_ICAL_ADJ_ITER_TMR, 0x0006); > > + > > + /* Resistor calibration registers */ > > + cdns_phy_reg_write(cdns_phy, CMN_TXPUCAL_INIT_TMR, 0x0022); > > + cdns_phy_reg_write(cdns_phy, CMN_TXPUCAL_ITER_TMR, 0x0006); > > + cdns_phy_reg_write(cdns_phy, CMN_TXPU_ADJ_INIT_TMR, 0x0022); > > + cdns_phy_reg_write(cdns_phy, CMN_TXPU_ADJ_ITER_TMR, 0x0006); > > + cdns_phy_reg_write(cdns_phy, CMN_TXPDCAL_INIT_TMR, 0x0022); > > + cdns_phy_reg_write(cdns_phy, CMN_TXPDCAL_ITER_TMR, 0x0006); > > + cdns_phy_reg_write(cdns_phy, CMN_TXPD_ADJ_INIT_TMR, 0x0022); > > + cdns_phy_reg_write(cdns_phy, CMN_TXPD_ADJ_ITER_TMR, 0x0006); > > + cdns_phy_reg_write(cdns_phy, CMN_RXCAL_INIT_TMR, 0x0022); > > + cdns_phy_reg_write(cdns_phy, CMN_RXCAL_ITER_TMR, 0x0006); > > + cdns_phy_reg_write(cdns_phy, CMN_RX_ADJ_INIT_TMR, 0x0022); > > + cdns_phy_reg_write(cdns_phy, CMN_RX_ADJ_ITER_TMR, 0x0006); > > + > > + for (k = 0; k < num_lanes; k = k + 1) { > > + /* Power state machine registers */ > > + cdns_phy_reg_write(cdns_phy, XCVR_PSM_CAL_TMR | (k > << > > + 9), > 0x016d); > > + cdns_phy_reg_write(cdns_phy, XCVR_PSM_A0IN_TMR | (k > << > > + 9), > 0x016d); > > + /* Transceiver control and diagnostic registers */ > > + cdns_phy_reg_write(cdns_phy, > > + XCVR_DIAG_LANE_FCM_EN_MGN_TMR > | (k << 9), > > 0x00a2); + cdns_phy_reg_write(cdns_phy, > TX_DIAG_BGREF_PREDRV_DELAY | (k << > > 9), 0x0097); + /* Transmitter receiver detect registers */ > > + cdns_phy_reg_write(cdns_phy, TX_RCVDET_EN_TMR | (k << > > + 9), > 0x0a8c); > > + cdns_phy_reg_write(cdns_phy, TX_RCVDET_ST_TMR | (k << > > + 9), > 0x0036); > > + } > > + > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_0, 1); > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_1, 1); > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_2, 1); > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_3, 1); } > > + > > +static void hdptx_dp_phy_pma_cmn_pll0_27mhz(struct > cdns_hdptx_dp_phy > > *cdns_phy) +{ > > + u32 num_lanes = cdns_phy->num_lanes; > > + u32 link_rate = cdns_phy->link_rate; > > + u16 val; > > + int index, i, k; > > + > > + /* DP PLL data rate 0/1 clock divider value */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > > + val &= ~PLL_DATA_RATE_CLK_DIV_MASK; > > + if (link_rate <= RATE_2_7) > > + val |= FIELD_PREP(PLL_DATA_RATE_CLK_DIV_MASK, > > + PLL_DATA_RATE_CLK_DIV_HBR); > > + else > > + val |= FIELD_PREP(PLL_DATA_RATE_CLK_DIV_MASK, > > + PLL_DATA_RATE_CLK_DIV_HBR2); > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > > + > > + /* High speed clock 0/1 div */ > > + val = cdns_phy_reg_read(cdns_phy, CMN_DIAG_HSCLK_SEL); > > + val &= ~(HSCLK1_SEL_MASK | HSCLK0_SEL_MASK); > > + if (link_rate <= RATE_2_7) { > > + val |= FIELD_PREP(HSCLK1_SEL_MASK, HSCLK_PLL0_DIV2); > > + val |= FIELD_PREP(HSCLK0_SEL_MASK, HSCLK_PLL0_DIV2); > > + } > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_HSCLK_SEL, val); > > + > > + for (k = 0; k < num_lanes; k++) { > > + val = cdns_phy_reg_read(cdns_phy, (XCVR_DIAG_HSCLK_SEL > | > (k << 9))); > > + val &= ~HSCLK_SEL_MODE3_MASK; > > + if (link_rate <= RATE_2_7) > > + val |= FIELD_PREP(HSCLK_SEL_MODE3_MASK, > HSCLK_SEL_MODE3_HSCLK1); > > + cdns_phy_reg_write(cdns_phy, (XCVR_DIAG_HSCLK_SEL | (k > > + << > 9)), val); > > + } > > + > > + /* DP PHY PLL 27MHz configuration */ > > + index = link_rate_index(link_rate); > > + for (i = 0; i < ARRAY_SIZE(phy_pll_27m_cfg); i++) > > + cdns_phy_reg_write(cdns_phy, phy_pll_27m_cfg[i].addr, > > + phy_pll_27m_cfg[i].val[index]); > > + > > + /* Transceiver control and diagnostic registers */ > > + for (k = 0; k < num_lanes; k++) { > > + val = cdns_phy_reg_read(cdns_phy, > (XCVR_DIAG_PLLDRC_CTRL > > + | > (k << 9))); > > + val &= ~(DPLL_DATA_RATE_DIV_MODE3_MASK | > DPLL_CLK_SEL_MODE3); > > + if (link_rate <= RATE_2_7) > > + val |= > FIELD_PREP(DPLL_DATA_RATE_DIV_MODE3_MASK, > 2); > > + else > > + val |= > FIELD_PREP(DPLL_DATA_RATE_DIV_MODE3_MASK, > 1); > > + cdns_phy_reg_write(cdns_phy, (XCVR_DIAG_PLLDRC_CTRL | > (k > << 9)), val); > > + } > > + > > + for (k = 0; k < num_lanes; k = k + 1) { > > + /* Power state machine registers */ > > + cdns_phy_reg_write(cdns_phy, (XCVR_PSM_RCTRL | (k << > > + 9)), > 0xbefc); > > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A0 | (k << 9)), > 0x6799); > > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A1 | (k << 9)), > 0x6798); > > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A2 | (k << 9)), > 0x0098); > > + cdns_phy_reg_write(cdns_phy, (TX_PSC_A3 | (k << 9)), > 0x0098); > > + /* Receiver calibration power state definition register */ > > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_CAL | (k << 9)); > > + val &= 0xffbb; > > + cdns_phy_reg_write(cdns_phy, (RX_PSC_CAL | (k << 9)), > val); > > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_A0 | (k << 9)); > > + val &= 0xffbb; > > + cdns_phy_reg_write(cdns_phy, (RX_PSC_A0 | (k << 9)), val); > > + } > > +} > > + > > +static void hdptx_dp_phy_ref_clock_type(struct cdns_hdptx_dp_phy > > +*cdns_phy) { > > + u32 val; > > + > > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > > + val &= ~CMA_REF_CLK_SEL_MASK; > > + /* > > + * single ended reference clock (val |= 0x0030); > > + * differential clock (val |= 0x0000); > > + * > > + * for differential clock on the refclk_p and > > + * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 > > + * cdns_phy_reg_write(cdns_phy, CMN_DIAG_ACYA, 0x0100); > > + */ > > + val |= FIELD_PREP(CMA_REF_CLK_SEL_MASK, 3); > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); } > > + > > +static int wait_for_ack(struct cdns_hdptx_dp_phy *cdns_phy, u32 reg, > > +u32 > > mask, + const char *err_msg) > > +{ > > + u32 val, i; > > + > > + for (i = 0; i < 10; i++) { > > + val = cdns_phy_reg_read(cdns_phy, reg); > > + if (val & mask) > > + return 0; > > + msleep(20); > > + } > > + > > + dev_err(cdns_phy->dev, "%s\n", err_msg); > > + return -1; > > return -ETIMEDOUT? OK. > > > +} > > + > > +static int wait_for_ack_clear(struct cdns_hdptx_dp_phy *cdns_phy, u32 > > +reg, > > u32 mask, + const char *err_msg) > > +{ > > + u32 val, i; > > + > > + for (i = 0; i < 10; i++) { > > + val = cdns_phy_reg_read(cdns_phy, reg); > > + if (!(val & mask)) > > + return 0; > > + msleep(20); > > + } > > + > > + dev_err(cdns_phy->dev, "%s\n", err_msg); > > + return -1; > > return -ETIMEDOUT? OK. > > > +} > > + > > +static int hdptx_dp_phy_power_up(struct cdns_hdptx_dp_phy *cdns_phy) > > +{ > > + u32 val; > > + > > + /* Enable HDP PLL’s for high speed clocks */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > > + val |= PLL_EN; > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > > + if (wait_for_ack(cdns_phy, PHY_HDP_CLK_CTL, PLL_READY, > > + "Wait PLL Ack failed")) > > + return -1; > > + > > + /* Enable HDP PLL’s data rate and full rate clocks out of PMA. */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > > + val |= PLL_CLK_EN; > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > > + if (wait_for_ack(cdns_phy, PHY_HDP_CLK_CTL, PLL_CLK_EN_ACK, > > + "Wait PLL clock enable ACK failed")) > > + return -1; > > + > > + /* Configure PHY in A2 Mode */ > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A2); > > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A2_ACK, > > + "Wait A2 Ack failed")) > > + return -1; > > + > > + /* Configure PHY in A0 mode (PHY must be in the A0 power > > + * state in order to transmit data) > > + */ > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A0); > > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A0_ACK, > > + "Wait A0 Ack failed")) > > + return -1; > > Maybe you should just return the return value of wait_for_ack() in each error > case. OK. > > > + cdns_phy->power_up = true; > > + > > + return 0; > > +} > > + > > +static void hdptx_dp_phy_power_down(struct cdns_hdptx_dp_phy > > +*cdns_phy) { > > + u16 val; > > + > > + if (!cdns_phy->power_up) > > + return; > > + > > + /* Place the PHY lanes in the A3 power state. */ > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A3); > > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A3_ACK, > > + "Wait A3 Ack failed")) > > + return; > > + > > + /* Disable HDP PLL’s data rate and full rate clocks out of PMA. */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > > + val &= ~PLL_CLK_EN; > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > > + if (wait_for_ack_clear(cdns_phy, PHY_HDP_CLK_CTL, > PLL_CLK_EN_ACK, > > + "Wait PLL clock Ack clear failed")) > > + return; > > + > > + /* Disable HDP PLL’s for high speed clocks */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > > + val &= ~PLL_EN; > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > > + if (wait_for_ack_clear(cdns_phy, PHY_HDP_CLK_CTL, PLL_READY, > > + "Wait PLL Ack clear failed")) > > + return; > > I would have expected cdns_phy->power_up = false somewhere in this > function. > I will added it. > > +} > > + > > +static int cdns_hdptx_dp_phy_on(struct phy *phy) { > > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > > + > > + return hdptx_dp_phy_power_up(cdns_phy); } > > + > > +static int cdns_hdptx_dp_phy_off(struct phy *phy) { > > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > > + > > + hdptx_dp_phy_power_down(cdns_phy); > > + > > + return 0; > > +} > > + > > +static int cdns_hdptx_dp_phy_init(struct phy *phy) { > > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > > + int ret; > > + > > + hdptx_dp_phy_ref_clock_type(cdns_phy); > > + > > + /* PHY power up */ > > + ret = hdptx_dp_phy_power_up(cdns_phy); > > + if (ret < 0) > > + return ret; > > + > > + hdptx_dp_aux_cfg(cdns_phy); > > + > > + return ret; > > +} > > + > > +static int cdns_hdptx_dp_configure(struct phy *phy, > > + union phy_configure_opts *opts) { > > + struct cdns_hdptx_dp_phy *cdns_phy = phy_get_drvdata(phy); > > + int ret; > > + > > + cdns_phy->link_rate = opts->dp.link_rate; > > + cdns_phy->num_lanes = opts->dp.lanes; > > + > > + if (cdns_phy->link_rate > MAX_LINK_RATE) { > > + dev_err(cdns_phy->dev, "Link Rate(%d) Not supported\n", > > cdns_phy->link_rate); + return false; > > + } > > + > > + /* Disable phy clock if PHY in power up state */ > > + hdptx_dp_phy_power_down(cdns_phy); > > + > > + if (cdns_phy->ref_clk_rate == REF_CLK_27MHZ) { > > + hdptx_dp_phy_pma_cmn_cfg_27mhz(cdns_phy); > > + hdptx_dp_phy_pma_cmn_pll0_27mhz(cdns_phy); > > + } else { > > + dev_err(cdns_phy->dev, "Not support ref clock rate\n"); > > + } > > + > > + /* PHY power up */ > > + ret = hdptx_dp_phy_power_up(cdns_phy); > > + > > + return ret; > > +} > > + > > +static const struct phy_ops cdns_hdptx_dp_phy_ops = { > > + .init = cdns_hdptx_dp_phy_init, > > + .configure = cdns_hdptx_dp_configure, > > + .power_on = cdns_hdptx_dp_phy_on, > > + .power_off = cdns_hdptx_dp_phy_off, > > + .owner = THIS_MODULE, > > +}; > > + > > +static int cdns_hdptx_dp_phy_probe(struct platform_device *pdev) { > > + struct cdns_hdptx_dp_phy *cdns_phy; > > + struct device *dev = &pdev->dev; > > + struct device_node *node = dev->of_node; > > + struct phy_provider *phy_provider; > > + struct resource *res; > > + struct phy *phy; > > + int ret; > > + > > + cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL); > > + if (!cdns_phy) > > + return -ENOMEM; > > + > > + dev_set_drvdata(dev, cdns_phy); > > + cdns_phy->dev = dev; > > + mutex_init(&cdns_phy->mbox_mutex); > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (!res) > > + return -ENODEV; > > + cdns_phy->regs = devm_ioremap(dev, res->start, resource_size(res)); > > + if (IS_ERR(cdns_phy->regs)) > > + return PTR_ERR(cdns_phy->regs); > > + > > + phy = devm_phy_create(dev, node, &cdns_hdptx_dp_phy_ops); > > + if (IS_ERR(phy)) > > + return PTR_ERR(phy); > > + > > + phy->attrs.mode = PHY_MODE_DP; > > + cdns_phy->phy = phy; > > + phy_set_drvdata(phy, cdns_phy); > > + > > + /* init base struct for access mhdp mailbox */ > > + cdns_phy->base.dev = cdns_phy->dev; > > + cdns_phy->base.regs = cdns_phy->regs; > > + cdns_phy->base.mbox_mutex = &cdns_phy->mbox_mutex; > > How is this mutex supposed to work? From the name > cdns_phy->base.mbox_mutex is supposed to protect the mailbox access in the > cdns-mhdp base, right? > But this mutex is different, initialized separately and thus is independent from > mhdp->mbox_mutex in cdns-mhdp8501-core.c. Yes, this mutex use to protect mailbox access in PHY driver only. In cdns-mhdp8501-core.c driver, every access PHY API functions are protected by core driver's mbox_mutex. Best regards, Sandor > > Best regards, > Alexander > > > + > > + ret = hdptx_dp_clk_enable(cdns_phy); > > + if (ret) { > > + dev_err(dev, "Init clk fail\n"); > > + return -EINVAL; > > + } > > + > > + phy_provider = devm_of_phy_provider_register(dev, > of_phy_simple_xlate); > > + if (IS_ERR(phy_provider)) { > > + ret = PTR_ERR(phy_provider); > > + goto clk_disable; > > + } > > + > > + return 0; > > + > > +clk_disable: > > + hdptx_dp_clk_disable(cdns_phy); > > + > > + return -EINVAL; > > +} > > + > > +static int cdns_hdptx_dp_phy_remove(struct platform_device *pdev) { > > + struct cdns_hdptx_dp_phy *cdns_phy = platform_get_drvdata(pdev); > > + > > + hdptx_dp_clk_disable(cdns_phy); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id cdns_hdptx_dp_phy_of_match[] = { > > + {.compatible = "fsl,imx8mq-dp-phy" }, > > + { /* sentinel */ } > > +}; > > +MODULE_DEVICE_TABLE(of, cdns_hdptx_dp_phy_of_match); > > + > > +static struct platform_driver cdns_hdptx_dp_phy_driver = { > > + .probe = cdns_hdptx_dp_phy_probe, > > + .remove = cdns_hdptx_dp_phy_remove, > > + .driver = { > > + .name = "cdns-hdptx-dp-phy", > > + .of_match_table = cdns_hdptx_dp_phy_of_match, > > + } > > +}; > > +module_platform_driver(cdns_hdptx_dp_phy_driver); > > + > > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > > +MODULE_DESCRIPTION("Cadence HDP-TX DisplayPort PHY driver"); > > +MODULE_LICENSE("GPL"); > > > -- > TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany > Amtsgericht München, HRB 105018 > Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider > http://www.tq-/ > group.com%2F&data=05%7C01%7CSandor.yu%40nxp.com%7C8c4206b83ee9 > 49f73f2108dbcf1072ea%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0 > %7C638331441796833531%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLj > AwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7 > C%7C&sdata=qsc2K6yrBVT1BbBxT2RyydD%2BrmGKCTJO%2FqgAOrUMUME%3 > D&reserved=0 >
Hi Alexander, Thanks for your comments, > -----Original Message----- > From: Alexander Stein <alexander.stein@ew.tq-group.com> > Sent: 2023年10月17日 21:17 > To: dmitry.baryshkov@linaro.org; andrzej.hajda@intel.com; > neil.armstrong@linaro.org; Laurent.pinchart@ideasonboard.com; > jonas@kwiboo.se; jernej.skrabec@gmail.com; airlied@gmail.com; > daniel@ffwll.ch; robh+dt@kernel.org; krzysztof.kozlowski+dt@linaro.org; > shawnguo@kernel.org; s.hauer@pengutronix.de; festevam@gmail.com; > vkoul@kernel.org; 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; Sandor Yu > <sandor.yu@nxp.com> > Cc: kernel@pengutronix.de; dl-linux-imx <linux-imx@nxp.com>; Sandor Yu > <sandor.yu@nxp.com>; Oliver Brown <oliver.brown@nxp.com>; > sam@ravnborg.org > Subject: [EXT] Re: [PATCH v11 7/7] phy: freescale: Add HDMI PHY driver for > i.MX8MQ > > Caution: This is an external email. Please take care when clicking links or > opening attachments. When in doubt, report the message using the 'Report > this email' button > > > Hi Sandor, > > thanks for the patch. > > Am Dienstag, 17. Oktober 2023, 09:04:03 CEST schrieb Sandor Yu: > > Add Cadence HDP-TX HDMI PHY driver for i.MX8MQ. > > > > Cadence HDP-TX PHY could be put in either DP mode or HDMI mode base > on > > the configuration chosen. > > HDMI PHY mode is configurated in the driver. > > > > Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> > > Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> > > --- > > v9->v11: > > *No change. > > > > drivers/phy/freescale/Kconfig | 10 + > > drivers/phy/freescale/Makefile | 1 + > > drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c | 961 > > ++++++++++++++++++++ > > 3 files changed, 972 insertions(+) > > create mode 100644 drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c > > > > diff --git a/drivers/phy/freescale/Kconfig > > b/drivers/phy/freescale/Kconfig index c39709fd700ac..14f47b7cc77ab > > 100644 > > --- a/drivers/phy/freescale/Kconfig > > +++ b/drivers/phy/freescale/Kconfig > > @@ -45,6 +45,16 @@ config PHY_FSL_IMX8MQ_DP > > Enable this to support the Cadence HDPTX DP PHY driver > > on i.MX8MQ SOC. > > > > +config PHY_FSL_IMX8MQ_HDMI > > + tristate "Freescale i.MX8MQ HDMI PHY support" > > + depends on OF && HAS_IOMEM > > + depends on COMMON_CLK > > + select GENERIC_PHY > > + select CDNS_MHDP_HELPER > > + help > > + Enable this to support the Cadence HDPTX HDMI PHY driver > > + on i.MX8MQ SOC. > > + > > endif > > > > config PHY_FSL_LYNX_28G > > diff --git a/drivers/phy/freescale/Makefile > > b/drivers/phy/freescale/Makefile index 47e5285209fa8..1380ac31c2ead > > 100644 > > --- a/drivers/phy/freescale/Makefile > > +++ b/drivers/phy/freescale/Makefile > > @@ -1,5 +1,6 @@ > > # SPDX-License-Identifier: GPL-2.0-only > > obj-$(CONFIG_PHY_FSL_IMX8MQ_DP) += > phy-fsl-imx8mq-dp.o > > +obj-$(CONFIG_PHY_FSL_IMX8MQ_HDMI) += phy-fsl-imx8mq-hdmi.o > > obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o > > obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += > phy-fsl-imx8qm-lvds-phy.o > > obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o > > diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c > > b/drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c new file mode 100644 > > index 0000000000000..9722b5e1803c7 > > --- /dev/null > > +++ b/drivers/phy/freescale/phy-fsl-imx8mq-hdmi.c > > @@ -0,0 +1,961 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* > > + * Cadence High-Definition Multimedia Interface (HDMI) PHY driver > > + * > > + * Copyright (C) 2022,2023 NXP Semiconductor, Inc. > > + */ > > +#include <asm/unaligned.h> > > +#include <drm/bridge/cdns-mhdp-helper.h> #include <linux/clk.h> > > +#include <linux/kernel.h> #include <linux/phy/phy.h> #include > > +<linux/platform_device.h> #include <linux/io.h> > > + > > +#define ADDR_PHY_AFE 0x80000 > > + > > +/* PHY registers */ > > +#define CMN_SSM_BIAS_TMR 0x0022 > > +#define CMN_PLLSM0_USER_DEF_CTRL 0x002f > > +#define CMN_PSM_CLK_CTRL 0x0061 > > +#define CMN_CDIAG_REFCLK_CTRL 0x0062 > > +#define CMN_PLL0_VCOCAL_START 0x0081 > > +#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084 > > +#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085 > > +#define CMN_TXPUCAL_CTRL 0x00e0 > > +#define CMN_TXPDCAL_CTRL 0x00f0 > > +#define CMN_TXPU_ADJ_CTRL 0x0108 > > +#define CMN_TXPD_ADJ_CTRL 0x010c > > +#define CMN_DIAG_PLL0_FBH_OVRD 0x01c0 > > +#define CMN_DIAG_PLL0_FBL_OVRD 0x01c1 > > +#define CMN_DIAG_PLL0_OVRD 0x01c2 > > +#define CMN_DIAG_PLL0_TEST_MODE 0x01c4 > > +#define CMN_DIAG_PLL0_V2I_TUNE 0x01c5 > > +#define CMN_DIAG_PLL0_CP_TUNE 0x01c6 > > +#define CMN_DIAG_PLL0_LF_PROG 0x01c7 > > +#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01c8 > > +#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01c9 > > +#define CMN_DIAG_PLL0_INCLK_CTRL 0x01ca > > +#define CMN_DIAG_PLL0_PXL_DIVH 0x01cb > > +#define CMN_DIAG_PLL0_PXL_DIVL 0x01cc > > +#define CMN_DIAG_HSCLK_SEL 0x01e0 > > +#define XCVR_PSM_RCTRL 0x4001 > > +#define TX_TXCC_CAL_SCLR_MULT_0 0x4047 > > +#define TX_TXCC_CPOST_MULT_00_0 0x404c > > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > > +#define XCVR_DIAG_PLLDRC_CTRL 0x40e0 > > +#define XCVR_DIAG_HSCLK_SEL 0x40e1 > > +#define XCVR_DIAG_BIDI_CTRL 0x40e8 > > +#define TX_PSC_A0 0x4100 > > +#define TX_PSC_A1 0x4101 > > +#define TX_PSC_A2 0x4102 > > +#define TX_PSC_A3 0x4103 > > +#define TX_DIAG_TX_CTRL 0x41e0 > > +#define TX_DIAG_TX_DRV 0x41e1 > > +#define TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 > > +#define TX_DIAG_ACYA_0 0x41ff > > +#define TX_DIAG_ACYA_1 0x43ff > > +#define TX_DIAG_ACYA_2 0x45ff > > +#define TX_DIAG_ACYA_3 0x47ff > > +#define TX_ANA_CTRL_REG_1 0x5020 > > +#define TX_ANA_CTRL_REG_2 0x5021 > > +#define TX_DIG_CTRL_REG_2 0x5024 > > +#define TXDA_CYA_AUXDA_CYA 0x5025 > > +#define TX_ANA_CTRL_REG_3 0x5026 > > +#define TX_ANA_CTRL_REG_4 0x5027 > > +#define TX_ANA_CTRL_REG_5 0x5029 > > +#define RX_PSC_A0 0x8000 > > +#define RX_PSC_CAL 0x8006 > > +#define PHY_HDP_MODE_CTRL 0xc008 > > +#define PHY_HDP_CLK_CTL 0xc009 > > +#define PHY_ISO_CMN_CTRL 0xc010 > > +#define PHY_PMA_CMN_CTRL1 0xc800 > > +#define PHY_PMA_ISO_CMN_CTRL 0xc810 > > +#define PHY_PMA_ISO_PLL_CTRL1 0xc812 > > +#define PHY_PMA_ISOLATION_CTRL 0xc81f > > + > > +/* PHY_HDP_CLK_CTL */ > > +#define PLL_DATA_RATE_CLK_DIV_MASK GENMASK(15, 8) > > +#define PLL_DATA_RATE_CLK_DIV_HBR 0x24 > > +#define PLL_DATA_RATE_CLK_DIV_HBR2 0x12 > > +#define PLL_CLK_EN_ACK_EN BIT(3) > > +#define PLL_CLK_EN BIT(2) > > +#define PLL_READY BIT(1) > > +#define PLL_EN BIT(0) > > + > > +/* PHY_PMA_CMN_CTRL1 */ > > +#define CMA_REF_CLK_DIG_DIV_MASK GENMASK(13, 12) > > +#define CMA_REF_CLK_SEL_MASK GENMASK(6, 4) > > +#define CMA_REF_CLK_RCV_EN_MASK BIT(3) > > +#define CMA_REF_CLK_RCV_EN 1 > > +#define CMN_READY BIT(0) > > + > > +/* PHY_PMA_ISO_PLL_CTRL1 */ > > +#define CMN_PLL0_CLK_DATART_DIV_MASK GENMASK(7, 0) > > + > > +/* TX_DIAG_TX_DRV */ > > +#define TX_DRIVER_PROG_BOOST_ENABLE BIT(10) > > +#define TX_DRIVER_PROG_BOOST_LEVEL_MASK > GENMASK(9, 8) > > +#define TX_DRIVER_LDO_BG_DEPENDENT_REF_ENABLE BIT(7) > > +#define TX_DRIVER_LDO_BANDGAP_REF_ENABLE BIT(6) > > + > > +/* TX_TXCC_CAL_SCLR_MULT_0 */ > > +#define SCALED_RESISTOR_CALIBRATION_CODE_ADD BIT(8) > > +#define RESISTOR_CAL_MULT_VAL_32_128 BIT(5) > > + > > +/* CMN_CDIAG_REFCLK_CTRL */ > > +#define DIG_REF_CLK_DIV_SCALER_MASK GENMASK(14, 12) > > +#define REFCLK_TERMINATION_EN_OVERRIDE_EN BIT(7) > > +#define REFCLK_TERMINATION_EN_OVERRIDE BIT(6) > > + > > +/* CMN_DIAG_HSCLK_SEL */ > > +#define HSCLK1_SEL_MASK > GENMASK(5, 4) > > +#define HSCLK0_SEL_MASK > GENMASK(1, 0) > > + > > +/* XCVR_DIAG_HSCLK_SEL */ > > +#define HSCLK_SEL_MODE3_MASK GENMASK(13, 12) > > +#define HSCLK_SEL_MODE3_HSCLK1 1 > > + > > +/* CMN_PLL0_VCOCAL_START */ > > +#define VCO_CALIB_CODE_START_POINT_VAL_MASK GENMASK(8, 0) > > + > > +/* CMN_DIAG_PLL0_FBH_OVRD */ > > +#define PLL_FEEDBACK_DIV_HI_OVERRIDE_EN BIT(15) > > + > > +/* CMN_DIAG_PLL0_FBL_OVRD */ > > +#define PLL_FEEDBACK_DIV_LO_OVERRIDE_EN BIT(15) > > + > > +/* CMN_DIAG_PLL0_PXL_DIVH */ > > +#define PLL_PCLK_DIV_EN BIT(15) > > + > > +/* XCVR_DIAG_PLLDRC_CTRL */ > > +#define DPLL_CLK_SEL_MODE3 BIT(14) > > + > > +/* TX_DIAG_TX_CTRL */ > > +#define TX_IF_SUBRATE_MODE3_MASK GENMASK(7, 6) > > + > > +/* PHY_HDP_MODE_CTRL */ > > +#define POWER_STATE_A3_ACK BIT(7) > > +#define POWER_STATE_A2_ACK BIT(6) > > +#define POWER_STATE_A1_ACK BIT(5) > > +#define POWER_STATE_A0_ACK BIT(4) > > +#define POWER_STATE_A3 BIT(3) > > +#define POWER_STATE_A2 BIT(2) > > +#define POWER_STATE_A1 BIT(1) > > +#define POWER_STATE_A0 BIT(0) > > + > > +/* PHY_PMA_ISO_CMN_CTRL */ > > +#define CMN_MACRO_PWR_EN_ACK BIT(5) > > + > > +#define KEEP_ALIVE 0x18 > > + > > +#define REF_CLK_27MHZ 27000000 > > + > > +/* HDMI TX clock control settings */ > > +struct hdptx_hdmi_ctrl { > > + u32 pixel_clk_freq_min; > > + u32 pixel_clk_freq_max; > > + u32 feedback_factor; > > + u32 data_range_kbps_min; > > + u32 data_range_kbps_max; > > + u32 cmnda_pll0_ip_div; > > + u32 cmn_ref_clk_dig_div; > > + u32 ref_clk_divider_scaler; > > + u32 pll_fb_div_total; > > + u32 cmnda_pll0_fb_div_low; > > + u32 cmnda_pll0_fb_div_high; > > + u32 pixel_div_total; > > + u32 cmnda_pll0_pxdiv_low; > > + u32 cmnda_pll0_pxdiv_high; > > + u32 vco_freq_min; > > + u32 vco_freq_max; > > + u32 vco_ring_select; > > + u32 cmnda_hs_clk_0_sel; > > + u32 cmnda_hs_clk_1_sel; > > + u32 hsclk_div_at_xcvr; > > + u32 hsclk_div_tx_sub_rate; > > + u32 cmnda_pll0_hs_sym_div_sel; > > + u32 cmnda_pll0_clk_freq_min; > > + u32 cmnda_pll0_clk_freq_max; > > +}; > > + > > +struct cdns_hdptx_hdmi_phy { > > + struct cdns_mhdp_base base; > > + > > + void __iomem *regs; /* DPTX registers base */ > > + struct mutex mbox_mutex; /* mutex to protect mailbox */ > > + struct device *dev; > > + struct phy *phy; > > + struct clk *ref_clk, *apb_clk; > > + u32 ref_clk_rate; > > + u32 pixel_clk_rate; > > + enum hdmi_colorspace color_space; > > + u32 bpc; > > +}; > > + > > +/* HDMI TX clock control settings, pixel clock is output */ static > > +const struct hdptx_hdmi_ctrl pixel_clk_output_ctrl_table[] = { > > +/*Minclk Maxclk Fdbak DR_min DR_max ip_d dig DS Totl > */ > > +{ 27000, 27000, 1000, 270000, 270000, 0x03, 0x1, 0x1, 240, 0x0bc, > > 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x3, > > 27000, 27000}, +{ 27000, 27000, 1250, 337500, 337500, 0x03, 0x1, > > 0x1, 300, 0x0ec, 0x03c, 100, 0x030, 0x030, 2700000, 2700000, 0, 2, 2, > > 2, 4, 0x3, 33750, 33750}, +{ 27000, 27000, 1500, 405000, 405000, > > 0x03, 0x1, 0x1, 360, 0x11c, 0x048, 120, 0x03a, 0x03a, 3240000, > > 3240000, 0, 2, 2, 2, 4, 0x3, 40500, 40500}, +{ 27000, 27000, 2000, > > 540000, 540000, 0x03, 0x1, 0x1, 240, 0x0bc, 0x030, 80, 0x026, 0x026, > > 2160000, 2160000, 0, 2, 2, 2, 4, 0x2, 54000, 54000}, +{ 54000, > > 54000, 1000, 540000, 540000, 0x03, 0x1, 0x1, 480, 0x17c, 0x060, 80, > > 0x026, 0x026, 4320000, 4320000, 1, 2, 2, 2, 4, 0x3, 54000, 54000}, > > +{ 54000, 54000, 1250, 675000, 675000, 0x04, 0x1, 0x1, 400, 0x13c, > > 0x050, 50, 0x017, 0x017, 2700000, 2700000, 0, 1, 1, 2, 4, 0x2, > > 67500, 67500}, +{ 54000, 54000, 1500, 810000, 810000, 0x04, 0x1, > > 0x1, 480, 0x17c, 0x060, 60, 0x01c, 0x01c, 3240000, 3240000, 0, 2, 2, > > 2, 2, 0x2, 81000, 81000}, +{ 54000, 54000, 2000, 1080000, 1080000, > > 0x03, 0x1, 0x1, 240, 0x0bc, 0x030, 40, 0x012, 0x012, 2160000, > > 2160000, 0, 2, 2, 2, 1, 0x1, 108000, 108000}, +{ 74250, 74250, 1000, > > 742500, 742500, 0x03, 0x1, 0x1, 660, 0x20c, 0x084, 80, 0x026, 0x026, > > 5940000, 5940000, 1, 2, 2, 2, 4, 0x3, 74250, 74250}, +{ 74250, > > 74250, 1250, 928125, 928125, 0x04, 0x1, 0x1, 550, 0x1b4, 0x06e, 50, > > 0x017, 0x017, 3712500, 3712500, 1, 1, 1, 2, 4, 0x2, 92812, 92812}, > > +{ 74250, 74250, 1500, 1113750, 1113750, 0x04, 0x1, 0x1, 660, 0x20c, > > 0x084, 60, 0x01c, 0x01c, 4455000, 4455000, 1, 2, 2, 2, 2, 0x2, > > 111375, 111375}, +{ 74250, 74250, 2000, 1485000, 1485000, 0x03, 0x1, > > 0x1, 330, 0x104, 0x042, 40, 0x012, 0x012, 2970000, 2970000, 0, 2, 2, > > 2, 1, 0x1, 148500, 148500}, +{ 99000, 99000, 1000, 990000, 990000, > > 0x03, 0x1, 0x1, 440, 0x15c, 0x058, 40, 0x012, 0x012, 3960000, > > 3960000, 1, 2, 2, 2, 2, 0x2, 99000, 99000}, +{ 99000, 99000, 1250, > > 1237500, 1237500, 0x03, 0x1, 0x1, 275, 0x0d8, 0x037, 25, 0x00b, > > 0x00a, 2475000, 2475000, 0, 1, 1, 2, 2, 0x1, 123750, 123750}, +{ > > 99000, 99000, 1500, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, > > 0x042, 30, 0x00d, 0x00d, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, > > 148500, 148500}, +{ 99000, 99000, 2000, 1980000, 1980000, 0x03, 0x1, > > 0x1, 440, 0x15c, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, > > 2, 1, 0x1, 198000, 198000}, +{148500, 148500, 1000, 1485000, 1485000, > > 0x03, 0x1, 0x1, 660, 0x20c, 0x084, 40, 0x012, 0x012, 5940000, > > 5940000, 1, 2, 2, 2, 2, 0x2, 148500, 148500}, +{148500, 148500, 1250, > > 1856250, 1856250, 0x04, 0x1, 0x1, 550, 0x1b4, 0x06e, 25, 0x00b, > > 0x00a, 3712500, 3712500, 1, 1, 1, 2, 2, 0x1, 185625, 185625}, > > +{148500, 148500, 1500, 2227500, 2227500, 0x03, 0x1, 0x1, 495, 0x188, > > 0x063, 30, 0x00d, 0x00d, 4455000, 4455000, 1, 1, 1, 2, 2, 0x1, > > 222750, 222750}, +{148500, 148500, 2000, 2970000, 2970000, 0x03, 0x1, > > 0x1, 660, 0x20c, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, > > 2, 1, 0x1, 297000, 297000}, +{198000, 198000, 1000, 1980000, 1980000, > > 0x03, 0x1, 0x1, 220, 0x0ac, 0x02c, 10, 0x003, 0x003, 1980000, > > 1980000, 0, 1, 1, 2, 1, 0x0, 198000, 198000}, +{198000, 198000, 1250, > > 2475000, 2475000, 0x03, 0x1, 0x1, 550, 0x1b4, 0x06e, 25, 0x00b, > > 0x00a, 4950000, 4950000, 1, 1, 1, 2, 2, 0x1, 247500, 247500}, > > +{198000, 198000, 1500, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, > > 0x042, 15, 0x006, 0x005, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, > > 297000, 297000}, +{198000, 198000, 2000, 3960000, 3960000, 0x03, 0x1, > > 0x1, 440, 0x15c, 0x058, 20, 0x008, 0x008, 3960000, 3960000, 1, 1, 1, > > 2, 1, 0x0, 396000, 396000}, +{297000, 297000, 1000, 2970000, 2970000, > > 0x03, 0x1, 0x1, 330, 0x104, 0x042, 10, 0x003, 0x003, 2970000, > > 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000}, +{297000, 297000, 1500, > > 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 15, 0x006, > > 0x005, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500}, > > +{297000, 297000, 2000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20c, > > 0x084, 20, 0x008, 0x008, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, > > 594000, 594000}, +{594000, 594000, 1000, 5940000, 5940000, 0x03, 0x1, > > 0x1, 660, 0x20c, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, > > 2, 1, 0x0, 594000, 594000}, +{594000, 594000, 750, 4455000, 4455000, > > 0x03, 0x1, 0x1, 495, 0x188, 0x063, 10, 0x003, 0x003, 4455000, > > 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500}, +{594000, 594000, 625, > > 3712500, 3712500, 0x04, 0x1, 0x1, 550, 0x1b4, 0x06e, 10, 0x003, > > 0x003, 3712500, 3712500, 1, 1, 1, 2, 1, 0x0, 371250, 371250}, > > +{594000, 594000, 500, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20c, > > 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 2, 0x1, > > 297000, 297000}, +}; > > + > > +/* HDMI TX PLL tuning settings */ > > +struct hdptx_hdmi_pll_tuning { > > + u32 vco_freq_bin; > > + u32 vco_freq_min; > > + u32 vco_freq_max; > > + u32 volt_to_current_coarse; > > + u32 volt_to_current; > > + u32 ndac_ctrl; > > + u32 pmos_ctrl; > > + u32 ptat_ndac_ctrl; > > + u32 feedback_div_total; > > + u32 charge_pump_gain; > > + u32 coarse_code; > > + u32 v2i_code; > > + u32 vco_cal_code; > > +}; > > + > > +/* HDMI TX PLL tuning settings, pixel clock is output */ static const > > +struct hdptx_hdmi_pll_tuning pixel_clk_output_pll_table[] = { /*bin > > +VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain Coa V2I > CAL > > */ +{ 1, 1980000, 1980000, 0x4, 0x3, 0x0, 0x09, 0x09, 220, 0x42, 160, > > 5, > > 183 }, +{ 2, 2160000, 2160000, 0x4, 0x3, 0x0, 0x09, 0x09, 240, 0x42, > > 166, 6, 208 }, +{ 3, 2475000, 2475000, 0x5, 0x3, 0x1, 0x00, 0x07, > > 275, 0x42, 167, 6, 209 }, +{ 4, 2700000, 2700000, 0x5, 0x3, 0x1, > > 0x00, 0x07, 300, 0x42, 188, 6, 230 }, +{ 4, 2700000, 2700000, 0x5, > > 0x3, 0x1, 0x00, 0x07, 400, 0x4c, 188, 6, 230 }, +{ 5, 2970000, > > 2970000, 0x6, 0x3, 0x1, 0x00, 0x07, 330, 0x42, 183, 6, 225 }, +{ 6, > > 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 360, 0x42, 203, 7, 256 }, > > +{ 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 480, 0x4c, 203, 7, > > 256 }, +{ 7, 3712500, 3712500, 0x4, 0x3, 0x0, 0x07, 0x0F, 550, 0x4c, > > 212, 7, 257 }, +{ 8, 3960000, 3960000, 0x5, 0x3, 0x0, 0x07, 0x0F, > > 440, 0x42, 184, 6, 226 }, +{ 9, 4320000, 4320000, 0x5, 0x3, 0x1, > > 0x07, 0x0F, 480, 0x42, 205, 7, 258 }, +{ 10, 4455000, 4455000, 0x5, > > 0x3, 0x0, 0x07, 0x0F, 495, 0x42, 219, 7, 272 }, +{ 10, 4455000, > > 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 660, 0x4c, 219, 7, 272 }, > > +{ 11, 4950000, 4950000, 0x6, 0x3, 0x1, 0x00, 0x07, 550, 0x42, 213, 7, > > +258 > > }, +{ 12, 5940000, 5940000, 0x7, 0x3, 0x1, 0x00, 0x07, 660, 0x42, 244, > > 8, > > 292 }, +}; > > + > > +static int cdns_phy_reg_write(struct cdns_hdptx_hdmi_phy *cdns_phy, > > +u32 > > addr, u32 val) +{ > > + return cdns_mhdp_reg_write(&cdns_phy->base, ADDR_PHY_AFE + > (addr > > + << > 2), > > val); +} > > + > > +static u32 cdns_phy_reg_read(struct cdns_hdptx_hdmi_phy *cdns_phy, > > +u32 > > addr) +{ > > + u32 reg32; > > + > > + cdns_mhdp_reg_read(&cdns_phy->base, ADDR_PHY_AFE + (addr << > 2), > ®32); > > + > > + return reg32; > > +} > > + > > +static int wait_for_ack(struct cdns_hdptx_hdmi_phy *cdns_phy, u32 > > +reg, u32 > > mask, + const char *err_msg) > > +{ > > + u32 val, i; > > + > > + for (i = 0; i < 10; i++) { > > + val = cdns_phy_reg_read(cdns_phy, reg); > > + if (val & mask) > > + return 0; > > + msleep(20); > > + } > > + > > + dev_err(cdns_phy->dev, "%s\n", err_msg); > > + return -1; > > return -ETIMEDOUT? OK. > > > +} > > + > > +static bool hdptx_phy_check_alive(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + u32 alive, newalive; > > + u8 retries_left = 50; > > + > > + alive = readl(cdns_phy->regs + KEEP_ALIVE); > > + > > + while (retries_left--) { > > + udelay(2); > > + > > + newalive = readl(cdns_phy->regs + KEEP_ALIVE); > > + if (alive == newalive) > > + continue; > > + return true; > > + } > > + return false; > > +} > > + > > +static int hdptx_hdmi_clk_enable(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + struct device *dev = cdns_phy->dev; > > + u32 ref_clk_rate; > > + int ret; > > + > > + cdns_phy->ref_clk = devm_clk_get(dev, "ref"); > > + if (IS_ERR(cdns_phy->ref_clk)) { > > + dev_err(dev, "phy ref clock not found\n"); > > + return PTR_ERR(cdns_phy->ref_clk); > > + } > > + > > + cdns_phy->apb_clk = devm_clk_get(dev, "apb"); > > + if (IS_ERR(cdns_phy->apb_clk)) { > > + dev_err(dev, "phy apb clock not found\n"); > > + return PTR_ERR(cdns_phy->apb_clk); > > + } > > + > > + ret = clk_prepare_enable(cdns_phy->ref_clk); > > + if (ret) { > > + dev_err(cdns_phy->dev, "Failed to prepare ref clock\n"); > > + return ret; > > + } > > + > > + ref_clk_rate = clk_get_rate(cdns_phy->ref_clk); > > + if (!ref_clk_rate) { > > + dev_err(cdns_phy->dev, "Failed to get ref clock rate\n"); > > + goto err_ref_clk; > > + } > > + > > + if (ref_clk_rate == REF_CLK_27MHZ) { > > + cdns_phy->ref_clk_rate = ref_clk_rate; > > + } else { > > + dev_err(cdns_phy->dev, "Not support Ref Clock > Rate(%dHz) > \n", > > ref_clk_rate); + goto err_ref_clk; > > + } > > + > > + ret = clk_prepare_enable(cdns_phy->apb_clk); > > + if (ret) { > > + dev_err(cdns_phy->dev, "Failed to prepare apb clock\n"); > > + goto err_ref_clk; > > + } > > + > > + return 0; > > + > > +err_ref_clk: > > + clk_disable_unprepare(cdns_phy->ref_clk); > > + return -EINVAL; > > +} > > + > > +static void hdptx_hdmi_clk_disable(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + clk_disable_unprepare(cdns_phy->ref_clk); > > + clk_disable_unprepare(cdns_phy->apb_clk); > > Shouldn't the clocks be disabled in reverse order to enabling path? OK, I will change it. > > > +} > > + > > +static void hdptx_hdmi_arc_config(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + u16 txpu_calib_code; > > + u16 txpd_calib_code; > > + u16 txpu_adj_calib_code; > > + u16 txpd_adj_calib_code; > > + u16 prev_calib_code; > > + u16 new_calib_code; > > + u16 rdata; > > + > > + /* Power ARC */ > > + cdns_phy_reg_write(cdns_phy, TXDA_CYA_AUXDA_CYA, 0x0001); > > + > > + prev_calib_code = cdns_phy_reg_read(cdns_phy, > TX_DIG_CTRL_REG_2); > > + txpu_calib_code = cdns_phy_reg_read(cdns_phy, > CMN_TXPUCAL_CTRL); > > + txpd_calib_code = cdns_phy_reg_read(cdns_phy, > CMN_TXPDCAL_CTRL); > > + txpu_adj_calib_code = cdns_phy_reg_read(cdns_phy, > CMN_TXPU_ADJ_CTRL); > > + txpd_adj_calib_code = cdns_phy_reg_read(cdns_phy, > CMN_TXPD_ADJ_CTRL); > > + > > + new_calib_code = ((txpu_calib_code + txpd_calib_code) / 2) > > + + txpu_adj_calib_code + txpd_adj_calib_code; > > + > > + if (new_calib_code != prev_calib_code) { > > + rdata = cdns_phy_reg_read(cdns_phy, > TX_ANA_CTRL_REG_1); > > + rdata &= 0xdfff; > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, > rdata); > > + cdns_phy_reg_write(cdns_phy, TX_DIG_CTRL_REG_2, > new_calib_code); > > + mdelay(10); > > + rdata |= 0x2000; > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, > rdata); > > + usleep_range(150, 250); > > + } > > + > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0100); > > + usleep_range(100, 200); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x0300); > > + usleep_range(100, 200); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_3, 0x0000); > > + usleep_range(100, 200); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2008); > > + usleep_range(100, 200); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2018); > > + usleep_range(100, 200); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2098); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030c); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_5, 0x0010); > > + usleep_range(100, 200); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_4, 0x4001); > > + mdelay(5); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_1, 0x2198); > > + mdelay(5); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030d); > > + usleep_range(100, 200); > > + cdns_phy_reg_write(cdns_phy, TX_ANA_CTRL_REG_2, 0x030f); } > > + > > +static void hdptx_hdmi_phy_set_vswing(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + u32 k; > > + const u32 num_lanes = 4; > > + > > + for (k = 0; k < num_lanes; k++) { > > + cdns_phy_reg_write(cdns_phy, (TX_DIAG_TX_DRV | (k << > 9)), > > + > TX_DRIVER_PROG_BOOST_ENABLE | > > + > FIELD_PREP(TX_DRIVER_PROG_BOOST_LEVEL_MASK, 3) | > > + > TX_DRIVER_LDO_BG_DEPENDENT_REF_ENABLE | > > + > TX_DRIVER_LDO_BANDGAP_REF_ENABLE); > > + cdns_phy_reg_write(cdns_phy, > (TX_TXCC_CPOST_MULT_00_0 | > > + (k > << 9)), 0x0); > > + cdns_phy_reg_write(cdns_phy, > (TX_TXCC_CAL_SCLR_MULT_0 | > > + (k > << 9)), > > + > SCALED_RESISTOR_CALIBRATION_CODE_ADD | > > + > RESISTOR_CAL_MULT_VAL_32_128); > > + } > > +} > > + > > +static int hdptx_hdmi_feedback_factor(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + u32 feedback_factor; > > + > > + switch (cdns_phy->color_space) { > > + case HDMI_COLORSPACE_YUV422: > > + feedback_factor = 1000; > > + break; > > + > > + case HDMI_COLORSPACE_YUV420: > > + switch (cdns_phy->bpc) { > > + case 8: > > + feedback_factor = 500; > > + break; > > + case 10: > > + feedback_factor = 625; > > + break; > > + case 12: > > + feedback_factor = 750; > > + break; > > + case 16: > > + feedback_factor = 1000; > > + break; > > + default: > > + dev_dbg(cdns_phy->dev, "Invalid > ColorDepth\n"); > > + return 0; > > + } > > + break; > > + > > + default: > > + /* Assume RGB/YUV444 */ > > + switch (cdns_phy->bpc) { > > + case 10: > > + feedback_factor = 1250; > > + break; > > + case 12: > > + feedback_factor = 1500; > > + break; > > + case 16: > > + feedback_factor = 2000; > > + break; > > + default: > > + feedback_factor = 1000; > > + } > > + } > > + > > + return feedback_factor; > > +} > > + > > +static int hdptx_hdmi_phy_config(struct cdns_hdptx_hdmi_phy *cdns_phy, > > + const struct hdptx_hdmi_ctrl > *p_ctrl_table, > > + const struct hdptx_hdmi_pll_tuning > *p_pll_table, > > + char pclk_in) > > bool pclk_in OK. > > > +{ > > + const u32 num_lanes = 4; > > + u32 val, k; > > + > > + /* enable PHY isolation mode only for CMN */ > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISOLATION_CTRL, > 0xd000); > > + > > + /* set cmn_pll0_clk_datart1_div/cmn_pll0_clk_datart0_div dividers > */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_ISO_PLL_CTRL1); > > + val &= ~CMN_PLL0_CLK_DATART_DIV_MASK; > > + val |= FIELD_PREP(CMN_PLL0_CLK_DATART_DIV_MASK, 0x12); > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_PLL_CTRL1, val); > > + > > + /* assert PHY reset from isolation register */ > > + cdns_phy_reg_write(cdns_phy, PHY_ISO_CMN_CTRL, 0x0000); > > + /* assert PMA CMN reset */ > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_CMN_CTRL, > 0x0000); > > + > > + /* register XCVR_DIAG_BIDI_CTRL */ > > + for (k = 0; k < num_lanes; k++) > > + cdns_phy_reg_write(cdns_phy, XCVR_DIAG_BIDI_CTRL | > (k << > 9), 0x00ff); > > + > > + /* Describing Task phy_cfg_hdp */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > > + val &= ~CMA_REF_CLK_RCV_EN_MASK; > > + val |= FIELD_PREP(CMA_REF_CLK_RCV_EN_MASK, > CMA_REF_CLK_RCV_EN); > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); > > + > > + /* PHY Registers */ > > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > > + val &= ~CMA_REF_CLK_DIG_DIV_MASK; > > + val |= FIELD_PREP(CMA_REF_CLK_DIG_DIV_MASK, > > p_ctrl_table->cmn_ref_clk_dig_div); + cdns_phy_reg_write(cdns_phy, > > PHY_PMA_CMN_CTRL1, val); > > + > > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_CLK_CTL); > > + val &= ~PLL_DATA_RATE_CLK_DIV_MASK; > > + val |= FIELD_PREP(PLL_DATA_RATE_CLK_DIV_MASK, > > + PLL_DATA_RATE_CLK_DIV_HBR2); > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_CLK_CTL, val); > > + > > + /* Common control module control and diagnostic registers */ > > + val = cdns_phy_reg_read(cdns_phy, CMN_CDIAG_REFCLK_CTRL); > > + val &= ~DIG_REF_CLK_DIV_SCALER_MASK; > > + val |= FIELD_PREP(DIG_REF_CLK_DIV_SCALER_MASK, > > p_ctrl_table->ref_clk_divider_scaler); + val |= > > REFCLK_TERMINATION_EN_OVERRIDE_EN | > REFCLK_TERMINATION_EN_OVERRIDE; > > + cdns_phy_reg_write(cdns_phy, CMN_CDIAG_REFCLK_CTRL, val); > > + > > + /* High speed clock used */ > > + val = cdns_phy_reg_read(cdns_phy, CMN_DIAG_HSCLK_SEL); > > + val &= ~(HSCLK1_SEL_MASK | HSCLK0_SEL_MASK); > > + val |= FIELD_PREP(HSCLK1_SEL_MASK, > > + (p_ctrl_table->cmnda_hs_clk_1_sel > >> > > 1)); + val |= FIELD_PREP(HSCLK0_SEL_MASK, > (p_ctrl_table->cmnda_hs_clk_0_sel > > >> 1)); + cdns_phy_reg_write(cdns_phy, CMN_DIAG_HSCLK_SEL, val); > > + > > + for (k = 0; k < num_lanes; k++) { > > + val = cdns_phy_reg_read(cdns_phy, > (XCVR_DIAG_HSCLK_SEL | > (k << 9))); > > + val &= ~HSCLK_SEL_MODE3_MASK; > > + val |= FIELD_PREP(HSCLK_SEL_MODE3_MASK, > > + > (p_ctrl_table->cmnda_hs_clk_0_sel >> > 1)); > > + cdns_phy_reg_write(cdns_phy, (XCVR_DIAG_HSCLK_SEL | > (k > > + << > 9)), val); > > + } > > + > > + /* PLL 0 control state machine registers */ > > + val = p_ctrl_table->vco_ring_select << 12; > > + cdns_phy_reg_write(cdns_phy, CMN_PLLSM0_USER_DEF_CTRL, > val); > > + > > + if (pclk_in) { > > + val = 0x30a0; > > + } else { > > + val = cdns_phy_reg_read(cdns_phy, > CMN_PLL0_VCOCAL_START); > > + val &= ~VCO_CALIB_CODE_START_POINT_VAL_MASK; > > + val |= > FIELD_PREP(VCO_CALIB_CODE_START_POINT_VAL_MASK, > > + p_pll_table->vco_cal_code); > > + } > > + cdns_phy_reg_write(cdns_phy, CMN_PLL0_VCOCAL_START, val); > > + > > + cdns_phy_reg_write(cdns_phy, CMN_PLL0_VCOCAL_INIT_TMR, > 0x0064); > > + cdns_phy_reg_write(cdns_phy, CMN_PLL0_VCOCAL_ITER_TMR, > 0x000a); > > + > > + /* Common functions control and diagnostics registers */ > > + val = p_ctrl_table->cmnda_pll0_hs_sym_div_sel << 8; > > + val |= p_ctrl_table->cmnda_pll0_ip_div; > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_INCLK_CTRL, val); > > + > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_OVRD, 0x0000); > > + > > + val = p_ctrl_table->cmnda_pll0_fb_div_high; > > + val |= PLL_FEEDBACK_DIV_HI_OVERRIDE_EN; > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_FBH_OVRD, val); > > + > > + val = p_ctrl_table->cmnda_pll0_fb_div_low; > > + val |= PLL_FEEDBACK_DIV_LO_OVERRIDE_EN; > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_FBL_OVRD, val); > > + > > + if (!pclk_in) { > > + val = p_ctrl_table->cmnda_pll0_pxdiv_low; > > + cdns_phy_reg_write(cdns_phy, > CMN_DIAG_PLL0_PXL_DIVL, > > + val); > > + > > + val = p_ctrl_table->cmnda_pll0_pxdiv_high; > > + val |= PLL_PCLK_DIV_EN; > > + cdns_phy_reg_write(cdns_phy, > CMN_DIAG_PLL0_PXL_DIVH, val); > > + } > > + > > + val = p_pll_table->volt_to_current_coarse; > > + val |= (p_pll_table->volt_to_current) << 4; > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_V2I_TUNE, val); > > + > > + val = p_pll_table->charge_pump_gain; > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_CP_TUNE, val); > > + > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_LF_PROG, > 0x0008); > > + > > + val = p_pll_table->pmos_ctrl; > > + val |= (p_pll_table->ndac_ctrl) << 8; > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_PTATIS_TUNE1, > val); > > + > > + val = p_pll_table->ptat_ndac_ctrl; > > + cdns_phy_reg_write(cdns_phy, CMN_DIAG_PLL0_PTATIS_TUNE2, > val); > > + > > + if (pclk_in) > > + cdns_phy_reg_write(cdns_phy, > CMN_DIAG_PLL0_TEST_MODE, > 0x0022); > > + else > > + cdns_phy_reg_write(cdns_phy, > CMN_DIAG_PLL0_TEST_MODE, > 0x0020); > > + > > + cdns_phy_reg_write(cdns_phy, CMN_PSM_CLK_CTRL, 0x0016); > > + > > + /* Transceiver control and diagnostic registers */ > > + for (k = 0; k < num_lanes; k++) { > > + val = cdns_phy_reg_read(cdns_phy, > (XCVR_DIAG_PLLDRC_CTRL > > + | > (k << 9))); > > + val &= ~DPLL_CLK_SEL_MODE3; > > + cdns_phy_reg_write(cdns_phy, > (XCVR_DIAG_PLLDRC_CTRL | (k > << 9)), val); > > + } > > + > > + for (k = 0; k < num_lanes; k++) { > > + val = cdns_phy_reg_read(cdns_phy, (TX_DIAG_TX_CTRL | > (k > > + << > 9))); > > + val &= ~TX_IF_SUBRATE_MODE3_MASK; > > + val |= FIELD_PREP(TX_IF_SUBRATE_MODE3_MASK, > > + > (p_ctrl_table->hsclk_div_tx_sub_rate > >> 1)); > > + cdns_phy_reg_write(cdns_phy, (TX_DIAG_TX_CTRL | (k << > > + 9)), > val); > > + } > > + > > + val = cdns_phy_reg_read(cdns_phy, PHY_PMA_CMN_CTRL1); > > + val &= ~CMA_REF_CLK_SEL_MASK; > > + /* > > + * single ended reference clock (val |= 0x0030); > > + * differential clock (val |= 0x0000); > > + * for differential clock on the refclk_p and > > + * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 > > + * cdns_phy_reg_write(cdns_phy, CMN_DIAG_ACYA, 0x0100); > > + */ > > + val |= FIELD_PREP(CMA_REF_CLK_SEL_MASK, 3); > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_CMN_CTRL1, val); > > + > > + /* Deassert PHY reset */ > > + cdns_phy_reg_write(cdns_phy, PHY_ISO_CMN_CTRL, 0x0001); > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_CMN_CTRL, > 0x0003); > > + > > + /* Power state machine registers */ > > + for (k = 0; k < num_lanes; k++) > > + cdns_phy_reg_write(cdns_phy, XCVR_PSM_RCTRL | (k << > 9), > 0xfefc); > > + > > + /* Assert cmn_macro_pwr_en */ > > + cdns_phy_reg_write(cdns_phy, PHY_PMA_ISO_CMN_CTRL, > 0x0013); > > + > > + /* wait for cmn_macro_pwr_en_ack */ > > + if (wait_for_ack(cdns_phy, PHY_PMA_ISO_CMN_CTRL, > CMN_MACRO_PWR_EN_ACK, > > + "MA output macro power up failed")) > > + return -1; > > Return the error value of wait_for_ack. OK. > > > + /* wait for cmn_ready */ > > + if (wait_for_ack(cdns_phy, PHY_PMA_CMN_CTRL1, CMN_READY, > > + "PMA output ready failed")) > > + return -1; > > Return the error value of wait_for_ack. OK. > > > + for (k = 0; k < num_lanes; k++) { > > + cdns_phy_reg_write(cdns_phy, TX_PSC_A0 | (k << 9), > 0x6791); > > + cdns_phy_reg_write(cdns_phy, TX_PSC_A1 | (k << 9), > 0x6790); > > + cdns_phy_reg_write(cdns_phy, TX_PSC_A2 | (k << 9), > 0x0090); > > + cdns_phy_reg_write(cdns_phy, TX_PSC_A3 | (k << 9), > 0x0090); > > + > > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_CAL | (k << > 9)); > > + val &= 0xffbb; > > + cdns_phy_reg_write(cdns_phy, RX_PSC_CAL | (k << 9), > > + val); > > + > > + val = cdns_phy_reg_read(cdns_phy, RX_PSC_A0 | (k << 9)); > > + val &= 0xffbb; > > + cdns_phy_reg_write(cdns_phy, RX_PSC_A0 | (k << 9), val); > > + } > > + return 0; > > +} > > + > > +static int hdptx_hdmi_phy_cfg(struct cdns_hdptx_hdmi_phy *cdns_phy, > > +u32 > > rate) +{ > > + const struct hdptx_hdmi_ctrl *p_ctrl_table; > > + const struct hdptx_hdmi_pll_tuning *p_pll_table; > > + const u32 refclk_freq_khz = cdns_phy->ref_clk_rate / 1000; > > + const u8 pclk_in = false; > > const bool pclk_in = false; OK. > > > + u32 pixel_freq = rate; > > + u32 vco_freq, char_freq; > > + u32 div_total, feedback_factor; > > + u32 i, ret; > > + > > + feedback_factor = hdptx_hdmi_feedback_factor(cdns_phy); > > + > > + char_freq = pixel_freq * feedback_factor / 1000; > > + > > + dev_dbg(cdns_phy->dev, > > + "Pixel clock: (%d KHz), character clock: %d, bpc is > > + (%0d- > bit)\n", > > + pixel_freq, char_freq, cdns_phy->bpc); > > + > > + /* Get right row from the ctrl_table table. > > + * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ > column. > > + * Consider only the rows with FEEDBACK_FACTOR column matching > > feedback_factor. + */ > > + for (i = 0; i < ARRAY_SIZE(pixel_clk_output_ctrl_table); i++) { > > + if (feedback_factor == > pixel_clk_output_ctrl_table[i].feedback_factor && > > + pixel_freq == > pixel_clk_output_ctrl_table[i].pixel_clk_freq_min) { > > + p_ctrl_table = &pixel_clk_output_ctrl_table[i]; > > + break; > > + } > > + } > > + if (i == ARRAY_SIZE(pixel_clk_output_ctrl_table)) { > > + dev_warn(cdns_phy->dev, > > + "Pixel clk (%d KHz) not supported, bpc is (%0d- > bit)\n", > > + pixel_freq, cdns_phy->bpc); > > + return 0; > > Returning 0 doesn't seem correct. The caller checks for small than 0. I suggest > returning 0 if configuration was successful, and some error code otherwise. OK, it should be change to return error code. > > > + } > > + > > + div_total = p_ctrl_table->pll_fb_div_total; > > + vco_freq = refclk_freq_khz * div_total / p_ctrl_table- > >cmnda_pll0_ip_div; > > + > > + /* Get right row from the pixel_clk_output_pll_table table. > > + * Check if vco_freq_khz and feedback_div_total > > + * column matching with pixel_clk_output_pll_table. > > + */ > > + for (i = 0; i < ARRAY_SIZE(pixel_clk_output_pll_table); i++) { > > + if (vco_freq == > > + pixel_clk_output_pll_table[i].vco_freq_min > && > > + div_total == > pixel_clk_output_pll_table[i].feedback_div_total) { > > + p_pll_table = &pixel_clk_output_pll_table[i]; > > + break; > > + } > > + } > > + if (i == ARRAY_SIZE(pixel_clk_output_pll_table)) { > > + dev_warn(cdns_phy->dev, "VCO (%d KHz) not > supported\n", > vco_freq); > > + return -1; > > return -EINVAL? OK. > > > + } > > + dev_dbg(cdns_phy->dev, "VCO frequency is (%d KHz)\n", vco_freq); > > + > > + ret = hdptx_hdmi_phy_config(cdns_phy, p_ctrl_table, p_pll_table, > pclk_in); > > + if (ret < 0) > > + return ret; > > + > > + return char_freq; > > See above. There is no need to return the character clock here. OK. > > > +} > > + > > +static int hdptx_hdmi_phy_power_up(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + /* set Power State to A2 */ > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A2); > > + > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_0, 1); > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_1, 1); > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_2, 1); > > + cdns_phy_reg_write(cdns_phy, TX_DIAG_ACYA_3, 1); > > + > > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A2_ACK, > > + "Wait A2 Ack failed")) > > + return -1; > > Return the error value of wait_for_ack. OK. > > > + > > + /* Power up ARC */ > > + hdptx_hdmi_arc_config(cdns_phy); > > + > > + /* Configure PHY in A0 mode (PHY must be in the A0 power > > + * state in order to transmit data) > > + */ > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A0); > > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A0_ACK, > > + "Wait A0 Ack failed")) > > + return -1; > > Return the error value of wait_for_ack. OK. > > > + > > + return 0; > > +} > > + > > +static int hdptx_hdmi_phy_power_down(struct cdns_hdptx_hdmi_phy > > +*cdns_phy) { > > + u32 val; > > + > > + val = cdns_phy_reg_read(cdns_phy, PHY_HDP_MODE_CTRL); > > + val &= ~(POWER_STATE_A0 | POWER_STATE_A1 | > POWER_STATE_A2 | > > POWER_STATE_A3); + /* PHY_DP_MODE_CTL set to A3 power state */ > > + cdns_phy_reg_write(cdns_phy, PHY_HDP_MODE_CTRL, val | > POWER_STATE_A3); > > + > > + if (wait_for_ack(cdns_phy, PHY_HDP_MODE_CTRL, > POWER_STATE_A3_ACK, > > + "Wait A3 Ack failed")) > > + return -1; > > Return the error value of wait_for_ack. OK. > > > + > > + return 0; > > +} > > + > > +static int cdns_hdptx_hdmi_phy_on(struct phy *phy) { > > + struct cdns_hdptx_hdmi_phy *cdns_phy = phy_get_drvdata(phy); > > + > > + return hdptx_hdmi_phy_power_up(cdns_phy); > > +} > > + > > +static int cdns_hdptx_hdmi_phy_off(struct phy *phy) { > > + struct cdns_hdptx_hdmi_phy *cdns_phy = phy_get_drvdata(phy); > > + > > + hdptx_hdmi_phy_power_down(cdns_phy); > > + return 0; > > +} > > + > > +int cdns_hdptx_hdmi_phy_valid(struct phy *phy, enum phy_mode mode, > > +int > > submode, + union phy_configure_opts > *opts) > > +{ > > + u32 rate = opts->hdmi.pixel_clk_rate; > > + int i; > > + > > + for (i = 0; i < ARRAY_SIZE(pixel_clk_output_ctrl_table); i++) > > + if (rate == > pixel_clk_output_ctrl_table[i].pixel_clk_freq_min) > > + return 0; > > + > > + return -EINVAL; > > +} > > + > > +static int cdns_hdptx_hdmi_phy_init(struct phy *phy) { > > + return 0; > > +} > > + > > +static int cdns_hdptx_hdmi_configure(struct phy *phy, > > + union phy_configure_opts *opts) > { > > + struct cdns_hdptx_hdmi_phy *cdns_phy = phy_get_drvdata(phy); > > + int ret; > > + > > + cdns_phy->pixel_clk_rate = opts->hdmi.pixel_clk_rate; > > + cdns_phy->color_space = opts->hdmi.color_space; > > + cdns_phy->bpc = opts->hdmi.bpc; > > + > > + /* Check HDMI FW alive before HDMI PHY init */ > > + ret = hdptx_phy_check_alive(cdns_phy); > > + if (!ret) { > > + dev_err(cdns_phy->dev, "NO HDMI FW running\n"); > > + return -ENXIO; > > + } > > + > > + /* Configure PHY */ > > + if (hdptx_hdmi_phy_cfg(cdns_phy, cdns_phy->pixel_clk_rate) < 0) { > > + dev_err(cdns_phy->dev, "failed to set phy pclock\n"); > > + return -EINVAL; > > + } > > + > > + ret = hdptx_hdmi_phy_power_up(cdns_phy); > > + if (ret < 0) > > + return ret; > > + > > + hdptx_hdmi_phy_set_vswing(cdns_phy); > > + > > + return 0; > > +} > > + > > +static const struct phy_ops cdns_hdptx_hdmi_phy_ops = { > > + .init = cdns_hdptx_hdmi_phy_init, > > + .configure = cdns_hdptx_hdmi_configure, > > + .power_on = cdns_hdptx_hdmi_phy_on, > > + .power_off = cdns_hdptx_hdmi_phy_off, > > + .validate = cdns_hdptx_hdmi_phy_valid, > > + .owner = THIS_MODULE, > > +}; > > + > > +static int cdns_hdptx_hdmi_phy_probe(struct platform_device *pdev) { > > + struct cdns_hdptx_hdmi_phy *cdns_phy; > > + struct device *dev = &pdev->dev; > > + struct device_node *node = dev->of_node; > > + struct phy_provider *phy_provider; > > + struct resource *res; > > + struct phy *phy; > > + int ret; > > + > > + cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL); > > + if (!cdns_phy) > > + return -ENOMEM; > > + > > + dev_set_drvdata(dev, cdns_phy); > > + cdns_phy->dev = dev; > > + mutex_init(&cdns_phy->mbox_mutex); > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (!res) > > + return -ENODEV; > > + cdns_phy->regs = devm_ioremap(dev, res->start, > resource_size(res)); > > + if (IS_ERR(cdns_phy->regs)) > > + return PTR_ERR(cdns_phy->regs); > > + > > + phy = devm_phy_create(dev, node, &cdns_hdptx_hdmi_phy_ops); > > + if (IS_ERR(phy)) > > + return PTR_ERR(phy); > > + > > + phy->attrs.mode = PHY_MODE_HDMI; > > + > > + cdns_phy->phy = phy; > > + phy_set_drvdata(phy, cdns_phy); > > + > > + /* init base struct for access mhdp mailbox */ > > + cdns_phy->base.dev = cdns_phy->dev; > > + cdns_phy->base.regs = cdns_phy->regs; > > + cdns_phy->base.mbox_mutex = &cdns_phy->mbox_mutex; > > The same as for the DP PHY driver applies here. Same as reply in DP PHY driver. Best, regards, Sandor > > Best regards, > Alexander > > > + > > + ret = hdptx_hdmi_clk_enable(cdns_phy); > > + if (ret) { > > + dev_err(dev, "Init clk fail\n"); > > + return -EINVAL; > > + } > > + > > + phy_provider = devm_of_phy_provider_register(dev, > of_phy_simple_xlate); > > + if (IS_ERR(phy_provider)) { > > + ret = PTR_ERR(phy_provider); > > + goto clk_disable; > > + } > > + > > + dev_dbg(dev, "probe success!\n"); > > + > > + return 0; > > + > > +clk_disable: > > + hdptx_hdmi_clk_disable(cdns_phy); > > + > > + return -EINVAL; > > +} > > + > > +static int cdns_hdptx_hdmi_phy_remove(struct platform_device *pdev) { > > + struct cdns_hdptx_hdmi_phy *cdns_phy = > > +platform_get_drvdata(pdev); > > + > > + hdptx_hdmi_clk_disable(cdns_phy); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id cdns_hdptx_hdmi_phy_of_match[] = { > > + {.compatible = "fsl,imx8mq-hdmi-phy" }, > > + { /* sentinel */ } > > +}; > > +MODULE_DEVICE_TABLE(of, cdns_hdptx_hdmi_phy_of_match); > > + > > +static struct platform_driver cdns_hdptx_hdmi_phy_driver = { > > + .probe = cdns_hdptx_hdmi_phy_probe, > > + .remove = cdns_hdptx_hdmi_phy_remove, > > + .driver = { > > + .name = "cdns-hdptx-hdmi-phy", > > + .of_match_table = cdns_hdptx_hdmi_phy_of_match, > > + } > > +}; > > +module_platform_driver(cdns_hdptx_hdmi_phy_driver); > > + > > +MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>"); > > +MODULE_DESCRIPTION("Cadence HDP-TX HDMI PHY driver"); > > +MODULE_LICENSE("GPL"); > > > -- > TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany > Amtsgericht München, HRB 105018 > Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider > http://www.tq/ > -group.com%2F&data=05%7C01%7CSandor.yu%40nxp.com%7C2d4ab7c82e4 > 94769e63a08dbcf135a98%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7 > C0%7C638331454264512557%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4 > wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000% > 7C%7C%7C&sdata=b%2F%2Fv3Wbo7ZRfDWR3bau%2BDrSQu3XiCpW7r12ZG > T5l1hQ%3D&reserved=0 >