Re: [PATCH v18 1/8] drm: bridge: Cadence: Create MHDP helper driver

From: Dmitry Baryshkov
Date: Thu Oct 31 2024 - 15:52:01 EST


On Tue, Oct 29, 2024 at 02:02:09PM +0800, Sandor Yu wrote:
> Mailbox access functions in MHDP8546 will be share to other MHDP driver
> and Cadence HDP-TX HDMI/DP PHY drivers.
>
> Create a new MHDP helper driver and move all mailbox access functions into.
> According the mailbox access sequence and type of security.
> Six mailbox access API functions are introduced.
> Three APIs for non-secure mailbox access:
> - cdns_mhdp_mailbox_send()
> - cdns_mhdp_mailbox_send_recv()
> - cdns_mhdp_mailbox_send_recv_multi()
> The other three APIs for secure mailbox access:
> - cdns_mhdp_secure_mailbox_send()
> - cdns_mhdp_secure_mailbox_send_recv()
> - cdns_mhdp_secure_mailbox_send_recv_multi()
>
> All MHDP commands that need to be passed through the mailbox
> have been rewritten using those new API functions.
>
> The register read/write and DP DPCD read/write command functions
> are also included in this new helper driver.
>
> Function 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 added with the general command ID
> GENERAL_REGISTER_WRITE.
>
> Signed-off-by: Sandor Yu <Sandor.yu@xxxxxxx>
> ---
> v17->v18:
> - Create three ordinary mailbox access APIs
> cdns_mhdp_mailbox_send
> cdns_mhdp_mailbox_send_recv
> cdns_mhdp_mailbox_send_recv_multi
> - Create three secure mailbox access APIs
> cdns_mhdp_secure_mailbox_send
> cdns_mhdp_secure_mailbox_send_recv
> cdns_mhdp_secure_mailbox_send_recv_multi
> - MHDP8546 DP and HDCP commands that need access mailbox are rewrited
> with above 6 API functions.
>
> v16->v17:
> - Replaces the local mutex mbox_mutex with a global mutex mhdp_mailbox_mutex
>
> v12->v16:
> *No change.
>
> drivers/gpu/drm/bridge/cadence/Kconfig | 4 +
> drivers/gpu/drm/bridge/cadence/Makefile | 1 +
> .../gpu/drm/bridge/cadence/cdns-mhdp-helper.c | 414 +++++++++++++++
> .../drm/bridge/cadence/cdns-mhdp8546-core.c | 487 +++---------------
> .../drm/bridge/cadence/cdns-mhdp8546-core.h | 47 +-
> .../drm/bridge/cadence/cdns-mhdp8546-hdcp.c | 236 +--------
> .../drm/bridge/cadence/cdns-mhdp8546-hdcp.h | 18 +-
> include/drm/bridge/cdns-mhdp-helper.h | 130 +++++
> 8 files changed, 658 insertions(+), 679 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 cced81633ddcd..e0973339e9e33 100644
> --- a/drivers/gpu/drm/bridge/cadence/Kconfig
> +++ b/drivers/gpu/drm/bridge/cadence/Kconfig
> @@ -21,6 +21,9 @@ config DRM_CDNS_DSI_J721E
> the routing of the DSS DPI signal to the Cadence DSI.
> endif
>
> +config CDNS_MHDP_HELPER
> + tristate
> +

I'm not sure if the placement for the helper is suitable. Especially if
you want to reuse the helper by some other subsystem. As far as I don't
like it, maybe drivers/soc is a better option.

> config DRM_CDNS_MHDP8546
> tristate "Cadence DPI/DP bridge"
> select DRM_DISPLAY_DP_HELPER
> @@ -28,6 +31,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..f39228a78c7cb
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-helper.c
> @@ -0,0 +1,414 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (C) 2023, 2024 NXP Semiconductor, Inc.
> + *
> + */
> +#include <drm/bridge/cdns-mhdp-helper.h>
> +#include <linux/dev_printk.h>
> +#include <linux/module.h>
> +
> +/* Protects mailbox communications with the firmware */
> +static DEFINE_MUTEX(mhdp_mailbox_mutex);
> +
> +/* Mailbox helper functions */
> +static int mhdp_mailbox_read(void __iomem *regs)
> +{
> + int ret, empty;
> +
> + WARN_ON(!mutex_is_locked(&mhdp_mailbox_mutex));
> +
> + ret = readx_poll_timeout(readl, regs + CDNS_MAILBOX_EMPTY,
> + empty, !empty, MAILBOX_RETRY_US,
> + MAILBOX_TIMEOUT_US);
> + if (ret < 0)
> + return ret;
> +
> + return readl(regs + CDNS_MAILBOX_RX_DATA) & 0xff;
> +}
> +
> +static int mhdp_mailbox_write(void __iomem *regs, u8 val)
> +{
> + int ret, full;
> +
> + WARN_ON(!mutex_is_locked(&mhdp_mailbox_mutex));
> +
> + ret = readx_poll_timeout(readl, regs + CDNS_MAILBOX_FULL,
> + full, !full, MAILBOX_RETRY_US,
> + MAILBOX_TIMEOUT_US);
> + if (ret < 0)
> + return ret;
> +
> + writel(val, regs + CDNS_MAILBOX_TX_DATA);
> +
> + return 0;
> +}
> +
> +static int mhdp_mailbox_recv_header(void __iomem *regs,
> + 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 = mhdp_mailbox_read(regs);
> + if (ret < 0)
> + return ret;
> +
> + header[i] = ret;
> + }
> +
> + mbox_size = get_unaligned_be16(header + 2);
> +
> + if (opcode != header[0] || module_id != header[1] ||
> + (opcode != HDCP_TRAN_IS_REC_ID_VALID && req_size != mbox_size)) {

Could you please explain the second clause?

> + /*
> + * 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 (mhdp_mailbox_read(regs) < 0)
> + break;
> +
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int mhdp_mailbox_recv_data(void __iomem *regs,
> + u8 *buff, u16 buff_size)
> +{
> + u32 i;
> + int ret;
> +
> + for (i = 0; i < buff_size; i++) {
> + ret = mhdp_mailbox_read(regs);
> + if (ret < 0)
> + return ret;
> +
> + buff[i] = ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mhdp_mailbox_send(void __iomem *regs, 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 = mhdp_mailbox_write(regs, header[i]);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < size; i++) {
> + ret = mhdp_mailbox_write(regs, message[i]);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +

Please add kerneldocs for all new API. It's obvious for the simple ones,
but not for the more complex functions.

> +int cdns_mhdp_mailbox_send(struct cdns_mhdp_base *base, u8 module_id,
> + u8 opcode, u16 size, u8 *message)
> +{
> + int ret;
> +
> + mutex_lock(&mhdp_mailbox_mutex);

guard(mutex)(&mhdp_mailbox_mutex);

return mhdp_mailbox_send(...);

Similar changes can be applied further on.

> +
> + ret = mhdp_mailbox_send(base->regs, module_id, opcode, size, message);
> +
> + mutex_unlock(&mhdp_mailbox_mutex);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_send);
> +
> +int cdns_mhdp_mailbox_send_recv(struct cdns_mhdp_base *base,
> + u8 module_id, u8 opcode,
> + u16 msg_size, u8 *msg,
> + u16 resp_size, u8 *resp)
> +{
> + int ret;
> +
> + mutex_lock(&mhdp_mailbox_mutex);
> +
> + ret = mhdp_mailbox_send(base->regs, module_id,
> + opcode, msg_size, msg);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_header(base->regs, module_id,
> + opcode, resp_size);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_data(base->regs, resp, resp_size);
> +out:
> + mutex_unlock(&mhdp_mailbox_mutex);
> +
> + if (ret)
> + dev_err(base->dev, "ModuleID=%d, CMD=%d failed: %d\n",
> + module_id, opcode, ret);

Using guard() would allow you to use precise messages, e.g. failed to
send, failed to received header, failed to receive data.

> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_send_recv);
> +

Like what is _multi? Why do you need to fetch into two separate buffers?
Can you fetch into one and then parse it accordingly? Please don't make
the API more complicated than necessary.

> +int cdns_mhdp_mailbox_send_recv_multi(struct cdns_mhdp_base *base,
> + u8 module_id, u8 opcode,
> + u16 msg_size, u8 *msg,
> + u8 opcode_resp,
> + u16 resp1_size, u8 *resp1,
> + u16 resp2_size, u8 *resp2)
> +{
> + int ret;
> +
> + mutex_lock(&mhdp_mailbox_mutex);
> +
> + ret = mhdp_mailbox_send(base->regs, module_id,
> + opcode, msg_size, msg);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_header(base->regs, module_id, opcode_resp,
> + resp1_size + resp2_size);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_data(base->regs, resp1, resp1_size);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_data(base->regs, resp2, resp2_size);
> +out:
> + mutex_unlock(&mhdp_mailbox_mutex);
> +
> + if (ret)
> + dev_err(base->dev, "ModuleID=%d, MSG_CMD=%d Resp_CMD=%d failed: %d\n",
> + module_id, opcode, opcode_resp, ret);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_mailbox_send_recv_multi);
> +
> +/*
> + * Secure mailbox access functions:
> + * These functions handle secure mailbox communication, which differs
> + * from non-secure mailbox access. They use the secure address.
> + */
> +int cdns_mhdp_secure_mailbox_send(struct cdns_mhdp_base *base, u8 module_id,
> + u8 opcode, u16 size, u8 *message)
> +{
> + int ret;
> +
> + mutex_lock(&mhdp_mailbox_mutex);
> +
> + ret = mhdp_mailbox_send(base->sapb_regs, module_id, opcode, size, message);
> +
> + mutex_unlock(&mhdp_mailbox_mutex);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_secure_mailbox_send);
> +
> +int cdns_mhdp_secure_mailbox_send_recv(struct cdns_mhdp_base *base,
> + u8 module_id, u8 opcode,
> + u16 msg_size, u8 *msg,
> + u16 resp_size, u8 *resp)
> +{
> + int ret;
> +
> + mutex_lock(&mhdp_mailbox_mutex);
> +
> + ret = mhdp_mailbox_send(base->sapb_regs, module_id,
> + opcode, msg_size, msg);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_header(base->sapb_regs, module_id,
> + opcode, resp_size);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_data(base->sapb_regs, resp, resp_size);
> +out:
> + mutex_unlock(&mhdp_mailbox_mutex);
> +
> + if (ret)
> + dev_err(base->dev, "ModuleID=%d, CMD=%d failed: %d\n",
> + module_id, opcode, ret);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_secure_mailbox_send_recv);
> +
> +int cdns_mhdp_secure_mailbox_send_recv_multi(struct cdns_mhdp_base *base,
> + u8 module_id, u8 opcode,
> + u16 msg_size, u8 *msg,
> + u8 opcode_resp,
> + u16 resp1_size, u8 *resp1,
> + u16 resp2_size, u8 *resp2)
> +{
> + int ret;
> +
> + mutex_lock(&mhdp_mailbox_mutex);
> +
> + ret = mhdp_mailbox_send(base->sapb_regs, module_id,
> + opcode, msg_size, msg);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_header(base->sapb_regs, module_id,
> + opcode_resp,
> + resp1_size + resp2_size);
> + if (ret)
> + goto out;
> +
> + ret = mhdp_mailbox_recv_data(base->sapb_regs, resp1, resp1_size);
> + if (ret)
> + goto out;
> +
> + if (module_id == MB_MODULE_ID_HDCP_TX &&
> + opcode == HDCP_TRAN_IS_REC_ID_VALID)

comment, please.

> + ret = mhdp_mailbox_recv_data(base->sapb_regs, resp2, 5 * resp1[0]);
> + else
> + ret = mhdp_mailbox_recv_data(base->sapb_regs, resp2, resp2_size);
> +out:
> + mutex_unlock(&mhdp_mailbox_mutex);
> +
> + if (ret)
> + dev_err(base->dev, "ModuleID=%d, CMD=%d failed: %d\n",
> + module_id, opcode, ret);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_secure_mailbox_send_recv_multi);
> +
> +/* General read/write 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);
> +
> + ret = cdns_mhdp_mailbox_send_recv(base, MB_MODULE_ID_GENERAL,
> + GENERAL_REGISTER_READ,
> + sizeof(msg), msg, sizeof(resp), 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:
> + 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];
> +
> + put_unaligned_be32(addr, msg);
> + put_unaligned_be32(val, msg + 4);
> +
> + return cdns_mhdp_mailbox_send(base, MB_MODULE_ID_GENERAL,
> + GENERAL_REGISTER_WRITE,
> + sizeof(msg), msg);
> +}
> +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];
> +
> + put_unaligned_be16(addr, msg);
> + put_unaligned_be32(val, msg + 2);
> +
> + return cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX,
> + DPTX_WRITE_REGISTER, sizeof(msg), msg);
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_dp_reg_write);

Please don't add API without a user. If the function is required in the
following patch, add it there.

> +
> +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];
> +
> + put_unaligned_be16(addr, field);
> + field[2] = start_bit;
> + field[3] = bits_no;
> + put_unaligned_be32(val, field + 4);
> +
> + return cdns_mhdp_mailbox_send(base, MB_MODULE_ID_DP_TX,
> + DPTX_WRITE_FIELD, sizeof(field), field);
> +}
> +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];
> +
> + put_unaligned_be16(len, msg);
> + put_unaligned_be24(addr, msg + 2);
> +
> + return cdns_mhdp_mailbox_send_recv_multi(base,
> + MB_MODULE_ID_DP_TX,
> + DPTX_READ_DPCD,
> + sizeof(msg), msg,
> + DPTX_READ_DPCD,
> + sizeof(reg), reg,
> + len, data);
> +}
> +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;
> +
> + ret = cdns_mhdp_mailbox_send_recv(base, MB_MODULE_ID_DP_TX,
> + DPTX_WRITE_DPCD,
> + sizeof(msg), msg, sizeof(reg), reg);
> + if (ret) {
> + dev_err(base->dev, "dpcd write failed: %d\n", ret);
> + return ret;
> + }
> +
> + if (addr != get_unaligned_be24(reg + 2)) {
> + dev_err(base->dev, "Invalid response: expected address 0x%06x, got 0x%06x\n",
> + addr, get_unaligned_be24(reg + 2));
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(cdns_mhdp_dpcd_write);
> +
> +MODULE_DESCRIPTION("Cadence MHDP Helper driver");
> +MODULE_AUTHOR("Sandor Yu <Sandor.yu@xxxxxxx>");
> +MODULE_LICENSE("GPL");

--
With best wishes
Dmitry