From 5bd0d8e687bf04fdd3d4a733a6bb17e25d4a1de2 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 16 Oct 2024 23:06:51 +0300 Subject: [PATCH 01/90] drm/bridge: synopsys: Add DW HDMI QP TX Controller support library The Synopsys DesignWare HDMI 2.1 Quad-Pixel (QP) TX Controller IP supports the following features, among others: * Fixed Rate Link (FRL) * Display Stream Compression (DSC) * 4K@120Hz and 8K@60Hz video modes * Variable Refresh Rate (VRR) including Quick Media Switching (QMS), aka Cinema VRR * Fast Vactive (FVA), aka Quick Frame Transport (QFT) * SCDC I2C DDC access * TMDS Scrambler enabling 2160p@60Hz with RGB/YCbCr4:4:4 * YCbCr4:2:0 enabling 2160p@60Hz at lower HDMI link speeds * Multi-stream audio * Enhanced Audio Return Channel (EARC) Add library containing common helpers to enable basic support, i.e. RGB output up to 4K@30Hz, without audio, CEC or any HDMI 2.1 specific features. Co-developed-by: Algea Cao Signed-off-by: Algea Cao Tested-by: Heiko Stuebner Reviewed-by: Maxime Ripard Signed-off-by: Cristian Ciocaltea Acked-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20241016-b4-rk3588-bridge-upstream-v10-1-87ef92a6d14e@collabora.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/bridge/synopsys/Kconfig | 8 + drivers/gpu/drm/bridge/synopsys/Makefile | 2 + drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 647 ++++++++++++++ drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h | 834 +++++++++++++++++++ include/drm/bridge/dw_hdmi_qp.h | 32 + 5 files changed, 1523 insertions(+) create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c create mode 100644 drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h create mode 100644 include/drm/bridge/dw_hdmi_qp.h diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig index 15fc182d05ef0..ca416dab156d8 100644 --- a/drivers/gpu/drm/bridge/synopsys/Kconfig +++ b/drivers/gpu/drm/bridge/synopsys/Kconfig @@ -46,6 +46,14 @@ config DRM_DW_HDMI_CEC Support the CE interface which is part of the Synopsys Designware HDMI block. +config DRM_DW_HDMI_QP + tristate + select DRM_DISPLAY_HDMI_HELPER + select DRM_DISPLAY_HDMI_STATE_HELPER + select DRM_DISPLAY_HELPER + select DRM_KMS_HELPER + select REGMAP_MMIO + config DRM_DW_MIPI_DSI tristate select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/bridge/synopsys/Makefile b/drivers/gpu/drm/bridge/synopsys/Makefile index ce715562e9e52..9869d9651ed1f 100644 --- a/drivers/gpu/drm/bridge/synopsys/Makefile +++ b/drivers/gpu/drm/bridge/synopsys/Makefile @@ -5,4 +5,6 @@ obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o +obj-$(CONFIG_DRM_DW_HDMI_QP) += dw-hdmi-qp.o + obj-$(CONFIG_DRM_DW_MIPI_DSI) += dw-mipi-dsi.o diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c new file mode 100644 index 0000000000000..181c5164b2319 --- /dev/null +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -0,0 +1,647 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. + * Copyright (c) 2024 Collabora Ltd. + * + * Author: Algea Cao + * Author: Cristian Ciocaltea + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dw-hdmi-qp.h" + +#define DDC_CI_ADDR 0x37 +#define DDC_SEGMENT_ADDR 0x30 + +#define HDMI14_MAX_TMDSCLK 340000000 + +#define SCRAMB_POLL_DELAY_MS 3000 + +struct dw_hdmi_qp_i2c { + struct i2c_adapter adap; + + struct mutex lock; /* used to serialize data transfers */ + struct completion cmp; + u8 stat; + + u8 slave_reg; + bool is_regaddr; + bool is_segment; +}; + +struct dw_hdmi_qp { + struct drm_bridge bridge; + + struct device *dev; + struct dw_hdmi_qp_i2c *i2c; + + struct { + const struct dw_hdmi_qp_phy_ops *ops; + void *data; + } phy; + + struct regmap *regm; +}; + +static void dw_hdmi_qp_write(struct dw_hdmi_qp *hdmi, unsigned int val, + int offset) +{ + regmap_write(hdmi->regm, offset, val); +} + +static unsigned int dw_hdmi_qp_read(struct dw_hdmi_qp *hdmi, int offset) +{ + unsigned int val = 0; + + regmap_read(hdmi->regm, offset, &val); + + return val; +} + +static void dw_hdmi_qp_mod(struct dw_hdmi_qp *hdmi, unsigned int data, + unsigned int mask, unsigned int reg) +{ + regmap_update_bits(hdmi->regm, reg, mask, data); +} + +static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi, + unsigned char *buf, unsigned int length) +{ + struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; + int stat; + + if (!i2c->is_regaddr) { + dev_dbg(hdmi->dev, "set read register address to 0\n"); + i2c->slave_reg = 0x00; + i2c->is_regaddr = true; + } + + while (length--) { + reinit_completion(&i2c->cmp); + + dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, + I2CM_INTERFACE_CONTROL0); + + if (i2c->is_segment) + dw_hdmi_qp_mod(hdmi, I2CM_EXT_READ, I2CM_WR_MASK, + I2CM_INTERFACE_CONTROL0); + else + dw_hdmi_qp_mod(hdmi, I2CM_FM_READ, I2CM_WR_MASK, + I2CM_INTERFACE_CONTROL0); + + stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); + if (!stat) { + dev_err(hdmi->dev, "i2c read timed out\n"); + dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); + return -EAGAIN; + } + + /* Check for error condition on the bus */ + if (i2c->stat & I2CM_NACK_RCVD_IRQ) { + dev_err(hdmi->dev, "i2c read error\n"); + dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); + return -EIO; + } + + *buf++ = dw_hdmi_qp_read(hdmi, I2CM_INTERFACE_RDDATA_0_3) & 0xff; + dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); + } + + i2c->is_segment = false; + + return 0; +} + +static int dw_hdmi_qp_i2c_write(struct dw_hdmi_qp *hdmi, + unsigned char *buf, unsigned int length) +{ + struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; + int stat; + + if (!i2c->is_regaddr) { + /* Use the first write byte as register address */ + i2c->slave_reg = buf[0]; + length--; + buf++; + i2c->is_regaddr = true; + } + + while (length--) { + reinit_completion(&i2c->cmp); + + dw_hdmi_qp_write(hdmi, *buf++, I2CM_INTERFACE_WRDATA_0_3); + dw_hdmi_qp_mod(hdmi, i2c->slave_reg++ << 12, I2CM_ADDR, + I2CM_INTERFACE_CONTROL0); + dw_hdmi_qp_mod(hdmi, I2CM_FM_WRITE, I2CM_WR_MASK, + I2CM_INTERFACE_CONTROL0); + + stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); + if (!stat) { + dev_err(hdmi->dev, "i2c write time out!\n"); + dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); + return -EAGAIN; + } + + /* Check for error condition on the bus */ + if (i2c->stat & I2CM_NACK_RCVD_IRQ) { + dev_err(hdmi->dev, "i2c write nack!\n"); + dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); + return -EIO; + } + + dw_hdmi_qp_mod(hdmi, 0, I2CM_WR_MASK, I2CM_INTERFACE_CONTROL0); + } + + return 0; +} + +static int dw_hdmi_qp_i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + struct dw_hdmi_qp *hdmi = i2c_get_adapdata(adap); + struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; + u8 addr = msgs[0].addr; + int i, ret = 0; + + if (addr == DDC_CI_ADDR) + /* + * The internal I2C controller does not support the multi-byte + * read and write operations needed for DDC/CI. + * FIXME: Blacklist the DDC/CI address until we filter out + * unsupported I2C operations. + */ + return -EOPNOTSUPP; + + for (i = 0; i < num; i++) { + if (msgs[i].len == 0) { + dev_err(hdmi->dev, + "unsupported transfer %d/%d, no data\n", + i + 1, num); + return -EOPNOTSUPP; + } + } + + guard(mutex)(&i2c->lock); + + /* Unmute DONE and ERROR interrupts */ + dw_hdmi_qp_mod(hdmi, I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, + I2CM_NACK_RCVD_MASK_N | I2CM_OP_DONE_MASK_N, + MAINUNIT_1_INT_MASK_N); + + /* Set slave device address taken from the first I2C message */ + if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1) + addr = DDC_ADDR; + + dw_hdmi_qp_mod(hdmi, addr << 5, I2CM_SLVADDR, I2CM_INTERFACE_CONTROL0); + + /* Set slave device register address on transfer */ + i2c->is_regaddr = false; + + /* Set segment pointer for I2C extended read mode operation */ + i2c->is_segment = false; + + for (i = 0; i < num; i++) { + if (msgs[i].addr == DDC_SEGMENT_ADDR && msgs[i].len == 1) { + i2c->is_segment = true; + dw_hdmi_qp_mod(hdmi, DDC_SEGMENT_ADDR, I2CM_SEG_ADDR, + I2CM_INTERFACE_CONTROL1); + dw_hdmi_qp_mod(hdmi, *msgs[i].buf << 7, I2CM_SEG_PTR, + I2CM_INTERFACE_CONTROL1); + } else { + if (msgs[i].flags & I2C_M_RD) + ret = dw_hdmi_qp_i2c_read(hdmi, msgs[i].buf, + msgs[i].len); + else + ret = dw_hdmi_qp_i2c_write(hdmi, msgs[i].buf, + msgs[i].len); + } + if (ret < 0) + break; + } + + if (!ret) + ret = num; + + /* Mute DONE and ERROR interrupts */ + dw_hdmi_qp_mod(hdmi, 0, I2CM_OP_DONE_MASK_N | I2CM_NACK_RCVD_MASK_N, + MAINUNIT_1_INT_MASK_N); + + return ret; +} + +static u32 dw_hdmi_qp_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm dw_hdmi_qp_algorithm = { + .master_xfer = dw_hdmi_qp_i2c_xfer, + .functionality = dw_hdmi_qp_i2c_func, +}; + +static struct i2c_adapter *dw_hdmi_qp_i2c_adapter(struct dw_hdmi_qp *hdmi) +{ + struct dw_hdmi_qp_i2c *i2c; + struct i2c_adapter *adap; + int ret; + + i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return ERR_PTR(-ENOMEM); + + mutex_init(&i2c->lock); + init_completion(&i2c->cmp); + + adap = &i2c->adap; + adap->owner = THIS_MODULE; + adap->dev.parent = hdmi->dev; + adap->algo = &dw_hdmi_qp_algorithm; + strscpy(adap->name, "DesignWare HDMI QP", sizeof(adap->name)); + + i2c_set_adapdata(adap, hdmi); + + ret = devm_i2c_add_adapter(hdmi->dev, adap); + if (ret) { + dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name); + devm_kfree(hdmi->dev, i2c); + return ERR_PTR(ret); + } + + hdmi->i2c = i2c; + dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); + + return adap; +} + +static int dw_hdmi_qp_config_avi_infoframe(struct dw_hdmi_qp *hdmi, + const u8 *buffer, size_t len) +{ + u32 val, i, j; + + if (len != HDMI_INFOFRAME_SIZE(AVI)) { + dev_err(hdmi->dev, "failed to configure avi infoframe\n"); + return -EINVAL; + } + + /* + * DW HDMI QP IP uses a different byte format from standard AVI info + * frames, though generally the bits are in the correct bytes. + */ + val = buffer[1] << 8 | buffer[2] << 16; + dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS0); + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + if (i * 4 + j >= 14) + break; + if (!j) + val = buffer[i * 4 + j + 3]; + val |= buffer[i * 4 + j + 3] << (8 * j); + } + + dw_hdmi_qp_write(hdmi, val, PKT_AVI_CONTENTS1 + i * 4); + } + + dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_FIELDRATE, PKTSCHED_PKT_CONFIG1); + + dw_hdmi_qp_mod(hdmi, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, + PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN); + + return 0; +} + +static int dw_hdmi_qp_config_drm_infoframe(struct dw_hdmi_qp *hdmi, + const u8 *buffer, size_t len) +{ + u32 val, i; + + if (len != HDMI_INFOFRAME_SIZE(DRM)) { + dev_err(hdmi->dev, "failed to configure drm infoframe\n"); + return -EINVAL; + } + + dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); + + val = buffer[1] << 8 | buffer[2] << 16; + dw_hdmi_qp_write(hdmi, val, PKT_DRMI_CONTENTS0); + + for (i = 0; i <= buffer[2]; i++) { + if (i % 4 == 0) + val = buffer[3 + i]; + val |= buffer[3 + i] << ((i % 4) * 8); + + if ((i % 4 == 3) || i == buffer[2]) + dw_hdmi_qp_write(hdmi, val, + PKT_DRMI_CONTENTS1 + ((i / 4) * 4)); + } + + dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_FIELDRATE, PKTSCHED_PKT_CONFIG1); + dw_hdmi_qp_mod(hdmi, PKTSCHED_DRMI_TX_EN, PKTSCHED_DRMI_TX_EN, + PKTSCHED_PKT_EN); + + return 0; +} + +static int dw_hdmi_qp_bridge_atomic_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + int ret; + + ret = drm_atomic_helper_connector_hdmi_check(conn_state->connector, + conn_state->state); + if (ret) + dev_dbg(hdmi->dev, "%s failed: %d\n", __func__, ret); + + return ret; +} + +static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + struct drm_atomic_state *state = old_state->base.state; + struct drm_connector_state *conn_state; + struct drm_connector *connector; + unsigned int op_mode; + + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); + if (WARN_ON(!connector)) + return; + + conn_state = drm_atomic_get_new_connector_state(state, connector); + if (WARN_ON(!conn_state)) + return; + + if (connector->display_info.is_hdmi) { + dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n", + __func__, conn_state->hdmi.tmds_char_rate); + op_mode = 0; + } else { + dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__); + op_mode = OPMODE_DVI; + } + + hdmi->phy.ops->init(hdmi, hdmi->phy.data); + + dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); + dw_hdmi_qp_mod(hdmi, op_mode, OPMODE_DVI, LINK_CONFIG0); + + drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); +} + +static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_state) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + + hdmi->phy.ops->disable(hdmi, hdmi->phy.data); +} + +static enum drm_connector_status +dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + + return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); +} + +static const struct drm_edid * +dw_hdmi_qp_bridge_edid_read(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + const struct drm_edid *drm_edid; + + drm_edid = drm_edid_read_ddc(connector, bridge->ddc); + if (!drm_edid) + dev_dbg(hdmi->dev, "failed to get edid\n"); + + return drm_edid; +} + +static enum drm_mode_status +dw_hdmi_qp_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + unsigned long long rate; + + rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB); + if (rate > HDMI14_MAX_TMDSCLK) { + dev_dbg(hdmi->dev, "Unsupported mode clock: %d\n", mode->clock); + return MODE_CLOCK_HIGH; + } + + return MODE_OK; +} + +static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN, + PKTSCHED_PKT_EN); + break; + + case HDMI_INFOFRAME_TYPE_DRM: + dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN); + break; + + default: + dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); + } + + return 0; +} + +static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge, + enum hdmi_infoframe_type type, + const u8 *buffer, size_t len) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + + dw_hdmi_qp_bridge_clear_infoframe(bridge, type); + + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: + return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len); + + case HDMI_INFOFRAME_TYPE_DRM: + return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len); + + default: + dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type); + return 0; + } +} + +static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = { + .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, + .atomic_check = dw_hdmi_qp_bridge_atomic_check, + .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, + .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, + .detect = dw_hdmi_qp_bridge_detect, + .edid_read = dw_hdmi_qp_bridge_edid_read, + .mode_valid = dw_hdmi_qp_bridge_mode_valid, + .hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe, + .hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe, +}; + +static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id) +{ + struct dw_hdmi_qp *hdmi = dev_id; + struct dw_hdmi_qp_i2c *i2c = hdmi->i2c; + u32 stat; + + stat = dw_hdmi_qp_read(hdmi, MAINUNIT_1_INT_STATUS); + + i2c->stat = stat & (I2CM_OP_DONE_IRQ | I2CM_READ_REQUEST_IRQ | + I2CM_NACK_RCVD_IRQ); + + if (i2c->stat) { + dw_hdmi_qp_write(hdmi, i2c->stat, MAINUNIT_1_INT_CLEAR); + complete(&i2c->cmp); + } + + if (stat) + return IRQ_HANDLED; + + return IRQ_NONE; +} + +static const struct regmap_config dw_hdmi_qp_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = EARCRX_1_INT_FORCE, +}; + +static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi) +{ + dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N); + dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N); + dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0); + + /* Software reset */ + dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); + + dw_hdmi_qp_write(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0); + + dw_hdmi_qp_mod(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0); + + /* Clear DONE and ERROR interrupts */ + dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, + MAINUNIT_1_INT_CLEAR); + + if (hdmi->phy.ops->setup_hpd) + hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); +} + +struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, + struct drm_encoder *encoder, + const struct dw_hdmi_qp_plat_data *plat_data) +{ + struct device *dev = &pdev->dev; + struct dw_hdmi_qp *hdmi; + void __iomem *regs; + int ret; + + if (!plat_data->phy_ops || !plat_data->phy_ops->init || + !plat_data->phy_ops->disable || !plat_data->phy_ops->read_hpd) { + dev_err(dev, "Missing platform PHY ops\n"); + return ERR_PTR(-ENODEV); + } + + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return ERR_PTR(-ENOMEM); + + hdmi->dev = dev; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return ERR_CAST(regs); + + hdmi->regm = devm_regmap_init_mmio(dev, regs, &dw_hdmi_qp_regmap_config); + if (IS_ERR(hdmi->regm)) { + dev_err(dev, "Failed to configure regmap\n"); + return ERR_CAST(hdmi->regm); + } + + hdmi->phy.ops = plat_data->phy_ops; + hdmi->phy.data = plat_data->phy_data; + + dw_hdmi_qp_init_hw(hdmi); + + ret = devm_request_threaded_irq(dev, plat_data->main_irq, + dw_hdmi_qp_main_hardirq, NULL, + IRQF_SHARED, dev_name(dev), hdmi); + if (ret) + return ERR_PTR(ret); + + hdmi->bridge.driver_private = hdmi; + hdmi->bridge.funcs = &dw_hdmi_qp_bridge_funcs; + hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | + DRM_BRIDGE_OP_EDID | + DRM_BRIDGE_OP_HDMI | + DRM_BRIDGE_OP_HPD; + hdmi->bridge.of_node = pdev->dev.of_node; + hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + hdmi->bridge.vendor = "Synopsys"; + hdmi->bridge.product = "DW HDMI QP TX"; + + hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi); + if (IS_ERR(hdmi->bridge.ddc)) + return ERR_CAST(hdmi->bridge.ddc); + + ret = devm_drm_bridge_add(dev, &hdmi->bridge); + if (ret) + return ERR_PTR(ret); + + ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) + return ERR_PTR(ret); + + return hdmi; +} +EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind); + +void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi) +{ + dw_hdmi_qp_init_hw(hdmi); +} +EXPORT_SYMBOL_GPL(dw_hdmi_qp_resume); + +MODULE_AUTHOR("Algea Cao "); +MODULE_AUTHOR("Cristian Ciocaltea "); +MODULE_DESCRIPTION("DW HDMI QP transmitter library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h new file mode 100644 index 0000000000000..2115b8ef0bd68 --- /dev/null +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h @@ -0,0 +1,834 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Rockchip Electronics Co.Ltd + * Author: + * Algea Cao + */ +#ifndef __DW_HDMI_QP_H__ +#define __DW_HDMI_QP_H__ + +#include + +/* Main Unit Registers */ +#define CORE_ID 0x0 +#define VER_NUMBER 0x4 +#define VER_TYPE 0x8 +#define CONFIG_REG 0xc +#define CONFIG_CEC BIT(28) +#define CONFIG_AUD_UD BIT(23) +#define CORE_TIMESTAMP_HHMM 0x14 +#define CORE_TIMESTAMP_MMDD 0x18 +#define CORE_TIMESTAMP_YYYY 0x1c +/* Reset Manager Registers */ +#define GLOBAL_SWRESET_REQUEST 0x40 +#define EARCRX_CMDC_SWINIT_P BIT(27) +#define AVP_DATAPATH_PACKET_AUDIO_SWINIT_P BIT(10) +#define GLOBAL_SWDISABLE 0x44 +#define CEC_SWDISABLE BIT(17) +#define AVP_DATAPATH_PACKET_AUDIO_SWDISABLE BIT(10) +#define AVP_DATAPATH_VIDEO_SWDISABLE BIT(6) +#define RESET_MANAGER_CONFIG0 0x48 +#define RESET_MANAGER_STATUS0 0x50 +#define RESET_MANAGER_STATUS1 0x54 +#define RESET_MANAGER_STATUS2 0x58 +/* Timer Base Registers */ +#define TIMER_BASE_CONFIG0 0x80 +#define TIMER_BASE_STATUS0 0x84 +/* CMU Registers */ +#define CMU_CONFIG0 0xa0 +#define CMU_CONFIG1 0xa4 +#define CMU_CONFIG2 0xa8 +#define CMU_CONFIG3 0xac +#define CMU_STATUS 0xb0 +#define DISPLAY_CLK_MONITOR 0x3f +#define DISPLAY_CLK_LOCKED 0X15 +#define EARC_BPCLK_OFF BIT(9) +#define AUDCLK_OFF BIT(7) +#define LINKQPCLK_OFF BIT(5) +#define VIDQPCLK_OFF BIT(3) +#define IPI_CLK_OFF BIT(1) +#define CMU_IPI_CLK_FREQ 0xb4 +#define CMU_VIDQPCLK_FREQ 0xb8 +#define CMU_LINKQPCLK_FREQ 0xbc +#define CMU_AUDQPCLK_FREQ 0xc0 +#define CMU_EARC_BPCLK_FREQ 0xc4 +/* I2CM Registers */ +#define I2CM_SM_SCL_CONFIG0 0xe0 +#define I2CM_FM_SCL_CONFIG0 0xe4 +#define I2CM_CONFIG0 0xe8 +#define I2CM_CONTROL0 0xec +#define I2CM_STATUS0 0xf0 +#define I2CM_INTERFACE_CONTROL0 0xf4 +#define I2CM_ADDR 0xff000 +#define I2CM_SLVADDR 0xfe0 +#define I2CM_WR_MASK 0x1e +#define I2CM_EXT_READ BIT(4) +#define I2CM_SHORT_READ BIT(3) +#define I2CM_FM_READ BIT(2) +#define I2CM_FM_WRITE BIT(1) +#define I2CM_FM_EN BIT(0) +#define I2CM_INTERFACE_CONTROL1 0xf8 +#define I2CM_SEG_PTR 0x7f80 +#define I2CM_SEG_ADDR 0x7f +#define I2CM_INTERFACE_WRDATA_0_3 0xfc +#define I2CM_INTERFACE_WRDATA_4_7 0x100 +#define I2CM_INTERFACE_WRDATA_8_11 0x104 +#define I2CM_INTERFACE_WRDATA_12_15 0x108 +#define I2CM_INTERFACE_RDDATA_0_3 0x10c +#define I2CM_INTERFACE_RDDATA_4_7 0x110 +#define I2CM_INTERFACE_RDDATA_8_11 0x114 +#define I2CM_INTERFACE_RDDATA_12_15 0x118 +/* SCDC Registers */ +#define SCDC_CONFIG0 0x140 +#define SCDC_I2C_FM_EN BIT(12) +#define SCDC_UPD_FLAGS_AUTO_CLR BIT(6) +#define SCDC_UPD_FLAGS_POLL_EN BIT(4) +#define SCDC_CONTROL0 0x148 +#define SCDC_STATUS0 0x150 +#define STATUS_UPDATE BIT(0) +#define FRL_START BIT(4) +#define FLT_UPDATE BIT(5) +/* FLT Registers */ +#define FLT_CONFIG0 0x160 +#define FLT_CONFIG1 0x164 +#define FLT_CONFIG2 0x168 +#define FLT_CONTROL0 0x170 +/* Main Unit 2 Registers */ +#define MAINUNIT_STATUS0 0x180 +/* Video Interface Registers */ +#define VIDEO_INTERFACE_CONFIG0 0x800 +#define VIDEO_INTERFACE_CONFIG1 0x804 +#define VIDEO_INTERFACE_CONFIG2 0x808 +#define VIDEO_INTERFACE_CONTROL0 0x80c +#define VIDEO_INTERFACE_STATUS0 0x814 +/* Video Packing Registers */ +#define VIDEO_PACKING_CONFIG0 0x81c +/* Audio Interface Registers */ +#define AUDIO_INTERFACE_CONFIG0 0x820 +#define AUD_IF_SEL_MSK 0x3 +#define AUD_IF_SPDIF 0x2 +#define AUD_IF_I2S 0x1 +#define AUD_IF_PAI 0x0 +#define AUD_FIFO_INIT_ON_OVF_MSK BIT(2) +#define AUD_FIFO_INIT_ON_OVF_EN BIT(2) +#define I2S_LINES_EN_MSK GENMASK(7, 4) +#define I2S_LINES_EN(x) BIT((x) + 4) +#define I2S_BPCUV_RCV_MSK BIT(12) +#define I2S_BPCUV_RCV_EN BIT(12) +#define I2S_BPCUV_RCV_DIS 0 +#define SPDIF_LINES_EN GENMASK(19, 16) +#define AUD_FORMAT_MSK GENMASK(26, 24) +#define AUD_3DOBA (0x7 << 24) +#define AUD_3DASP (0x6 << 24) +#define AUD_MSOBA (0x5 << 24) +#define AUD_MSASP (0x4 << 24) +#define AUD_HBR (0x3 << 24) +#define AUD_DST (0x2 << 24) +#define AUD_OBA (0x1 << 24) +#define AUD_ASP (0x0 << 24) +#define AUDIO_INTERFACE_CONFIG1 0x824 +#define AUDIO_INTERFACE_CONTROL0 0x82c +#define AUDIO_FIFO_CLR_P BIT(0) +#define AUDIO_INTERFACE_STATUS0 0x834 +/* Frame Composer Registers */ +#define FRAME_COMPOSER_CONFIG0 0x840 +#define FRAME_COMPOSER_CONFIG1 0x844 +#define FRAME_COMPOSER_CONFIG2 0x848 +#define FRAME_COMPOSER_CONFIG3 0x84c +#define FRAME_COMPOSER_CONFIG4 0x850 +#define FRAME_COMPOSER_CONFIG5 0x854 +#define FRAME_COMPOSER_CONFIG6 0x858 +#define FRAME_COMPOSER_CONFIG7 0x85c +#define FRAME_COMPOSER_CONFIG8 0x860 +#define FRAME_COMPOSER_CONFIG9 0x864 +#define FRAME_COMPOSER_CONTROL0 0x86c +/* Video Monitor Registers */ +#define VIDEO_MONITOR_CONFIG0 0x880 +#define VIDEO_MONITOR_STATUS0 0x884 +#define VIDEO_MONITOR_STATUS1 0x888 +#define VIDEO_MONITOR_STATUS2 0x88c +#define VIDEO_MONITOR_STATUS3 0x890 +#define VIDEO_MONITOR_STATUS4 0x894 +#define VIDEO_MONITOR_STATUS5 0x898 +#define VIDEO_MONITOR_STATUS6 0x89c +/* HDCP2 Logic Registers */ +#define HDCP2LOGIC_CONFIG0 0x8e0 +#define HDCP2_BYPASS BIT(0) +#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4 +#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8 +/* HDCP14 Registers */ +#define HDCP14_CONFIG0 0x900 +#define HDCP14_CONFIG1 0x904 +#define HDCP14_CONFIG2 0x908 +#define HDCP14_CONFIG3 0x90c +#define HDCP14_KEY_SEED 0x914 +#define HDCP14_KEY_H 0x918 +#define HDCP14_KEY_L 0x91c +#define HDCP14_KEY_STATUS 0x920 +#define HDCP14_AKSV_H 0x924 +#define HDCP14_AKSV_L 0x928 +#define HDCP14_AN_H 0x92c +#define HDCP14_AN_L 0x930 +#define HDCP14_STATUS0 0x934 +#define HDCP14_STATUS1 0x938 +/* Scrambler Registers */ +#define SCRAMB_CONFIG0 0x960 +/* Video Configuration Registers */ +#define LINK_CONFIG0 0x968 +#define OPMODE_FRL_4LANES BIT(8) +#define OPMODE_DVI BIT(4) +#define OPMODE_FRL BIT(0) +/* TMDS FIFO Registers */ +#define TMDS_FIFO_CONFIG0 0x970 +#define TMDS_FIFO_CONTROL0 0x974 +/* FRL RSFEC Registers */ +#define FRL_RSFEC_CONFIG0 0xa20 +#define FRL_RSFEC_STATUS0 0xa30 +/* FRL Packetizer Registers */ +#define FRL_PKTZ_CONFIG0 0xa40 +#define FRL_PKTZ_CONTROL0 0xa44 +#define FRL_PKTZ_CONTROL1 0xa50 +#define FRL_PKTZ_STATUS1 0xa54 +/* Packet Scheduler Registers */ +#define PKTSCHED_CONFIG0 0xa80 +#define PKTSCHED_PRQUEUE0_CONFIG0 0xa84 +#define PKTSCHED_PRQUEUE1_CONFIG0 0xa88 +#define PKTSCHED_PRQUEUE2_CONFIG0 0xa8c +#define PKTSCHED_PRQUEUE2_CONFIG1 0xa90 +#define PKTSCHED_PRQUEUE2_CONFIG2 0xa94 +#define PKTSCHED_PKT_CONFIG0 0xa98 +#define PKTSCHED_PKT_CONFIG1 0xa9c +#define PKTSCHED_DRMI_FIELDRATE BIT(13) +#define PKTSCHED_AVI_FIELDRATE BIT(12) +#define PKTSCHED_PKT_CONFIG2 0xaa0 +#define PKTSCHED_PKT_CONFIG3 0xaa4 +#define PKTSCHED_PKT_EN 0xaa8 +#define PKTSCHED_DRMI_TX_EN BIT(17) +#define PKTSCHED_AUDI_TX_EN BIT(15) +#define PKTSCHED_AVI_TX_EN BIT(13) +#define PKTSCHED_EMP_CVTEM_TX_EN BIT(10) +#define PKTSCHED_AMD_TX_EN BIT(8) +#define PKTSCHED_GCP_TX_EN BIT(3) +#define PKTSCHED_AUDS_TX_EN BIT(2) +#define PKTSCHED_ACR_TX_EN BIT(1) +#define PKTSCHED_NULL_TX_EN BIT(0) +#define PKTSCHED_PKT_CONTROL0 0xaac +#define PKTSCHED_PKT_SEND 0xab0 +#define PKTSCHED_PKT_STATUS0 0xab4 +#define PKTSCHED_PKT_STATUS1 0xab8 +#define PKT_NULL_CONTENTS0 0xb00 +#define PKT_NULL_CONTENTS1 0xb04 +#define PKT_NULL_CONTENTS2 0xb08 +#define PKT_NULL_CONTENTS3 0xb0c +#define PKT_NULL_CONTENTS4 0xb10 +#define PKT_NULL_CONTENTS5 0xb14 +#define PKT_NULL_CONTENTS6 0xb18 +#define PKT_NULL_CONTENTS7 0xb1c +#define PKT_ACP_CONTENTS0 0xb20 +#define PKT_ACP_CONTENTS1 0xb24 +#define PKT_ACP_CONTENTS2 0xb28 +#define PKT_ACP_CONTENTS3 0xb2c +#define PKT_ACP_CONTENTS4 0xb30 +#define PKT_ACP_CONTENTS5 0xb34 +#define PKT_ACP_CONTENTS6 0xb38 +#define PKT_ACP_CONTENTS7 0xb3c +#define PKT_ISRC1_CONTENTS0 0xb40 +#define PKT_ISRC1_CONTENTS1 0xb44 +#define PKT_ISRC1_CONTENTS2 0xb48 +#define PKT_ISRC1_CONTENTS3 0xb4c +#define PKT_ISRC1_CONTENTS4 0xb50 +#define PKT_ISRC1_CONTENTS5 0xb54 +#define PKT_ISRC1_CONTENTS6 0xb58 +#define PKT_ISRC1_CONTENTS7 0xb5c +#define PKT_ISRC2_CONTENTS0 0xb60 +#define PKT_ISRC2_CONTENTS1 0xb64 +#define PKT_ISRC2_CONTENTS2 0xb68 +#define PKT_ISRC2_CONTENTS3 0xb6c +#define PKT_ISRC2_CONTENTS4 0xb70 +#define PKT_ISRC2_CONTENTS5 0xb74 +#define PKT_ISRC2_CONTENTS6 0xb78 +#define PKT_ISRC2_CONTENTS7 0xb7c +#define PKT_GMD_CONTENTS0 0xb80 +#define PKT_GMD_CONTENTS1 0xb84 +#define PKT_GMD_CONTENTS2 0xb88 +#define PKT_GMD_CONTENTS3 0xb8c +#define PKT_GMD_CONTENTS4 0xb90 +#define PKT_GMD_CONTENTS5 0xb94 +#define PKT_GMD_CONTENTS6 0xb98 +#define PKT_GMD_CONTENTS7 0xb9c +#define PKT_AMD_CONTENTS0 0xba0 +#define PKT_AMD_CONTENTS1 0xba4 +#define PKT_AMD_CONTENTS2 0xba8 +#define PKT_AMD_CONTENTS3 0xbac +#define PKT_AMD_CONTENTS4 0xbb0 +#define PKT_AMD_CONTENTS5 0xbb4 +#define PKT_AMD_CONTENTS6 0xbb8 +#define PKT_AMD_CONTENTS7 0xbbc +#define PKT_VSI_CONTENTS0 0xbc0 +#define PKT_VSI_CONTENTS1 0xbc4 +#define PKT_VSI_CONTENTS2 0xbc8 +#define PKT_VSI_CONTENTS3 0xbcc +#define PKT_VSI_CONTENTS4 0xbd0 +#define PKT_VSI_CONTENTS5 0xbd4 +#define PKT_VSI_CONTENTS6 0xbd8 +#define PKT_VSI_CONTENTS7 0xbdc +#define PKT_AVI_CONTENTS0 0xbe0 +#define HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT BIT(4) +#define HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR 0x04 +#define HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR 0x08 +#define HDMI_FC_AVICONF2_IT_CONTENT_VALID 0x80 +#define PKT_AVI_CONTENTS1 0xbe4 +#define PKT_AVI_CONTENTS2 0xbe8 +#define PKT_AVI_CONTENTS3 0xbec +#define PKT_AVI_CONTENTS4 0xbf0 +#define PKT_AVI_CONTENTS5 0xbf4 +#define PKT_AVI_CONTENTS6 0xbf8 +#define PKT_AVI_CONTENTS7 0xbfc +#define PKT_SPDI_CONTENTS0 0xc00 +#define PKT_SPDI_CONTENTS1 0xc04 +#define PKT_SPDI_CONTENTS2 0xc08 +#define PKT_SPDI_CONTENTS3 0xc0c +#define PKT_SPDI_CONTENTS4 0xc10 +#define PKT_SPDI_CONTENTS5 0xc14 +#define PKT_SPDI_CONTENTS6 0xc18 +#define PKT_SPDI_CONTENTS7 0xc1c +#define PKT_AUDI_CONTENTS0 0xc20 +#define PKT_AUDI_CONTENTS1 0xc24 +#define PKT_AUDI_CONTENTS2 0xc28 +#define PKT_AUDI_CONTENTS3 0xc2c +#define PKT_AUDI_CONTENTS4 0xc30 +#define PKT_AUDI_CONTENTS5 0xc34 +#define PKT_AUDI_CONTENTS6 0xc38 +#define PKT_AUDI_CONTENTS7 0xc3c +#define PKT_NVI_CONTENTS0 0xc40 +#define PKT_NVI_CONTENTS1 0xc44 +#define PKT_NVI_CONTENTS2 0xc48 +#define PKT_NVI_CONTENTS3 0xc4c +#define PKT_NVI_CONTENTS4 0xc50 +#define PKT_NVI_CONTENTS5 0xc54 +#define PKT_NVI_CONTENTS6 0xc58 +#define PKT_NVI_CONTENTS7 0xc5c +#define PKT_DRMI_CONTENTS0 0xc60 +#define PKT_DRMI_CONTENTS1 0xc64 +#define PKT_DRMI_CONTENTS2 0xc68 +#define PKT_DRMI_CONTENTS3 0xc6c +#define PKT_DRMI_CONTENTS4 0xc70 +#define PKT_DRMI_CONTENTS5 0xc74 +#define PKT_DRMI_CONTENTS6 0xc78 +#define PKT_DRMI_CONTENTS7 0xc7c +#define PKT_GHDMI1_CONTENTS0 0xc80 +#define PKT_GHDMI1_CONTENTS1 0xc84 +#define PKT_GHDMI1_CONTENTS2 0xc88 +#define PKT_GHDMI1_CONTENTS3 0xc8c +#define PKT_GHDMI1_CONTENTS4 0xc90 +#define PKT_GHDMI1_CONTENTS5 0xc94 +#define PKT_GHDMI1_CONTENTS6 0xc98 +#define PKT_GHDMI1_CONTENTS7 0xc9c +#define PKT_GHDMI2_CONTENTS0 0xca0 +#define PKT_GHDMI2_CONTENTS1 0xca4 +#define PKT_GHDMI2_CONTENTS2 0xca8 +#define PKT_GHDMI2_CONTENTS3 0xcac +#define PKT_GHDMI2_CONTENTS4 0xcb0 +#define PKT_GHDMI2_CONTENTS5 0xcb4 +#define PKT_GHDMI2_CONTENTS6 0xcb8 +#define PKT_GHDMI2_CONTENTS7 0xcbc +/* EMP Packetizer Registers */ +#define PKT_EMP_CONFIG0 0xce0 +#define PKT_EMP_CONTROL0 0xcec +#define PKT_EMP_CONTROL1 0xcf0 +#define PKT_EMP_CONTROL2 0xcf4 +#define PKT_EMP_VTEM_CONTENTS0 0xd00 +#define PKT_EMP_VTEM_CONTENTS1 0xd04 +#define PKT_EMP_VTEM_CONTENTS2 0xd08 +#define PKT_EMP_VTEM_CONTENTS3 0xd0c +#define PKT_EMP_VTEM_CONTENTS4 0xd10 +#define PKT_EMP_VTEM_CONTENTS5 0xd14 +#define PKT_EMP_VTEM_CONTENTS6 0xd18 +#define PKT_EMP_VTEM_CONTENTS7 0xd1c +#define PKT0_EMP_CVTEM_CONTENTS0 0xd20 +#define PKT0_EMP_CVTEM_CONTENTS1 0xd24 +#define PKT0_EMP_CVTEM_CONTENTS2 0xd28 +#define PKT0_EMP_CVTEM_CONTENTS3 0xd2c +#define PKT0_EMP_CVTEM_CONTENTS4 0xd30 +#define PKT0_EMP_CVTEM_CONTENTS5 0xd34 +#define PKT0_EMP_CVTEM_CONTENTS6 0xd38 +#define PKT0_EMP_CVTEM_CONTENTS7 0xd3c +#define PKT1_EMP_CVTEM_CONTENTS0 0xd40 +#define PKT1_EMP_CVTEM_CONTENTS1 0xd44 +#define PKT1_EMP_CVTEM_CONTENTS2 0xd48 +#define PKT1_EMP_CVTEM_CONTENTS3 0xd4c +#define PKT1_EMP_CVTEM_CONTENTS4 0xd50 +#define PKT1_EMP_CVTEM_CONTENTS5 0xd54 +#define PKT1_EMP_CVTEM_CONTENTS6 0xd58 +#define PKT1_EMP_CVTEM_CONTENTS7 0xd5c +#define PKT2_EMP_CVTEM_CONTENTS0 0xd60 +#define PKT2_EMP_CVTEM_CONTENTS1 0xd64 +#define PKT2_EMP_CVTEM_CONTENTS2 0xd68 +#define PKT2_EMP_CVTEM_CONTENTS3 0xd6c +#define PKT2_EMP_CVTEM_CONTENTS4 0xd70 +#define PKT2_EMP_CVTEM_CONTENTS5 0xd74 +#define PKT2_EMP_CVTEM_CONTENTS6 0xd78 +#define PKT2_EMP_CVTEM_CONTENTS7 0xd7c +#define PKT3_EMP_CVTEM_CONTENTS0 0xd80 +#define PKT3_EMP_CVTEM_CONTENTS1 0xd84 +#define PKT3_EMP_CVTEM_CONTENTS2 0xd88 +#define PKT3_EMP_CVTEM_CONTENTS3 0xd8c +#define PKT3_EMP_CVTEM_CONTENTS4 0xd90 +#define PKT3_EMP_CVTEM_CONTENTS5 0xd94 +#define PKT3_EMP_CVTEM_CONTENTS6 0xd98 +#define PKT3_EMP_CVTEM_CONTENTS7 0xd9c +#define PKT4_EMP_CVTEM_CONTENTS0 0xda0 +#define PKT4_EMP_CVTEM_CONTENTS1 0xda4 +#define PKT4_EMP_CVTEM_CONTENTS2 0xda8 +#define PKT4_EMP_CVTEM_CONTENTS3 0xdac +#define PKT4_EMP_CVTEM_CONTENTS4 0xdb0 +#define PKT4_EMP_CVTEM_CONTENTS5 0xdb4 +#define PKT4_EMP_CVTEM_CONTENTS6 0xdb8 +#define PKT4_EMP_CVTEM_CONTENTS7 0xdbc +#define PKT5_EMP_CVTEM_CONTENTS0 0xdc0 +#define PKT5_EMP_CVTEM_CONTENTS1 0xdc4 +#define PKT5_EMP_CVTEM_CONTENTS2 0xdc8 +#define PKT5_EMP_CVTEM_CONTENTS3 0xdcc +#define PKT5_EMP_CVTEM_CONTENTS4 0xdd0 +#define PKT5_EMP_CVTEM_CONTENTS5 0xdd4 +#define PKT5_EMP_CVTEM_CONTENTS6 0xdd8 +#define PKT5_EMP_CVTEM_CONTENTS7 0xddc +/* Audio Packetizer Registers */ +#define AUDPKT_CONTROL0 0xe20 +#define AUDPKT_PBIT_FORCE_EN_MASK BIT(12) +#define AUDPKT_PBIT_FORCE_EN BIT(12) +#define AUDPKT_CHSTATUS_OVR_EN_MASK BIT(0) +#define AUDPKT_CHSTATUS_OVR_EN BIT(0) +#define AUDPKT_CONTROL1 0xe24 +#define AUDPKT_ACR_CONTROL0 0xe40 +#define AUDPKT_ACR_N_VALUE 0xfffff +#define AUDPKT_ACR_CONTROL1 0xe44 +#define AUDPKT_ACR_CTS_OVR_VAL_MSK GENMASK(23, 4) +#define AUDPKT_ACR_CTS_OVR_VAL(x) ((x) << 4) +#define AUDPKT_ACR_CTS_OVR_EN_MSK BIT(1) +#define AUDPKT_ACR_CTS_OVR_EN BIT(1) +#define AUDPKT_ACR_STATUS0 0xe4c +#define AUDPKT_CHSTATUS_OVR0 0xe60 +#define AUDPKT_CHSTATUS_OVR1 0xe64 +/* IEC60958 Byte 3: Sampleing frenuency Bits 24 to 27 */ +#define AUDPKT_CHSTATUS_SR_MASK GENMASK(3, 0) +#define AUDPKT_CHSTATUS_SR_22050 0x4 +#define AUDPKT_CHSTATUS_SR_24000 0x6 +#define AUDPKT_CHSTATUS_SR_32000 0x3 +#define AUDPKT_CHSTATUS_SR_44100 0x0 +#define AUDPKT_CHSTATUS_SR_48000 0x2 +#define AUDPKT_CHSTATUS_SR_88200 0x8 +#define AUDPKT_CHSTATUS_SR_96000 0xa +#define AUDPKT_CHSTATUS_SR_176400 0xc +#define AUDPKT_CHSTATUS_SR_192000 0xe +#define AUDPKT_CHSTATUS_SR_768000 0x9 +#define AUDPKT_CHSTATUS_SR_NOT_INDICATED 0x1 +/* IEC60958 Byte 4: Original Sampleing frenuency Bits 36 to 39 */ +#define AUDPKT_CHSTATUS_0SR_MASK GENMASK(15, 12) +#define AUDPKT_CHSTATUS_OSR_8000 0x6 +#define AUDPKT_CHSTATUS_OSR_11025 0xa +#define AUDPKT_CHSTATUS_OSR_12000 0x2 +#define AUDPKT_CHSTATUS_OSR_16000 0x8 +#define AUDPKT_CHSTATUS_OSR_22050 0xb +#define AUDPKT_CHSTATUS_OSR_24000 0x9 +#define AUDPKT_CHSTATUS_OSR_32000 0xc +#define AUDPKT_CHSTATUS_OSR_44100 0xf +#define AUDPKT_CHSTATUS_OSR_48000 0xd +#define AUDPKT_CHSTATUS_OSR_88200 0x7 +#define AUDPKT_CHSTATUS_OSR_96000 0x5 +#define AUDPKT_CHSTATUS_OSR_176400 0x3 +#define AUDPKT_CHSTATUS_OSR_192000 0x1 +#define AUDPKT_CHSTATUS_OSR_NOT_INDICATED 0x0 +#define AUDPKT_CHSTATUS_OVR2 0xe68 +#define AUDPKT_CHSTATUS_OVR3 0xe6c +#define AUDPKT_CHSTATUS_OVR4 0xe70 +#define AUDPKT_CHSTATUS_OVR5 0xe74 +#define AUDPKT_CHSTATUS_OVR6 0xe78 +#define AUDPKT_CHSTATUS_OVR7 0xe7c +#define AUDPKT_CHSTATUS_OVR8 0xe80 +#define AUDPKT_CHSTATUS_OVR9 0xe84 +#define AUDPKT_CHSTATUS_OVR10 0xe88 +#define AUDPKT_CHSTATUS_OVR11 0xe8c +#define AUDPKT_CHSTATUS_OVR12 0xe90 +#define AUDPKT_CHSTATUS_OVR13 0xe94 +#define AUDPKT_CHSTATUS_OVR14 0xe98 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC0 0xea0 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC1 0xea4 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC2 0xea8 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC3 0xeac +#define AUDPKT_USRDATA_OVR_MSG_GENERIC4 0xeb0 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC5 0xeb4 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC6 0xeb8 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC7 0xebc +#define AUDPKT_USRDATA_OVR_MSG_GENERIC8 0xec0 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC9 0xec4 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC10 0xec8 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC11 0xecc +#define AUDPKT_USRDATA_OVR_MSG_GENERIC12 0xed0 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC13 0xed4 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC14 0xed8 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC15 0xedc +#define AUDPKT_USRDATA_OVR_MSG_GENERIC16 0xee0 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC17 0xee4 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC18 0xee8 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC19 0xeec +#define AUDPKT_USRDATA_OVR_MSG_GENERIC20 0xef0 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC21 0xef4 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC22 0xef8 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC23 0xefc +#define AUDPKT_USRDATA_OVR_MSG_GENERIC24 0xf00 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC25 0xf04 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC26 0xf08 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC27 0xf0c +#define AUDPKT_USRDATA_OVR_MSG_GENERIC28 0xf10 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC29 0xf14 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC30 0xf18 +#define AUDPKT_USRDATA_OVR_MSG_GENERIC31 0xf1c +#define AUDPKT_USRDATA_OVR_MSG_GENERIC32 0xf20 +#define AUDPKT_VBIT_OVR0 0xf24 +/* CEC Registers */ +#define CEC_TX_CONTROL 0x1000 +#define CEC_STATUS 0x1004 +#define CEC_CONFIG 0x1008 +#define CEC_ADDR 0x100c +#define CEC_TX_COUNT 0x1020 +#define CEC_TX_DATA3_0 0x1024 +#define CEC_TX_DATA7_4 0x1028 +#define CEC_TX_DATA11_8 0x102c +#define CEC_TX_DATA15_12 0x1030 +#define CEC_RX_COUNT_STATUS 0x1040 +#define CEC_RX_DATA3_0 0x1044 +#define CEC_RX_DATA7_4 0x1048 +#define CEC_RX_DATA11_8 0x104c +#define CEC_RX_DATA15_12 0x1050 +#define CEC_LOCK_CONTROL 0x1054 +#define CEC_RXQUAL_BITTIME_CONFIG 0x1060 +#define CEC_RX_BITTIME_CONFIG 0x1064 +#define CEC_TX_BITTIME_CONFIG 0x1068 +/* eARC RX CMDC Registers */ +#define EARCRX_CMDC_CONFIG0 0x1800 +#define EARCRX_XACTREAD_STOP_CFG BIT(26) +#define EARCRX_XACTREAD_RETRY_CFG BIT(25) +#define EARCRX_CMDC_DSCVR_EARCVALID0_TO_DISC1 BIT(24) +#define EARCRX_CMDC_XACT_RESTART_EN BIT(18) +#define EARCRX_CMDC_CONFIG1 0x1804 +#define EARCRX_CMDC_CONTROL 0x1808 +#define EARCRX_CMDC_HEARTBEAT_LOSS_EN BIT(4) +#define EARCRX_CMDC_DISCOVERY_EN BIT(3) +#define EARCRX_CONNECTOR_HPD BIT(1) +#define EARCRX_CMDC_WHITELIST0_CONFIG 0x180c +#define EARCRX_CMDC_WHITELIST1_CONFIG 0x1810 +#define EARCRX_CMDC_WHITELIST2_CONFIG 0x1814 +#define EARCRX_CMDC_WHITELIST3_CONFIG 0x1818 +#define EARCRX_CMDC_STATUS 0x181c +#define EARCRX_CMDC_XACT_INFO 0x1820 +#define EARCRX_CMDC_XACT_ACTION 0x1824 +#define EARCRX_CMDC_HEARTBEAT_RXSTAT_SE 0x1828 +#define EARCRX_CMDC_HEARTBEAT_STATUS 0x182c +#define EARCRX_CMDC_XACT_WR0 0x1840 +#define EARCRX_CMDC_XACT_WR1 0x1844 +#define EARCRX_CMDC_XACT_WR2 0x1848 +#define EARCRX_CMDC_XACT_WR3 0x184c +#define EARCRX_CMDC_XACT_WR4 0x1850 +#define EARCRX_CMDC_XACT_WR5 0x1854 +#define EARCRX_CMDC_XACT_WR6 0x1858 +#define EARCRX_CMDC_XACT_WR7 0x185c +#define EARCRX_CMDC_XACT_WR8 0x1860 +#define EARCRX_CMDC_XACT_WR9 0x1864 +#define EARCRX_CMDC_XACT_WR10 0x1868 +#define EARCRX_CMDC_XACT_WR11 0x186c +#define EARCRX_CMDC_XACT_WR12 0x1870 +#define EARCRX_CMDC_XACT_WR13 0x1874 +#define EARCRX_CMDC_XACT_WR14 0x1878 +#define EARCRX_CMDC_XACT_WR15 0x187c +#define EARCRX_CMDC_XACT_WR16 0x1880 +#define EARCRX_CMDC_XACT_WR17 0x1884 +#define EARCRX_CMDC_XACT_WR18 0x1888 +#define EARCRX_CMDC_XACT_WR19 0x188c +#define EARCRX_CMDC_XACT_WR20 0x1890 +#define EARCRX_CMDC_XACT_WR21 0x1894 +#define EARCRX_CMDC_XACT_WR22 0x1898 +#define EARCRX_CMDC_XACT_WR23 0x189c +#define EARCRX_CMDC_XACT_WR24 0x18a0 +#define EARCRX_CMDC_XACT_WR25 0x18a4 +#define EARCRX_CMDC_XACT_WR26 0x18a8 +#define EARCRX_CMDC_XACT_WR27 0x18ac +#define EARCRX_CMDC_XACT_WR28 0x18b0 +#define EARCRX_CMDC_XACT_WR29 0x18b4 +#define EARCRX_CMDC_XACT_WR30 0x18b8 +#define EARCRX_CMDC_XACT_WR31 0x18bc +#define EARCRX_CMDC_XACT_WR32 0x18c0 +#define EARCRX_CMDC_XACT_WR33 0x18c4 +#define EARCRX_CMDC_XACT_WR34 0x18c8 +#define EARCRX_CMDC_XACT_WR35 0x18cc +#define EARCRX_CMDC_XACT_WR36 0x18d0 +#define EARCRX_CMDC_XACT_WR37 0x18d4 +#define EARCRX_CMDC_XACT_WR38 0x18d8 +#define EARCRX_CMDC_XACT_WR39 0x18dc +#define EARCRX_CMDC_XACT_WR40 0x18e0 +#define EARCRX_CMDC_XACT_WR41 0x18e4 +#define EARCRX_CMDC_XACT_WR42 0x18e8 +#define EARCRX_CMDC_XACT_WR43 0x18ec +#define EARCRX_CMDC_XACT_WR44 0x18f0 +#define EARCRX_CMDC_XACT_WR45 0x18f4 +#define EARCRX_CMDC_XACT_WR46 0x18f8 +#define EARCRX_CMDC_XACT_WR47 0x18fc +#define EARCRX_CMDC_XACT_WR48 0x1900 +#define EARCRX_CMDC_XACT_WR49 0x1904 +#define EARCRX_CMDC_XACT_WR50 0x1908 +#define EARCRX_CMDC_XACT_WR51 0x190c +#define EARCRX_CMDC_XACT_WR52 0x1910 +#define EARCRX_CMDC_XACT_WR53 0x1914 +#define EARCRX_CMDC_XACT_WR54 0x1918 +#define EARCRX_CMDC_XACT_WR55 0x191c +#define EARCRX_CMDC_XACT_WR56 0x1920 +#define EARCRX_CMDC_XACT_WR57 0x1924 +#define EARCRX_CMDC_XACT_WR58 0x1928 +#define EARCRX_CMDC_XACT_WR59 0x192c +#define EARCRX_CMDC_XACT_WR60 0x1930 +#define EARCRX_CMDC_XACT_WR61 0x1934 +#define EARCRX_CMDC_XACT_WR62 0x1938 +#define EARCRX_CMDC_XACT_WR63 0x193c +#define EARCRX_CMDC_XACT_WR64 0x1940 +#define EARCRX_CMDC_XACT_RD0 0x1960 +#define EARCRX_CMDC_XACT_RD1 0x1964 +#define EARCRX_CMDC_XACT_RD2 0x1968 +#define EARCRX_CMDC_XACT_RD3 0x196c +#define EARCRX_CMDC_XACT_RD4 0x1970 +#define EARCRX_CMDC_XACT_RD5 0x1974 +#define EARCRX_CMDC_XACT_RD6 0x1978 +#define EARCRX_CMDC_XACT_RD7 0x197c +#define EARCRX_CMDC_XACT_RD8 0x1980 +#define EARCRX_CMDC_XACT_RD9 0x1984 +#define EARCRX_CMDC_XACT_RD10 0x1988 +#define EARCRX_CMDC_XACT_RD11 0x198c +#define EARCRX_CMDC_XACT_RD12 0x1990 +#define EARCRX_CMDC_XACT_RD13 0x1994 +#define EARCRX_CMDC_XACT_RD14 0x1998 +#define EARCRX_CMDC_XACT_RD15 0x199c +#define EARCRX_CMDC_XACT_RD16 0x19a0 +#define EARCRX_CMDC_XACT_RD17 0x19a4 +#define EARCRX_CMDC_XACT_RD18 0x19a8 +#define EARCRX_CMDC_XACT_RD19 0x19ac +#define EARCRX_CMDC_XACT_RD20 0x19b0 +#define EARCRX_CMDC_XACT_RD21 0x19b4 +#define EARCRX_CMDC_XACT_RD22 0x19b8 +#define EARCRX_CMDC_XACT_RD23 0x19bc +#define EARCRX_CMDC_XACT_RD24 0x19c0 +#define EARCRX_CMDC_XACT_RD25 0x19c4 +#define EARCRX_CMDC_XACT_RD26 0x19c8 +#define EARCRX_CMDC_XACT_RD27 0x19cc +#define EARCRX_CMDC_XACT_RD28 0x19d0 +#define EARCRX_CMDC_XACT_RD29 0x19d4 +#define EARCRX_CMDC_XACT_RD30 0x19d8 +#define EARCRX_CMDC_XACT_RD31 0x19dc +#define EARCRX_CMDC_XACT_RD32 0x19e0 +#define EARCRX_CMDC_XACT_RD33 0x19e4 +#define EARCRX_CMDC_XACT_RD34 0x19e8 +#define EARCRX_CMDC_XACT_RD35 0x19ec +#define EARCRX_CMDC_XACT_RD36 0x19f0 +#define EARCRX_CMDC_XACT_RD37 0x19f4 +#define EARCRX_CMDC_XACT_RD38 0x19f8 +#define EARCRX_CMDC_XACT_RD39 0x19fc +#define EARCRX_CMDC_XACT_RD40 0x1a00 +#define EARCRX_CMDC_XACT_RD41 0x1a04 +#define EARCRX_CMDC_XACT_RD42 0x1a08 +#define EARCRX_CMDC_XACT_RD43 0x1a0c +#define EARCRX_CMDC_XACT_RD44 0x1a10 +#define EARCRX_CMDC_XACT_RD45 0x1a14 +#define EARCRX_CMDC_XACT_RD46 0x1a18 +#define EARCRX_CMDC_XACT_RD47 0x1a1c +#define EARCRX_CMDC_XACT_RD48 0x1a20 +#define EARCRX_CMDC_XACT_RD49 0x1a24 +#define EARCRX_CMDC_XACT_RD50 0x1a28 +#define EARCRX_CMDC_XACT_RD51 0x1a2c +#define EARCRX_CMDC_XACT_RD52 0x1a30 +#define EARCRX_CMDC_XACT_RD53 0x1a34 +#define EARCRX_CMDC_XACT_RD54 0x1a38 +#define EARCRX_CMDC_XACT_RD55 0x1a3c +#define EARCRX_CMDC_XACT_RD56 0x1a40 +#define EARCRX_CMDC_XACT_RD57 0x1a44 +#define EARCRX_CMDC_XACT_RD58 0x1a48 +#define EARCRX_CMDC_XACT_RD59 0x1a4c +#define EARCRX_CMDC_XACT_RD60 0x1a50 +#define EARCRX_CMDC_XACT_RD61 0x1a54 +#define EARCRX_CMDC_XACT_RD62 0x1a58 +#define EARCRX_CMDC_XACT_RD63 0x1a5c +#define EARCRX_CMDC_XACT_RD64 0x1a60 +#define EARCRX_CMDC_SYNC_CONFIG 0x1b00 +/* eARC RX DMAC Registers */ +#define EARCRX_DMAC_PHY_CONTROL 0x1c00 +#define EARCRX_DMAC_CONFIG 0x1c08 +#define EARCRX_DMAC_CONTROL0 0x1c0c +#define EARCRX_DMAC_AUDIO_EN BIT(1) +#define EARCRX_DMAC_EN BIT(0) +#define EARCRX_DMAC_CONTROL1 0x1c10 +#define EARCRX_DMAC_STATUS 0x1c14 +#define EARCRX_DMAC_CHSTATUS0 0x1c18 +#define EARCRX_DMAC_CHSTATUS1 0x1c1c +#define EARCRX_DMAC_CHSTATUS2 0x1c20 +#define EARCRX_DMAC_CHSTATUS3 0x1c24 +#define EARCRX_DMAC_CHSTATUS4 0x1c28 +#define EARCRX_DMAC_CHSTATUS5 0x1c2c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC0 0x1c30 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC1 0x1c34 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC2 0x1c38 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC3 0x1c3c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC4 0x1c40 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC5 0x1c44 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC6 0x1c48 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC7 0x1c4c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC8 0x1c50 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC9 0x1c54 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC10 0x1c58 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_AC11 0x1c5c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT0 0x1c60 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT1 0x1c64 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT2 0x1c68 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT3 0x1c6c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT4 0x1c70 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT5 0x1c74 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT6 0x1c78 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT7 0x1c7c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT8 0x1c80 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT9 0x1c84 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT10 0x1c88 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC1_PKT11 0x1c8c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT0 0x1c90 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT1 0x1c94 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT2 0x1c98 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT3 0x1c9c +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT4 0x1ca0 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT5 0x1ca4 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT6 0x1ca8 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT7 0x1cac +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT8 0x1cb0 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT9 0x1cb4 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT10 0x1cb8 +#define EARCRX_DMAC_USRDATA_MSG_HDMI_ISRC2_PKT11 0x1cbc +#define EARCRX_DMAC_USRDATA_MSG_GENERIC0 0x1cc0 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC1 0x1cc4 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC2 0x1cc8 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC3 0x1ccc +#define EARCRX_DMAC_USRDATA_MSG_GENERIC4 0x1cd0 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC5 0x1cd4 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC6 0x1cd8 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC7 0x1cdc +#define EARCRX_DMAC_USRDATA_MSG_GENERIC8 0x1ce0 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC9 0x1ce4 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC10 0x1ce8 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC11 0x1cec +#define EARCRX_DMAC_USRDATA_MSG_GENERIC12 0x1cf0 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC13 0x1cf4 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC14 0x1cf8 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC15 0x1cfc +#define EARCRX_DMAC_USRDATA_MSG_GENERIC16 0x1d00 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC17 0x1d04 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC18 0x1d08 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC19 0x1d0c +#define EARCRX_DMAC_USRDATA_MSG_GENERIC20 0x1d10 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC21 0x1d14 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC22 0x1d18 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC23 0x1d1c +#define EARCRX_DMAC_USRDATA_MSG_GENERIC24 0x1d20 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC25 0x1d24 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC26 0x1d28 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC27 0x1d2c +#define EARCRX_DMAC_USRDATA_MSG_GENERIC28 0x1d30 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC29 0x1d34 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC30 0x1d38 +#define EARCRX_DMAC_USRDATA_MSG_GENERIC31 0x1d3c +#define EARCRX_DMAC_USRDATA_MSG_GENERIC32 0x1d40 +#define EARCRX_DMAC_CHSTATUS_STREAMER0 0x1d44 +#define EARCRX_DMAC_CHSTATUS_STREAMER1 0x1d48 +#define EARCRX_DMAC_CHSTATUS_STREAMER2 0x1d4c +#define EARCRX_DMAC_CHSTATUS_STREAMER3 0x1d50 +#define EARCRX_DMAC_CHSTATUS_STREAMER4 0x1d54 +#define EARCRX_DMAC_CHSTATUS_STREAMER5 0x1d58 +#define EARCRX_DMAC_CHSTATUS_STREAMER6 0x1d5c +#define EARCRX_DMAC_CHSTATUS_STREAMER7 0x1d60 +#define EARCRX_DMAC_CHSTATUS_STREAMER8 0x1d64 +#define EARCRX_DMAC_CHSTATUS_STREAMER9 0x1d68 +#define EARCRX_DMAC_CHSTATUS_STREAMER10 0x1d6c +#define EARCRX_DMAC_CHSTATUS_STREAMER11 0x1d70 +#define EARCRX_DMAC_CHSTATUS_STREAMER12 0x1d74 +#define EARCRX_DMAC_CHSTATUS_STREAMER13 0x1d78 +#define EARCRX_DMAC_CHSTATUS_STREAMER14 0x1d7c +#define EARCRX_DMAC_USRDATA_STREAMER0 0x1d80 +/* Main Unit Interrupt Registers */ +#define MAIN_INTVEC_INDEX 0x3000 +#define MAINUNIT_0_INT_STATUS 0x3010 +#define MAINUNIT_0_INT_MASK_N 0x3014 +#define MAINUNIT_0_INT_CLEAR 0x3018 +#define MAINUNIT_0_INT_FORCE 0x301c +#define MAINUNIT_1_INT_STATUS 0x3020 +#define FLT_EXIT_TO_LTSL_IRQ BIT(22) +#define FLT_EXIT_TO_LTS4_IRQ BIT(21) +#define FLT_EXIT_TO_LTSP_IRQ BIT(20) +#define SCDC_NACK_RCVD_IRQ BIT(12) +#define SCDC_RR_REPLY_STOP_IRQ BIT(11) +#define SCDC_UPD_FLAGS_CLR_IRQ BIT(10) +#define SCDC_UPD_FLAGS_CHG_IRQ BIT(9) +#define SCDC_UPD_FLAGS_RD_IRQ BIT(8) +#define I2CM_NACK_RCVD_IRQ BIT(2) +#define I2CM_READ_REQUEST_IRQ BIT(1) +#define I2CM_OP_DONE_IRQ BIT(0) +#define MAINUNIT_1_INT_MASK_N 0x3024 +#define I2CM_NACK_RCVD_MASK_N BIT(2) +#define I2CM_READ_REQUEST_MASK_N BIT(1) +#define I2CM_OP_DONE_MASK_N BIT(0) +#define MAINUNIT_1_INT_CLEAR 0x3028 +#define I2CM_NACK_RCVD_CLEAR BIT(2) +#define I2CM_READ_REQUEST_CLEAR BIT(1) +#define I2CM_OP_DONE_CLEAR BIT(0) +#define MAINUNIT_1_INT_FORCE 0x302c +/* AVPUNIT Interrupt Registers */ +#define AVP_INTVEC_INDEX 0x3800 +#define AVP_0_INT_STATUS 0x3810 +#define AVP_0_INT_MASK_N 0x3814 +#define AVP_0_INT_CLEAR 0x3818 +#define AVP_0_INT_FORCE 0x381c +#define AVP_1_INT_STATUS 0x3820 +#define AVP_1_INT_MASK_N 0x3824 +#define HDCP14_AUTH_CHG_MASK_N BIT(6) +#define AVP_1_INT_CLEAR 0x3828 +#define AVP_1_INT_FORCE 0x382c +#define AVP_2_INT_STATUS 0x3830 +#define AVP_2_INT_MASK_N 0x3834 +#define AVP_2_INT_CLEAR 0x3838 +#define AVP_2_INT_FORCE 0x383c +#define AVP_3_INT_STATUS 0x3840 +#define AVP_3_INT_MASK_N 0x3844 +#define AVP_3_INT_CLEAR 0x3848 +#define AVP_3_INT_FORCE 0x384c +#define AVP_4_INT_STATUS 0x3850 +#define AVP_4_INT_MASK_N 0x3854 +#define AVP_4_INT_CLEAR 0x3858 +#define AVP_4_INT_FORCE 0x385c +#define AVP_5_INT_STATUS 0x3860 +#define AVP_5_INT_MASK_N 0x3864 +#define AVP_5_INT_CLEAR 0x3868 +#define AVP_5_INT_FORCE 0x386c +#define AVP_6_INT_STATUS 0x3870 +#define AVP_6_INT_MASK_N 0x3874 +#define AVP_6_INT_CLEAR 0x3878 +#define AVP_6_INT_FORCE 0x387c +/* CEC Interrupt Registers */ +#define CEC_INT_STATUS 0x4000 +#define CEC_INT_MASK_N 0x4004 +#define CEC_INT_CLEAR 0x4008 +#define CEC_INT_FORCE 0x400c +/* eARC RX Interrupt Registers */ +#define EARCRX_INTVEC_INDEX 0x4800 +#define EARCRX_0_INT_STATUS 0x4810 +#define EARCRX_CMDC_DISCOVERY_TIMEOUT_IRQ BIT(9) +#define EARCRX_CMDC_DISCOVERY_DONE_IRQ BIT(8) +#define EARCRX_0_INT_MASK_N 0x4814 +#define EARCRX_0_INT_CLEAR 0x4818 +#define EARCRX_0_INT_FORCE 0x481c +#define EARCRX_1_INT_STATUS 0x4820 +#define EARCRX_1_INT_MASK_N 0x4824 +#define EARCRX_1_INT_CLEAR 0x4828 +#define EARCRX_1_INT_FORCE 0x482c + +#endif /* __DW_HDMI_QP_H__ */ diff --git a/include/drm/bridge/dw_hdmi_qp.h b/include/drm/bridge/dw_hdmi_qp.h new file mode 100644 index 0000000000000..e9be6d507ad9c --- /dev/null +++ b/include/drm/bridge/dw_hdmi_qp.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. + * Copyright (c) 2024 Collabora Ltd. + */ + +#ifndef __DW_HDMI_QP__ +#define __DW_HDMI_QP__ + +struct device; +struct drm_encoder; +struct dw_hdmi_qp; +struct platform_device; + +struct dw_hdmi_qp_phy_ops { + int (*init)(struct dw_hdmi_qp *hdmi, void *data); + void (*disable)(struct dw_hdmi_qp *hdmi, void *data); + enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, void *data); + void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data); +}; + +struct dw_hdmi_qp_plat_data { + const struct dw_hdmi_qp_phy_ops *phy_ops; + void *phy_data; + int main_irq; +}; + +struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, + struct drm_encoder *encoder, + const struct dw_hdmi_qp_plat_data *plat_data); +void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); +#endif /* __DW_HDMI_QP__ */ From c0d28332ff90f800ccd1a3b73cb50c3335b0286a Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 16 Oct 2024 23:06:52 +0300 Subject: [PATCH 02/90] dt-bindings: display: rockchip: Add schema for RK3588 HDMI TX Controller Rockchip RK3588 SoC integrates the Synopsys DesignWare HDMI 2.1 Quad-Pixel (QP) TX controller IP. Since this is a new IP block, quite different from those used in the previous generations of Rockchip SoCs, add a dedicated binding file. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Cristian Ciocaltea Link: https://patchwork.freedesktop.org/patch/msgid/20241016-b4-rk3588-bridge-upstream-v10-2-87ef92a6d14e@collabora.com Signed-off-by: Maxime Ripard --- .../rockchip/rockchip,rk3588-dw-hdmi-qp.yaml | 188 ++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml new file mode 100644 index 0000000000000..d8e761865f27e --- /dev/null +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml @@ -0,0 +1,188 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip DW HDMI QP TX Encoder + +maintainers: + - Cristian Ciocaltea + +description: | + Rockchip RK3588 SoC integrates the Synopsys DesignWare HDMI QP TX controller + IP and a HDMI/eDP TX Combo PHY based on a Samsung IP block, providing the + following features, among others: + + * Fixed Rate Link (FRL) + * Display Stream Compression (DSC) + * 4K@120Hz and 8K@60Hz video modes + * Variable Refresh Rate (VRR) including Quick Media Switching (QMS) + * Fast Vactive (FVA) + * SCDC I2C DDC access + * Multi-stream audio + * Enhanced Audio Return Channel (EARC) + +allOf: + - $ref: /schemas/sound/dai-common.yaml# + +properties: + compatible: + enum: + - rockchip,rk3588-dw-hdmi-qp + + reg: + maxItems: 1 + + clocks: + items: + - description: Peripheral/APB bus clock + - description: EARC RX biphase clock + - description: Reference clock + - description: Audio interface clock + - description: TMDS/FRL link clock + - description: Video datapath clock + + clock-names: + items: + - const: pclk + - const: earc + - const: ref + - const: aud + - const: hdp + - const: hclk_vo1 + + interrupts: + items: + - description: AVP Unit interrupt + - description: CEC interrupt + - description: eARC RX interrupt + - description: Main Unit interrupt + - description: HPD interrupt + + interrupt-names: + items: + - const: avp + - const: cec + - const: earc + - const: main + - const: hpd + + phys: + maxItems: 1 + description: The HDMI/eDP PHY + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Video port for RGB/YUV input. + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: Video port for HDMI/eDP output. + + required: + - port@0 + - port@1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 2 + + reset-names: + items: + - const: ref + - const: hdp + + "#sound-dai-cells": + const: 0 + + rockchip,grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Some HDMI QP related data is accessed through SYS GRF regs. + + rockchip,vo-grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Additional HDMI QP related data is accessed through VO GRF regs. + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + - phys + - ports + - resets + - reset-names + - rockchip,grf + - rockchip,vo-grf + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + hdmi@fde80000 { + compatible = "rockchip,rk3588-dw-hdmi-qp"; + reg = <0x0 0xfde80000 0x0 0x20000>; + clocks = <&cru PCLK_HDMITX0>, + <&cru CLK_HDMITX0_EARC>, + <&cru CLK_HDMITX0_REF>, + <&cru MCLK_I2S5_8CH_TX>, + <&cru CLK_HDMIHDP0>, + <&cru HCLK_VO1>; + clock-names = "pclk", "earc", "ref", "aud", "hdp", "hclk_vo1"; + interrupts = , + , + , + , + ; + interrupt-names = "avp", "cec", "earc", "main", "hpd"; + phys = <&hdptxphy_hdmi0>; + power-domains = <&power RK3588_PD_VO1>; + resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>; + reset-names = "ref", "hdp"; + rockchip,grf = <&sys_grf>; + rockchip,vo-grf = <&vo1_grf>; + #sound-dai-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + hdmi0_in_vp0: endpoint { + remote-endpoint = <&vp0_out_hdmi0>; + }; + }; + + port@1 { + reg = <1>; + + hdmi0_out_con0: endpoint { + remote-endpoint = <&hdmi_con0_in>; + }; + }; + }; + }; + }; From 128a9bf8ace290d86d2805c06f3b0e4cfab75de0 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Wed, 16 Oct 2024 23:06:53 +0300 Subject: [PATCH 03/90] drm/rockchip: Add basic RK3588 HDMI output support The RK3588 SoC family integrates the newer Synopsys DesignWare HDMI 2.1 Quad-Pixel (QP) TX controller IP and a HDMI/eDP TX Combo PHY based on a Samsung IP block. Add just the basic support for now, i.e. RGB output up to 4K@60Hz, without audio, CEC or any of the HDMI 2.1 specific features. Co-developed-by: Algea Cao Signed-off-by: Algea Cao Tested-by: Heiko Stuebner Reviewed-by: Maxime Ripard Signed-off-by: Cristian Ciocaltea Link: https://patchwork.freedesktop.org/patch/msgid/20241016-b4-rk3588-bridge-upstream-v10-3-87ef92a6d14e@collabora.com Signed-off-by: Maxime Ripard --- drivers/gpu/drm/rockchip/Kconfig | 9 + drivers/gpu/drm/rockchip/Makefile | 1 + .../gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 424 ++++++++++++++++++ drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 + drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 + 5 files changed, 437 insertions(+) create mode 100644 drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 258dd38e1a8e8..3ac579615749d 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -9,6 +9,7 @@ config DRM_ROCKCHIP select VIDEOMODE_HELPERS select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP select DRM_DW_HDMI if ROCKCHIP_DW_HDMI + select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI @@ -64,6 +65,14 @@ config ROCKCHIP_DW_HDMI enable HDMI on RK3288 or RK3399 based SoC, you should select this option. +config ROCKCHIP_DW_HDMI_QP + bool "Rockchip specific extensions for Synopsys DW HDMI QP" + select DRM_BRIDGE_CONNECTOR + help + This selects support for Rockchip SoC specific extensions + for the Synopsys DesignWare HDMI QP driver. If you want to + enable HDMI on RK3588 based SoC, you should select this option. + config ROCKCHIP_DW_MIPI_DSI bool "Rockchip specific extensions for Synopsys DW MIPI DSI" select GENERIC_PHY_MIPI_DPHY diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index 3ff7b21c04149..3eab662a5a1d6 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -11,6 +11,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o +rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c new file mode 100644 index 0000000000000..9c796ee4c303a --- /dev/null +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. + * Copyright (c) 2024 Collabora Ltd. + * + * Author: Algea Cao + * Author: Cristian Ciocaltea + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "rockchip_drm_drv.h" + +#define RK3588_GRF_SOC_CON2 0x0308 +#define RK3588_HDMI0_HPD_INT_MSK BIT(13) +#define RK3588_HDMI0_HPD_INT_CLR BIT(12) +#define RK3588_GRF_SOC_CON7 0x031c +#define RK3588_SET_HPD_PATH_MASK GENMASK(13, 12) +#define RK3588_GRF_SOC_STATUS1 0x0384 +#define RK3588_HDMI0_LEVEL_INT BIT(16) +#define RK3588_GRF_VO1_CON3 0x000c +#define RK3588_SCLIN_MASK BIT(9) +#define RK3588_SDAIN_MASK BIT(10) +#define RK3588_MODE_MASK BIT(11) +#define RK3588_I2S_SEL_MASK BIT(13) +#define RK3588_GRF_VO1_CON9 0x0024 +#define RK3588_HDMI0_GRANT_SEL BIT(10) + +#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16) +#define HOTPLUG_DEBOUNCE_MS 150 + +struct rockchip_hdmi_qp { + struct device *dev; + struct regmap *regmap; + struct regmap *vo_regmap; + struct rockchip_encoder encoder; + struct clk *ref_clk; + struct dw_hdmi_qp *hdmi; + struct phy *phy; + struct gpio_desc *enable_gpio; + struct delayed_work hpd_work; +}; + +static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder) +{ + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); + + return container_of(rkencoder, struct rockchip_hdmi_qp, encoder); +} + +static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder) +{ + struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder); + struct drm_crtc *crtc = encoder->crtc; + unsigned long long rate; + + /* Unconditionally switch to TMDS as FRL is not yet supported */ + gpiod_set_value(hdmi->enable_gpio, 1); + + if (crtc && crtc->state) { + rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode, + 8, HDMI_COLORSPACE_RGB); + clk_set_rate(hdmi->ref_clk, rate); + /* + * FIXME: Temporary workaround to pass pixel clock rate + * to the PHY driver until phy_configure_opts_hdmi + * becomes available in the PHY API. See also the related + * comment in rk_hdptx_phy_power_on() from + * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c + */ + phy_set_bus_width(hdmi->phy, rate / 100); + } +} + +static int +dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + + s->output_mode = ROCKCHIP_OUT_MODE_AAAA; + s->output_type = DRM_MODE_CONNECTOR_HDMIA; + + return 0; +} + +static const struct +drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = { + .enable = dw_hdmi_qp_rockchip_encoder_enable, + .atomic_check = dw_hdmi_qp_rockchip_encoder_atomic_check, +}; + +static int dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; + + return phy_power_on(hdmi->phy); +} + +static void dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp *dw_hdmi, + void *data) +{ + struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; + + phy_power_off(hdmi->phy); +} + +static enum drm_connector_status +dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; + u32 val; + + regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val); + + return val & RK3588_HDMI0_LEVEL_INT ? + connector_status_connected : connector_status_disconnected; +} + +static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; + + regmap_write(hdmi->regmap, + RK3588_GRF_SOC_CON2, + HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, + RK3588_HDMI0_HPD_INT_CLR | + RK3588_HDMI0_HPD_INT_MSK)); +} + +static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { + .init = dw_hdmi_qp_rk3588_phy_init, + .disable = dw_hdmi_qp_rk3588_phy_disable, + .read_hpd = dw_hdmi_qp_rk3588_read_hpd, + .setup_hpd = dw_hdmi_qp_rk3588_setup_hpd, +}; + +static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work) +{ + struct rockchip_hdmi_qp *hdmi = container_of(work, + struct rockchip_hdmi_qp, + hpd_work.work); + struct drm_device *drm = hdmi->encoder.encoder.dev; + bool changed; + + if (drm) { + changed = drm_helper_hpd_irq_event(drm); + if (changed) + drm_dbg(hdmi, "connector status changed\n"); + } +} + +static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id) +{ + struct rockchip_hdmi_qp *hdmi = dev_id; + u32 intr_stat, val; + + regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); + + if (intr_stat) { + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, + RK3588_HDMI0_HPD_INT_MSK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id) +{ + struct rockchip_hdmi_qp *hdmi = dev_id; + u32 intr_stat, val; + + regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); + if (!intr_stat) + return IRQ_NONE; + + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR, + RK3588_HDMI0_HPD_INT_CLR); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + + mod_delayed_work(system_wq, &hdmi->hpd_work, + msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); + + val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + + return IRQ_HANDLED; +} + +static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3588-dw-hdmi-qp", + .data = &rk3588_hdmi_phy_ops }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids); + +static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, + void *data) +{ + static const char * const clk_names[] = { + "pclk", "earc", "aud", "hdp", "hclk_vo1", + "ref" /* keep "ref" last */ + }; + struct platform_device *pdev = to_platform_device(dev); + struct dw_hdmi_qp_plat_data plat_data; + struct drm_device *drm = data; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct rockchip_hdmi_qp *hdmi; + struct clk *clk; + int ret, irq, i; + u32 val; + + if (!pdev->dev.of_node) + return -ENODEV; + + hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + plat_data.phy_ops = of_device_get_match_data(dev); + if (!plat_data.phy_ops) + return -ENODEV; + + plat_data.phy_data = hdmi; + hdmi->dev = &pdev->dev; + + encoder = &hdmi->encoder.encoder; + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + + rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, + dev->of_node, 0, 0); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) + return -EPROBE_DEFER; + + hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) { + drm_err(hdmi, "Unable to get rockchip,grf\n"); + return PTR_ERR(hdmi->regmap); + } + + hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,vo-grf"); + if (IS_ERR(hdmi->vo_regmap)) { + drm_err(hdmi, "Unable to get rockchip,vo-grf\n"); + return PTR_ERR(hdmi->vo_regmap); + } + + for (i = 0; i < ARRAY_SIZE(clk_names); i++) { + clk = devm_clk_get_enabled(hdmi->dev, clk_names[i]); + + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + if (ret != -EPROBE_DEFER) + drm_err(hdmi, "Failed to get %s clock: %d\n", + clk_names[i], ret); + return ret; + } + } + hdmi->ref_clk = clk; + + hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(hdmi->enable_gpio)) { + ret = PTR_ERR(hdmi->enable_gpio); + drm_err(hdmi, "Failed to request enable GPIO: %d\n", ret); + return ret; + } + + hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0); + if (IS_ERR(hdmi->phy)) { + ret = PTR_ERR(hdmi->phy); + if (ret != -EPROBE_DEFER) + drm_err(hdmi, "failed to get phy: %d\n", ret); + return ret; + } + + val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | + HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | + HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | + HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); + regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val); + + val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, + RK3588_SET_HPD_PATH_MASK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); + + val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, + RK3588_HDMI0_GRANT_SEL); + regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val); + + val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + + INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work); + + plat_data.main_irq = platform_get_irq_byname(pdev, "main"); + if (plat_data.main_irq < 0) + return plat_data.main_irq; + + irq = platform_get_irq_byname(pdev, "hpd"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(hdmi->dev, irq, + dw_hdmi_qp_rk3588_hardirq, + dw_hdmi_qp_rk3588_irq, + IRQF_SHARED, "dw-hdmi-qp-hpd", + hdmi); + if (ret) + return ret; + + drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs); + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); + + platform_set_drvdata(pdev, hdmi); + + hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data); + if (IS_ERR(hdmi->hdmi)) { + ret = PTR_ERR(hdmi->hdmi); + drm_encoder_cleanup(encoder); + return ret; + } + + connector = drm_bridge_connector_init(drm, encoder); + if (IS_ERR(connector)) { + ret = PTR_ERR(connector); + drm_err(hdmi, "failed to init bridge connector: %d\n", ret); + return ret; + } + + return drm_connector_attach_encoder(connector, encoder); +} + +static void dw_hdmi_qp_rockchip_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&hdmi->hpd_work); + + drm_encoder_cleanup(&hdmi->encoder.encoder); +} + +static const struct component_ops dw_hdmi_qp_rockchip_ops = { + .bind = dw_hdmi_qp_rockchip_bind, + .unbind = dw_hdmi_qp_rockchip_unbind, +}; + +static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops); +} + +static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops); +} + +static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev) +{ + struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev); + u32 val; + + val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) | + HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) | + HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) | + HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK); + regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val); + + val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK, + RK3588_SET_HPD_PATH_MASK); + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val); + + val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL, + RK3588_HDMI0_GRANT_SEL); + regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val); + + dw_hdmi_qp_resume(dev, hdmi->hdmi); + + if (hdmi->encoder.encoder.dev) + drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev); + + return 0; +} + +static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_qp_rockchip_resume) +}; + +struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = { + .probe = dw_hdmi_qp_rockchip_probe, + .remove = dw_hdmi_qp_rockchip_remove, + .driver = { + .name = "dwhdmiqp-rockchip", + .pm = &dw_hdmi_qp_rockchip_pm, + .of_match_table = dw_hdmi_qp_rockchip_dt_ids, + }, +}; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 2560b0eec9db0..585355de696b7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -532,6 +532,8 @@ static int __init rockchip_drm_init(void) ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver, CONFIG_ROCKCHIP_DW_HDMI); + ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver, + CONFIG_ROCKCHIP_DW_HDMI_QP); ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver, CONFIG_ROCKCHIP_DW_MIPI_DSI); ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 8d566fcd80a23..24b4ce5ceaf19 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -88,6 +88,7 @@ int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder, int rockchip_drm_endpoint_is_subdriver(struct device_node *ep); extern struct platform_driver cdn_dp_driver; extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; +extern struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver; extern struct platform_driver dw_mipi_dsi_rockchip_driver; extern struct platform_driver inno_hdmi_driver; extern struct platform_driver rockchip_dp_driver; From feda66ce0519311ee349fb002839e91eb8a5453f Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:15 +0200 Subject: [PATCH 04/90] drm/i915: Select DRM_CLIENT_SELECTION The Kconfig token DRM_CLIENT_SELECTION will make DRM clients available to drivers. Select it from i915. Signed-off-by: Thomas Zimmermann Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-2-tzimmermann@suse.de --- drivers/gpu/drm/i915/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index db400aad88fa6..1158a6b97f9a9 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -10,6 +10,7 @@ config DRM_I915 # the shmem_readpage() which depends upon tmpfs select SHMEM select TMPFS + select DRM_CLIENT_SELECTION select DRM_DISPLAY_DP_HELPER select DRM_DISPLAY_DSC_HELPER select DRM_DISPLAY_HDCP_HELPER From f1d730cbafeb7deb5fb6bab0b81a73a6d92efc58 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:16 +0200 Subject: [PATCH 05/90] drm/xe: Select DRM_CLIENT_SELECTION MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Kconfig token DRM_CLIENT_SELECTION will make DRM clients available to drivers. Select it from xe. Signed-off-by: Thomas Zimmermann Cc: Lucas De Marchi Cc: "Thomas Hellström" Cc: Rodrigo Vivi Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-3-tzimmermann@suse.de --- drivers/gpu/drm/xe/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig index ebd0879e04d44..bac96c0dd66e6 100644 --- a/drivers/gpu/drm/xe/Kconfig +++ b/drivers/gpu/drm/xe/Kconfig @@ -8,6 +8,7 @@ config DRM_XE select SHMEM select TMPFS select DRM_BUDDY + select DRM_CLIENT_SELECTION select DRM_EXEC select DRM_KMS_HELPER select DRM_KUNIT_TEST_HELPERS if DRM_XE_KUNIT_TEST != n From 67c40c9b2ec5f375bf78274d4e9ef0e3b8315bea Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:17 +0200 Subject: [PATCH 06/90] drm/fbdev-dma: Select FB_DEFERRED_IO Commit 808a40b69468 ("drm/fbdev-dma: Implement damage handling and deferred I/O") added deferred I/O for fbdev-dma. Also select the Kconfig symbol FB_DEFERRED_IO (via FB_DMAMEM_HELPERS_DEFERRED). Fixes build errors about missing fbdefio, such as drivers/gpu/drm/drm_fbdev_dma.c:218:26: error: 'struct drm_fb_helper' has no member named 'fbdefio' 218 | fb_helper->fbdefio.delay = HZ / 20; | ^~ drivers/gpu/drm/drm_fbdev_dma.c:219:26: error: 'struct drm_fb_helper' has no member named 'fbdefio' 219 | fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io; | ^~ drivers/gpu/drm/drm_fbdev_dma.c:221:21: error: 'struct fb_info' has no member named 'fbdefio' 221 | info->fbdefio = &fb_helper->fbdefio; | ^~ drivers/gpu/drm/drm_fbdev_dma.c:221:43: error: 'struct drm_fb_helper' has no member named 'fbdefio' 221 | info->fbdefio = &fb_helper->fbdefio; | ^~ Signed-off-by: Thomas Zimmermann Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202410050241.Mox9QRjP-lkp@intel.com/ Fixes: 808a40b69468 ("drm/fbdev-dma: Implement damage handling and deferred I/O") Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: # v6.11+ Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-4-tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 45f624b088da4..1710e04e3986f 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -339,7 +339,7 @@ config DRM_TTM_HELPER config DRM_GEM_DMA_HELPER tristate depends on DRM - select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION + select FB_DMAMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION help Choose this if you need the GEM DMA helper functions From 8058944f52262e8e8246af5f8408586b1ebfd2c6 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:18 +0200 Subject: [PATCH 07/90] drm/fbdev: Select fbdev I/O helpers from modules that require them Fbdev emulation for SHMEM and TTM requires helpers from the fbdev subsystem. Select them from the modules that use them instead of the core DRM module. Signed-off-by: Thomas Zimmermann Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-5-tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 1710e04e3986f..d4f33a9247874 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -11,7 +11,6 @@ menuconfig DRM select DRM_PANEL_ORIENTATION_QUIRKS select DRM_KMS_HELPER if DRM_FBDEV_EMULATION select FB_CORE if DRM_FBDEV_EMULATION - select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION select HDMI select I2C select DMA_SHARED_BUFFER @@ -333,6 +332,7 @@ config DRM_TTM_HELPER tristate depends on DRM select DRM_TTM + select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION help Helpers for ttm-based gem objects @@ -346,6 +346,7 @@ config DRM_GEM_DMA_HELPER config DRM_GEM_SHMEM_HELPER tristate depends on DRM && MMU + select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION help Choose this if you need the GEM shmem helper functions From df7e8b522a6090162ecb50fd298ebc4db137562b Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:20 +0200 Subject: [PATCH 08/90] drm/client: Move client event handlers to drm_client_event.c A number of DRM-client functions serve as entry points from device operations to client code. Moving them info a separate file will later allow for a more fine-grained kernel configuration. For most of the users it is sufficient to include instead of the full driver-side interface in v2: - rename new files to drm_client_event.{c,h} Signed-off-by: Thomas Zimmermann Cc: Jani Nikula Cc: Rodrigo Vivi Cc: Joonas Lahtinen Cc: Tvrtko Ursulin Cc: Karol Herbst Cc: Lyude Paul Cc: Danilo Krummrich Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-7-tzimmermann@suse.de --- Documentation/gpu/drm-client.rst | 3 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_client.c | 121 ---------------- drivers/gpu/drm/drm_client_event.c | 135 ++++++++++++++++++ drivers/gpu/drm/drm_drv.c | 2 +- drivers/gpu/drm/drm_file.c | 2 +- drivers/gpu/drm/drm_probe_helper.c | 2 +- .../drm/i915/display/intel_display_driver.c | 2 +- drivers/gpu/drm/nouveau/nouveau_vga.c | 2 +- include/drm/drm_client.h | 4 - include/drm/drm_client_event.h | 12 ++ 11 files changed, 156 insertions(+), 130 deletions(-) create mode 100644 drivers/gpu/drm/drm_client_event.c create mode 100644 include/drm/drm_client_event.h diff --git a/Documentation/gpu/drm-client.rst b/Documentation/gpu/drm-client.rst index 58b5a1d1219d1..cbcfe30de777f 100644 --- a/Documentation/gpu/drm-client.rst +++ b/Documentation/gpu/drm-client.rst @@ -13,3 +13,6 @@ Kernel clients .. kernel-doc:: drivers/gpu/drm/drm_client_modeset.c :export: + +.. kernel-doc:: drivers/gpu/drm/drm_client_event.c + :export: diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 31d8bf60a2fd4..252188d2122c9 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -41,6 +41,7 @@ drm-y := \ drm_bridge.o \ drm_cache.o \ drm_client.o \ + drm_client_event.o \ drm_client_modeset.o \ drm_color_mgmt.o \ drm_connector.o \ diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index bfedcbf516dbe..549b28a5918c6 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -172,99 +171,6 @@ void drm_client_release(struct drm_client_dev *client) } EXPORT_SYMBOL(drm_client_release); -/** - * drm_client_dev_unregister - Unregister clients - * @dev: DRM device - * - * This function releases all clients by calling each client's - * &drm_client_funcs.unregister callback. The callback function - * is responsibe for releaseing all resources including the client - * itself. - * - * The helper drm_dev_unregister() calls this function. Drivers - * that use it don't need to call this function themselves. - */ -void drm_client_dev_unregister(struct drm_device *dev) -{ - struct drm_client_dev *client, *tmp; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - mutex_lock(&dev->clientlist_mutex); - list_for_each_entry_safe(client, tmp, &dev->clientlist, list) { - list_del(&client->list); - if (client->funcs && client->funcs->unregister) { - client->funcs->unregister(client); - } else { - drm_client_release(client); - kfree(client); - } - } - mutex_unlock(&dev->clientlist_mutex); -} -EXPORT_SYMBOL(drm_client_dev_unregister); - -/** - * drm_client_dev_hotplug - Send hotplug event to clients - * @dev: DRM device - * - * This function calls the &drm_client_funcs.hotplug callback on the attached clients. - * - * drm_kms_helper_hotplug_event() calls this function, so drivers that use it - * don't need to call this function themselves. - */ -void drm_client_dev_hotplug(struct drm_device *dev) -{ - struct drm_client_dev *client; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - if (!dev->mode_config.num_connector) { - drm_dbg_kms(dev, "No connectors found, will not send hotplug events!\n"); - return; - } - - mutex_lock(&dev->clientlist_mutex); - list_for_each_entry(client, &dev->clientlist, list) { - if (!client->funcs || !client->funcs->hotplug) - continue; - - if (client->hotplug_failed) - continue; - - ret = client->funcs->hotplug(client); - drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); - if (ret) - client->hotplug_failed = true; - } - mutex_unlock(&dev->clientlist_mutex); -} -EXPORT_SYMBOL(drm_client_dev_hotplug); - -void drm_client_dev_restore(struct drm_device *dev) -{ - struct drm_client_dev *client; - int ret; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - mutex_lock(&dev->clientlist_mutex); - list_for_each_entry(client, &dev->clientlist, list) { - if (!client->funcs || !client->funcs->restore) - continue; - - ret = client->funcs->restore(client); - drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); - if (!ret) /* The first one to return zero gets the privilege to restore */ - break; - } - mutex_unlock(&dev->clientlist_mutex); -} - static void drm_client_buffer_delete(struct drm_client_buffer *buffer) { if (buffer->gem) { @@ -584,30 +490,3 @@ int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_re 0, 0, NULL, 0); } EXPORT_SYMBOL(drm_client_framebuffer_flush); - -#ifdef CONFIG_DEBUG_FS -static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) -{ - struct drm_debugfs_entry *entry = m->private; - struct drm_device *dev = entry->dev; - struct drm_printer p = drm_seq_file_printer(m); - struct drm_client_dev *client; - - mutex_lock(&dev->clientlist_mutex); - list_for_each_entry(client, &dev->clientlist, list) - drm_printf(&p, "%s\n", client->name); - mutex_unlock(&dev->clientlist_mutex); - - return 0; -} - -static const struct drm_debugfs_info drm_client_debugfs_list[] = { - { "internal_clients", drm_client_debugfs_internal_clients, 0 }, -}; - -void drm_client_debugfs_init(struct drm_device *dev) -{ - drm_debugfs_add_files(dev, drm_client_debugfs_list, - ARRAY_SIZE(drm_client_debugfs_list)); -} -#endif diff --git a/drivers/gpu/drm/drm_client_event.c b/drivers/gpu/drm/drm_client_event.c new file mode 100644 index 0000000000000..d13d44320c5c0 --- /dev/null +++ b/drivers/gpu/drm/drm_client_event.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT +/* + * Copyright 2018 Noralf Trønnes + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * drm_client_dev_unregister - Unregister clients + * @dev: DRM device + * + * This function releases all clients by calling each client's + * &drm_client_funcs.unregister callback. The callback function + * is responsibe for releaseing all resources including the client + * itself. + * + * The helper drm_dev_unregister() calls this function. Drivers + * that use it don't need to call this function themselves. + */ +void drm_client_dev_unregister(struct drm_device *dev) +{ + struct drm_client_dev *client, *tmp; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry_safe(client, tmp, &dev->clientlist, list) { + list_del(&client->list); + if (client->funcs && client->funcs->unregister) { + client->funcs->unregister(client); + } else { + drm_client_release(client); + kfree(client); + } + } + mutex_unlock(&dev->clientlist_mutex); +} +EXPORT_SYMBOL(drm_client_dev_unregister); + +/** + * drm_client_dev_hotplug - Send hotplug event to clients + * @dev: DRM device + * + * This function calls the &drm_client_funcs.hotplug callback on the attached clients. + * + * drm_kms_helper_hotplug_event() calls this function, so drivers that use it + * don't need to call this function themselves. + */ +void drm_client_dev_hotplug(struct drm_device *dev) +{ + struct drm_client_dev *client; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + if (!dev->mode_config.num_connector) { + drm_dbg_kms(dev, "No connectors found, will not send hotplug events!\n"); + return; + } + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (!client->funcs || !client->funcs->hotplug) + continue; + + if (client->hotplug_failed) + continue; + + ret = client->funcs->hotplug(client); + drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); + if (ret) + client->hotplug_failed = true; + } + mutex_unlock(&dev->clientlist_mutex); +} +EXPORT_SYMBOL(drm_client_dev_hotplug); + +void drm_client_dev_restore(struct drm_device *dev) +{ + struct drm_client_dev *client; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (!client->funcs || !client->funcs->restore) + continue; + + ret = client->funcs->restore(client); + drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); + if (!ret) /* The first one to return zero gets the privilege to restore */ + break; + } + mutex_unlock(&dev->clientlist_mutex); +} + +#ifdef CONFIG_DEBUG_FS +static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) +{ + struct drm_debugfs_entry *entry = m->private; + struct drm_device *dev = entry->dev; + struct drm_printer p = drm_seq_file_printer(m); + struct drm_client_dev *client; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) + drm_printf(&p, "%s\n", client->name); + mutex_unlock(&dev->clientlist_mutex); + + return 0; +} + +static const struct drm_debugfs_info drm_client_debugfs_list[] = { + { "internal_clients", drm_client_debugfs_internal_clients, 0 }, +}; + +void drm_client_debugfs_init(struct drm_device *dev) +{ + drm_debugfs_add_files(dev, drm_client_debugfs_list, + ARRAY_SIZE(drm_client_debugfs_list)); +} +#endif diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index ac30b0ec9d931..c2c172eb25df7 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -38,7 +38,7 @@ #include #include -#include +#include #include #include #include diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 2ee1c3233b0c6..9011f8e160993 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -40,7 +40,7 @@ #include #include -#include +#include #include #include #include diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 92f21764246f8..96b266b37ba49 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -33,7 +33,7 @@ #include #include -#include +#include #include #include #include diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 069426d9260b1..44de2d8591cd9 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index ab4e11dc0b8a9..a6c375a241542 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -2,7 +2,7 @@ #include #include -#include +#include #include "nouveau_drv.h" #include "nouveau_acpi.h" diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index bc0e66f9c4251..dfd5afcc9463f 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -121,10 +121,6 @@ int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, void drm_client_release(struct drm_client_dev *client); void drm_client_register(struct drm_client_dev *client); -void drm_client_dev_unregister(struct drm_device *dev); -void drm_client_dev_hotplug(struct drm_device *dev); -void drm_client_dev_restore(struct drm_device *dev); - /** * struct drm_client_buffer - DRM client buffer */ diff --git a/include/drm/drm_client_event.h b/include/drm/drm_client_event.h new file mode 100644 index 0000000000000..2c89152411208 --- /dev/null +++ b/include/drm/drm_client_event.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 or MIT */ + +#ifndef _DRM_CLIENT_EVENT_H_ +#define _DRM_CLIENT_EVENT_H_ + +struct drm_device; + +void drm_client_dev_unregister(struct drm_device *dev); +void drm_client_dev_hotplug(struct drm_device *dev); +void drm_client_dev_restore(struct drm_device *dev); + +#endif From bf17766f108309027aac2bfe184df6088dfd7384 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:21 +0200 Subject: [PATCH 09/90] drm/client: Move suspend/resume into DRM client callbacks Suspend and resume is still tied to fbdev emulation. Modeset helpers and several drivers call drm_fb_helper_set_suspend_unlocked() to inform the fbdev client about suspend/resume events. To make it work with arbitrary clients, add per-client callback functions for suspend and resume. Implement them for fbdev emulation with the existing drm_fb_helper_set_suspend_unlocked(). Then update DRM's modeset helpers to call the new interface. Clients that are not fbdev can now implement suspend/resume to their requirements. The callback parameter holds_console_lock is a workaround for i915, radeon and xe, which possibly call the interface while having the console lock acquired. Even though the commit doesn't modify these drivers, it already adds the flag to avoid churn later on. New code should not hold the console lock. v4: - clarify holds_console_lock in commit description (Jonathan) Signed-off-by: Thomas Zimmermann Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-8-tzimmermann@suse.de --- drivers/gpu/drm/drm_client_event.c | 60 ++++++++++++++++++++++++++++ drivers/gpu/drm/drm_fbdev_client.c | 30 +++++++++++++- drivers/gpu/drm/drm_modeset_helper.c | 14 ++++--- include/drm/drm_client.h | 35 ++++++++++++++++ include/drm/drm_client_event.h | 2 + 5 files changed, 133 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_client_event.c b/drivers/gpu/drm/drm_client_event.c index d13d44320c5c0..c52e936436723 100644 --- a/drivers/gpu/drm/drm_client_event.c +++ b/drivers/gpu/drm/drm_client_event.c @@ -107,6 +107,66 @@ void drm_client_dev_restore(struct drm_device *dev) mutex_unlock(&dev->clientlist_mutex); } +static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_lock) +{ + struct drm_device *dev = client->dev; + int ret = 0; + + if (drm_WARN_ON_ONCE(dev, client->suspended)) + return 0; + + if (client->funcs && client->funcs->suspend) + ret = client->funcs->suspend(client, holds_console_lock); + drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); + + client->suspended = true; + + return ret; +} + +void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock) +{ + struct drm_client_dev *client; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (!client->suspended) + drm_client_suspend(client, holds_console_lock); + } + mutex_unlock(&dev->clientlist_mutex); +} +EXPORT_SYMBOL(drm_client_dev_suspend); + +static int drm_client_resume(struct drm_client_dev *client, bool holds_console_lock) +{ + struct drm_device *dev = client->dev; + int ret = 0; + + if (drm_WARN_ON_ONCE(dev, !client->suspended)) + return 0; + + if (client->funcs && client->funcs->resume) + ret = client->funcs->resume(client, holds_console_lock); + drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); + + client->suspended = false; + + return ret; +} + +void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock) +{ + struct drm_client_dev *client; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (client->suspended) + drm_client_resume(client, holds_console_lock); + } + mutex_unlock(&dev->clientlist_mutex); +} +EXPORT_SYMBOL(drm_client_dev_resume); + #ifdef CONFIG_DEBUG_FS static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) { diff --git a/drivers/gpu/drm/drm_fbdev_client.c b/drivers/gpu/drm/drm_fbdev_client.c index a09382afe2fb6..246fb63ab2505 100644 --- a/drivers/gpu/drm/drm_fbdev_client.c +++ b/drivers/gpu/drm/drm_fbdev_client.c @@ -61,11 +61,37 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client) return ret; } +static int drm_fbdev_client_suspend(struct drm_client_dev *client, bool holds_console_lock) +{ + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + + if (holds_console_lock) + drm_fb_helper_set_suspend(fb_helper, true); + else + drm_fb_helper_set_suspend_unlocked(fb_helper, true); + + return 0; +} + +static int drm_fbdev_client_resume(struct drm_client_dev *client, bool holds_console_lock) +{ + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); + + if (holds_console_lock) + drm_fb_helper_set_suspend(fb_helper, false); + else + drm_fb_helper_set_suspend_unlocked(fb_helper, false); + + return 0; +} + static const struct drm_client_funcs drm_fbdev_client_funcs = { .owner = THIS_MODULE, .unregister = drm_fbdev_client_unregister, .restore = drm_fbdev_client_restore, .hotplug = drm_fbdev_client_hotplug, + .suspend = drm_fbdev_client_suspend, + .resume = drm_fbdev_client_resume, }; /** @@ -76,8 +102,8 @@ static const struct drm_client_funcs drm_fbdev_client_funcs = { * * This function sets up fbdev emulation. Restore, hotplug events and * teardown are all taken care of. Drivers that do suspend/resume need - * to call drm_fb_helper_set_suspend_unlocked() themselves. Simple - * drivers might use drm_mode_config_helper_suspend(). + * to call drm_client_dev_suspend() and drm_client_dev_resume() by + * themselves. Simple drivers might use drm_mode_config_helper_suspend(). * * This function is safe to call even when there are no connectors present. * Setup will be retried on the next hotplug event. diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c index 2c582020cb423..5565464c17348 100644 --- a/drivers/gpu/drm/drm_modeset_helper.c +++ b/drivers/gpu/drm/drm_modeset_helper.c @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include #include @@ -185,7 +185,7 @@ EXPORT_SYMBOL(drm_crtc_init); * Zero on success, negative error code on error. * * See also: - * drm_kms_helper_poll_disable() and drm_fb_helper_set_suspend_unlocked(). + * drm_kms_helper_poll_disable() and drm_client_dev_suspend(). */ int drm_mode_config_helper_suspend(struct drm_device *dev) { @@ -199,10 +199,11 @@ int drm_mode_config_helper_suspend(struct drm_device *dev) if (dev->mode_config.poll_enabled) drm_kms_helper_poll_disable(dev); - drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1); + drm_client_dev_suspend(dev, false); state = drm_atomic_helper_suspend(dev); if (IS_ERR(state)) { - drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); + drm_client_dev_resume(dev, false); + /* * Don't enable polling if it was never initialized */ @@ -230,7 +231,7 @@ EXPORT_SYMBOL(drm_mode_config_helper_suspend); * Zero on success, negative error code on error. * * See also: - * drm_fb_helper_set_suspend_unlocked() and drm_kms_helper_poll_enable(). + * drm_client_dev_resume() and drm_kms_helper_poll_enable(). */ int drm_mode_config_helper_resume(struct drm_device *dev) { @@ -247,7 +248,8 @@ int drm_mode_config_helper_resume(struct drm_device *dev) DRM_ERROR("Failed to resume (%d)\n", ret); dev->mode_config.suspend_state = NULL; - drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0); + drm_client_dev_resume(dev, false); + /* * Don't enable polling if it is not initialized */ diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index dfd5afcc9463f..c03c4b0f3e946 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -63,6 +63,34 @@ struct drm_client_funcs { * This callback is optional. */ int (*hotplug)(struct drm_client_dev *client); + + /** + * @suspend: + * + * Called when suspending the device. + * + * This callback is optional. + * + * FIXME: Some callers hold the console lock when invoking this + * function. This interferes with fbdev emulation, which + * also tries to acquire the lock. Push the console lock + * into the callback and remove 'holds_console_lock'. + */ + int (*suspend)(struct drm_client_dev *client, bool holds_console_lock); + + /** + * @resume: + * + * Called when resuming the device from suspend. + * + * This callback is optional. + * + * FIXME: Some callers hold the console lock when invoking this + * function. This interferes with fbdev emulation, which + * also tries to acquire the lock. Push the console lock + * into the callback and remove 'holds_console_lock'. + */ + int (*resume)(struct drm_client_dev *client, bool holds_console_lock); }; /** @@ -107,6 +135,13 @@ struct drm_client_dev { */ struct drm_mode_set *modesets; + /** + * @suspended: + * + * The client has been suspended. + */ + bool suspended; + /** * @hotplug_failed: * diff --git a/include/drm/drm_client_event.h b/include/drm/drm_client_event.h index 2c89152411208..72c97d111169d 100644 --- a/include/drm/drm_client_event.h +++ b/include/drm/drm_client_event.h @@ -8,5 +8,7 @@ struct drm_device; void drm_client_dev_unregister(struct drm_device *dev); void drm_client_dev_hotplug(struct drm_device *dev); void drm_client_dev_restore(struct drm_device *dev); +void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock); +void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock); #endif From 4cf50bae0506dd37b5c593ecaf837ac0c3d894c7 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:22 +0200 Subject: [PATCH 10/90] drm/amdgpu: Suspend and resume internal clients with client helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace calls to drm_fb_helper_set_suspend_unlocked() with calls to the client functions drm_client_dev_suspend() and drm_client_dev_resume(). Any registered in-kernel client will now receive suspend and resume events. Signed-off-by: Thomas Zimmermann Cc: Alex Deucher Cc: "Christian König" Cc: Xinhui Pan Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-9-tzimmermann@suse.de --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 128e086b4251c..5a1ae08817baa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -38,8 +38,8 @@ #include #include +#include #include -#include #include #include #include @@ -4712,13 +4712,13 @@ int amdgpu_device_prepare(struct drm_device *dev) * amdgpu_device_suspend - initiate device suspend * * @dev: drm dev pointer - * @fbcon : notify the fbdev of suspend + * @notify_clients: notify in-kernel DRM clients * * Puts the hw in the suspend state (all asics). * Returns 0 for success or an error on failure. * Called at driver suspend. */ -int amdgpu_device_suspend(struct drm_device *dev, bool fbcon) +int amdgpu_device_suspend(struct drm_device *dev, bool notify_clients) { struct amdgpu_device *adev = drm_to_adev(dev); int r = 0; @@ -4738,8 +4738,8 @@ int amdgpu_device_suspend(struct drm_device *dev, bool fbcon) if (amdgpu_acpi_smart_shift_update(dev, AMDGPU_SS_DEV_D3)) DRM_WARN("smart shift update failed\n"); - if (fbcon) - drm_fb_helper_set_suspend_unlocked(adev_to_drm(adev)->fb_helper, true); + if (notify_clients) + drm_client_dev_suspend(adev_to_drm(adev), false); cancel_delayed_work_sync(&adev->delayed_init_work); @@ -4774,13 +4774,13 @@ int amdgpu_device_suspend(struct drm_device *dev, bool fbcon) * amdgpu_device_resume - initiate device resume * * @dev: drm dev pointer - * @fbcon : notify the fbdev of resume + * @notify_clients: notify in-kernel DRM clients * * Bring the hw back to operating state (all asics). * Returns 0 for success or an error on failure. * Called at driver resume. */ -int amdgpu_device_resume(struct drm_device *dev, bool fbcon) +int amdgpu_device_resume(struct drm_device *dev, bool notify_clients) { struct amdgpu_device *adev = drm_to_adev(dev); int r = 0; @@ -4836,8 +4836,8 @@ int amdgpu_device_resume(struct drm_device *dev, bool fbcon) /* Make sure IB tests flushed */ flush_delayed_work(&adev->delayed_init_work); - if (fbcon) - drm_fb_helper_set_suspend_unlocked(adev_to_drm(adev)->fb_helper, false); + if (notify_clients) + drm_client_dev_resume(adev_to_drm(adev), false); amdgpu_ras_resume(adev); @@ -5449,7 +5449,7 @@ int amdgpu_do_asic_reset(struct list_head *device_list_handle, if (r) goto out; - drm_fb_helper_set_suspend_unlocked(adev_to_drm(tmp_adev)->fb_helper, false); + drm_client_dev_resume(adev_to_drm(tmp_adev), false); /* * The GPU enters bad state once faulty pages @@ -5735,7 +5735,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, */ amdgpu_unregister_gpu_instance(tmp_adev); - drm_fb_helper_set_suspend_unlocked(adev_to_drm(tmp_adev)->fb_helper, true); + drm_client_dev_suspend(adev_to_drm(tmp_adev), false); /* disable ras on ALL IPs */ if (!need_emergency_restart && From 4785658660d507b1e026ea2873caa2ea11486a74 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:23 +0200 Subject: [PATCH 11/90] drm/nouveau: Suspend and resume clients with client helpers Replace calls to drm_fb_helper_set_suspend_unlocked() with calls to the client functions drm_client_dev_suspend() and drm_client_dev_resume(). Any registered in-kernel client will now receive suspend and resume events. Signed-off-by: Thomas Zimmermann Cc: Karol Herbst Cc: Lyude Paul Cc: Danilo Krummrich Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-10-tzimmermann@suse.de --- drivers/gpu/drm/nouveau/nouveau_display.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index e2fd561cd23f4..619a3efbe8c88 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -28,8 +28,8 @@ #include #include +#include #include -#include #include #include #include @@ -804,8 +804,7 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime) { struct nouveau_display *disp = nouveau_display(dev); - /* Disable console. */ - drm_fb_helper_set_suspend_unlocked(dev->fb_helper, true); + drm_client_dev_suspend(dev, false); if (drm_drv_uses_atomic_modeset(dev)) { if (!runtime) { @@ -836,8 +835,7 @@ nouveau_display_resume(struct drm_device *dev, bool runtime) } } - /* Enable console. */ - drm_fb_helper_set_suspend_unlocked(dev->fb_helper, false); + drm_client_dev_resume(dev, false); } int From 88c79de8575c0061a5cdf23c65f8b1ae6202f0d5 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:24 +0200 Subject: [PATCH 12/90] drm/radeon: Suspend and resume clients with client helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace calls to radeon_fbdev_set_suspend() with calls to the client functions drm_client_dev_suspend() and drm_client_dev_resume(). Any registered in-kernel client will now receive suspend and resume events. v4: - refer to radeon_fbdev_set_suspend() in commit description (Jonathan) Signed-off-by: Thomas Zimmermann Cc: Alex Deucher Cc: "Christian König" Cc: Xinhui Pan Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-11-tzimmermann@suse.de --- drivers/gpu/drm/radeon/radeon_device.c | 19 ++++++++++--------- drivers/gpu/drm/radeon/radeon_fbdev.c | 6 ------ drivers/gpu/drm/radeon/radeon_mode.h | 3 --- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 554b236c2328a..6f071e61f7648 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -1542,7 +1543,7 @@ void radeon_device_fini(struct radeon_device *rdev) * Called at driver suspend. */ int radeon_suspend_kms(struct drm_device *dev, bool suspend, - bool fbcon, bool freeze) + bool notify_clients, bool freeze) { struct radeon_device *rdev; struct pci_dev *pdev; @@ -1634,9 +1635,9 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, pci_set_power_state(pdev, PCI_D3hot); } - if (fbcon) { + if (notify_clients) { console_lock(); - radeon_fbdev_set_suspend(rdev, 1); + drm_client_dev_suspend(dev, true); console_unlock(); } return 0; @@ -1649,7 +1650,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, * Returns 0 for success or an error on failure. * Called at driver resume. */ -int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) +int radeon_resume_kms(struct drm_device *dev, bool resume, bool notify_clients) { struct drm_connector *connector; struct radeon_device *rdev = dev->dev_private; @@ -1660,14 +1661,14 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - if (fbcon) { + if (notify_clients) { console_lock(); } if (resume) { pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); if (pci_enable_device(pdev)) { - if (fbcon) + if (notify_clients) console_unlock(); return -1; } @@ -1730,7 +1731,7 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) /* reset hpd state */ radeon_hpd_init(rdev); /* blat the mode back in */ - if (fbcon) { + if (notify_clients) { drm_helper_resume_force_mode(dev); /* turn on display hw */ drm_modeset_lock_all(dev); @@ -1746,8 +1747,8 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) radeon_pm_compute_clocks(rdev); - if (fbcon) { - radeon_fbdev_set_suspend(rdev, 0); + if (notify_clients) { + drm_client_dev_resume(dev, true); console_unlock(); } diff --git a/drivers/gpu/drm/radeon/radeon_fbdev.c b/drivers/gpu/drm/radeon/radeon_fbdev.c index 0aa20c8df5462..d4a58bd679dbc 100644 --- a/drivers/gpu/drm/radeon/radeon_fbdev.c +++ b/drivers/gpu/drm/radeon/radeon_fbdev.c @@ -288,12 +288,6 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, return ret; } -void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state) -{ - if (rdev_to_drm(rdev)->fb_helper) - drm_fb_helper_set_suspend(rdev_to_drm(rdev)->fb_helper, state); -} - bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) { struct drm_fb_helper *fb_helper = rdev_to_drm(rdev)->fb_helper; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 12a1d99a18156..4063d3801e819 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -942,13 +942,10 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper, struct drm_fb_helper_surface_size *sizes); #define RADEON_FBDEV_DRIVER_OPS \ .fbdev_probe = radeon_fbdev_driver_fbdev_probe -void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state); bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj); #else #define RADEON_FBDEV_DRIVER_OPS \ .fbdev_probe = NULL -static inline void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state) -{ } static inline bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) { return false; From 1f828b4dd40264028d9b481c0412e63837d968f6 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:25 +0200 Subject: [PATCH 13/90] drm/client: Make client support optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only build client code if DRM_CLIENT has been selected. Automatially do so if one of the default clients has been enabled. If client support has been disabled, the helpers for client-related events are empty and the regular client functions are not present. Amdgpu has an internal DRM client, so it has to select DRM_CLIENT by itself unconditionally. v3: - provide empty drm_client_debugfs_init() if DRM_CLIENT=n (kernel test robot) Signed-off-by: Thomas Zimmermann Cc: Alex Deucher Cc: "Christian König" Cc: Xinhui Pan Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-12-tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 10 ++++++++++ drivers/gpu/drm/Makefile | 7 ++++--- drivers/gpu/drm/amd/amdgpu/Kconfig | 1 + drivers/gpu/drm/drm_client_event.c | 2 ++ drivers/gpu/drm/drm_debugfs.c | 1 - drivers/gpu/drm/drm_internal.h | 8 ++++++++ include/drm/drm_client.h | 2 -- include/drm/drm_client_event.h | 13 +++++++++++++ 8 files changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index d4f33a9247874..5cbb0b2a4c180 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -210,6 +210,15 @@ config DRM_DEBUG_MODESET_LOCK If in doubt, say "N". +config DRM_CLIENT + bool + depends on DRM + help + Enables support for DRM clients. DRM drivers that need + struct drm_client_dev and its interfaces should select this + option. Drivers that support the default clients should + select DRM_CLIENT_SELECTION instead. + config DRM_CLIENT_SELECTION bool depends on DRM @@ -225,6 +234,7 @@ config DRM_CLIENT_SETUP config DRM_FBDEV_EMULATION bool "Enable legacy fbdev support for your modesetting driver" depends on DRM + select DRM_CLIENT select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE default FB help diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 252188d2122c9..fcb447cafe419 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -40,9 +40,6 @@ drm-y := \ drm_blend.o \ drm_bridge.o \ drm_cache.o \ - drm_client.o \ - drm_client_event.o \ - drm_client_modeset.o \ drm_color_mgmt.o \ drm_connector.o \ drm_crtc.o \ @@ -76,6 +73,10 @@ drm-y := \ drm_vblank_work.o \ drm_vma_manager.o \ drm_writeback.o +drm-$(CONFIG_DRM_CLIENT) += \ + drm_client.o \ + drm_client_event.o \ + drm_client_modeset.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig index 680a94c361baa..41fa3377d9cf5 100644 --- a/drivers/gpu/drm/amd/amdgpu/Kconfig +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig @@ -5,6 +5,7 @@ config DRM_AMDGPU depends on DRM && PCI && MMU depends on !UML select FW_LOADER + select DRM_CLIENT select DRM_CLIENT_SELECTION select DRM_DISPLAY_DP_HELPER select DRM_DISPLAY_DSC_HELPER diff --git a/drivers/gpu/drm/drm_client_event.c b/drivers/gpu/drm/drm_client_event.c index c52e936436723..e303de564485d 100644 --- a/drivers/gpu/drm/drm_client_event.c +++ b/drivers/gpu/drm/drm_client_event.c @@ -14,6 +14,8 @@ #include #include +#include "drm_internal.h" + /** * drm_client_dev_unregister - Unregister clients * @dev: DRM device diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 52783eac078e8..536409a35df40 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 1705bfc90b1e7..b2b6a8e49dda4 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -48,6 +48,14 @@ struct drm_prime_file_private; struct drm_printer; struct drm_vblank_crtc; +/* drm_client_event.c */ +#if defined(CONFIG_DRM_CLIENT) +void drm_client_debugfs_init(struct drm_device *dev); +#else +static inline void drm_client_debugfs_init(struct drm_device *dev) +{ } +#endif + /* drm_file.c */ extern struct mutex drm_global_mutex; bool drm_dev_needs_global_mutex(struct drm_device *dev); diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index c03c4b0f3e946..3b13cf29ed55c 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -236,6 +236,4 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode); drm_for_each_connector_iter(connector, iter) \ if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) -void drm_client_debugfs_init(struct drm_device *dev); - #endif diff --git a/include/drm/drm_client_event.h b/include/drm/drm_client_event.h index 72c97d111169d..99863554b055d 100644 --- a/include/drm/drm_client_event.h +++ b/include/drm/drm_client_event.h @@ -5,10 +5,23 @@ struct drm_device; +#if defined(CONFIG_DRM_CLIENT) void drm_client_dev_unregister(struct drm_device *dev); void drm_client_dev_hotplug(struct drm_device *dev); void drm_client_dev_restore(struct drm_device *dev); void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock); void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock); +#else +static inline void drm_client_dev_unregister(struct drm_device *dev) +{ } +static inline void drm_client_dev_hotplug(struct drm_device *dev) +{ } +static inline void drm_client_dev_restore(struct drm_device *dev) +{ } +static inline void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock) +{ } +static inline void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock) +{ } +#endif #endif From dadd28d4142f9ad39eefb7b45ee7518bd4d2459c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 14 Oct 2024 10:55:26 +0200 Subject: [PATCH 14/90] drm/client: Add client-lib module Add drm_client_lib.ko to contain DRM's built-in client. Move the existing client for fbdev emulation into the new module. Protect the new module behind CONFIG_DRM_CLIENT_LIB. The Kconfig rules separate the DRM drivers from the DRM clients. A driver can opt into the default clients, but the user configures each client individually. To do so, DRM drivers still select DRM_CLIENT_SELECTION. The option is now a tristate that further selects all dependencies of the enabled DRM clients. There's a menu option for each client. Enabling at least one client also selects DRM_CLIENT_SETUP, so that drivers call drm_client_setup(). New DRM clients should depend on DRM_CLIENT_SELECTION. There are existing kernel options in drm_fb_helper.o, so leave this file in the KMS-helper module for now. v5: - leave fbdev helpers in drm_kms_helper.ko for now v3: - fix commit changelog v2: - keep client code in core - protect lib with DRM_CLIENT_LIB - remove duplicate line from Makefile (Jocelyn) Signed-off-by: Thomas Zimmermann Reviewed-by: Jonathan Cavitt Link: https://patchwork.freedesktop.org/patch/msgid/20241014085740.582287-13-tzimmermann@suse.de --- drivers/gpu/drm/Kconfig | 26 +++++++++++++++++++++----- drivers/gpu/drm/Makefile | 14 +++++++++----- drivers/gpu/drm/drm_client_setup.c | 3 +++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 5cbb0b2a4c180..5504721007cc1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -9,8 +9,6 @@ menuconfig DRM tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA select DRM_PANEL_ORIENTATION_QUIRKS - select DRM_KMS_HELPER if DRM_FBDEV_EMULATION - select FB_CORE if DRM_FBDEV_EMULATION select HDMI select I2C select DMA_SHARED_BUFFER @@ -219,10 +217,19 @@ config DRM_CLIENT option. Drivers that support the default clients should select DRM_CLIENT_SELECTION instead. +config DRM_CLIENT_LIB + tristate + depends on DRM + select DRM_KMS_HELPER if DRM_FBDEV_EMULATION + select FB_CORE if DRM_FBDEV_EMULATION + help + This option enables the DRM client library and selects all + modules and components according to the enabled clients. + config DRM_CLIENT_SELECTION - bool + tristate depends on DRM - select DRM_CLIENT_SETUP if DRM_FBDEV_EMULATION + select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION help Drivers that support in-kernel DRM clients have to select this option. @@ -230,11 +237,18 @@ config DRM_CLIENT_SELECTION config DRM_CLIENT_SETUP bool depends on DRM_CLIENT_SELECTION + help + Enables the DRM client selection. DRM drivers that support the + default clients should select DRM_CLIENT_SELECTION instead. + +menu "Supported DRM clients" + depends on DRM_CLIENT_SELECTION config DRM_FBDEV_EMULATION bool "Enable legacy fbdev support for your modesetting driver" - depends on DRM + depends on DRM_CLIENT_SELECTION select DRM_CLIENT + select DRM_CLIENT_SETUP select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE default FB help @@ -273,6 +287,8 @@ config DRM_FBDEV_LEAK_PHYS_SMEM If in doubt, say "N" or spread the word to your closed source library vendor. +endmenu + config DRM_LOAD_EDID_FIRMWARE bool "Allow to specify an EDID data set instead of probing for it" depends on DRM diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index fcb447cafe419..463afad1b5ca6 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -144,14 +144,18 @@ drm_kms_helper-y := \ drm_probe_helper.o \ drm_self_refresh_helper.o \ drm_simple_kms_helper.o -drm_kms_helper-$(CONFIG_DRM_CLIENT_SETUP) += \ - drm_client_setup.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o -drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += \ - drm_fbdev_client.o \ - drm_fb_helper.o +drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o +# +# DRM clients +# + +drm_client_lib-y := drm_client_setup.o +drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o +obj-$(CONFIG_DRM_CLIENT_LIB) += drm_client_lib.o + # # Drivers and the rest # diff --git a/drivers/gpu/drm/drm_client_setup.c b/drivers/gpu/drm/drm_client_setup.c index 5969c4ffe31ba..c14221ca5a0d8 100644 --- a/drivers/gpu/drm/drm_client_setup.c +++ b/drivers/gpu/drm/drm_client_setup.c @@ -64,3 +64,6 @@ void drm_client_setup_with_color_mode(struct drm_device *dev, unsigned int color drm_client_setup_with_fourcc(dev, fourcc); } EXPORT_SYMBOL(drm_client_setup_with_color_mode); + +MODULE_DESCRIPTION("In-kernel DRM clients"); +MODULE_LICENSE("GPL and additional rights"); From 2063ca42486bc07b49bc145b5dfcb421f4deebaf Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 15 Oct 2024 13:46:06 +0200 Subject: [PATCH 15/90] drm/display: Drop obsolete dependency on COMPILE_TEST Since commit 0166dc11be91 ("of: make CONFIG_OF user selectable"), it is possible to test-build any driver which depends on OF on any architecture by explicitly selecting OF. Therefore depending on COMPILE_TEST as an alternative is no longer needed. To avoid reintroducing the randconfig bug originally fixed by commit 876271118aa4 ("drm/display: Fix build error without CONFIG_OF"), DRM_MSM which selects DRM_DISPLAY_DP_HELPER must explicitly depend on OF. This is consistent with what all other DRM drivers are doing. Signed-off-by: Jean Delvare Reviewed-by: Javier Martinez Canillas Cc: David Airlie Cc: Daniel Vetter Acked-by: Douglas Anderson Acked-by: Dmitry Baryshkov Reviewed-by: Abhinav Kumar Link: https://patchwork.freedesktop.org/patch/msgid/20241015134606.5b87093e@endymion.delvare Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/Kconfig | 2 +- drivers/gpu/drm/msm/Kconfig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig index 0f07cf1483ffc..6a4e892afcf89 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -3,7 +3,7 @@ config DRM_DISPLAY_DP_AUX_BUS tristate depends on DRM - depends on OF || COMPILE_TEST + depends on OF config DRM_DISPLAY_HELPER tristate diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index f16d7c00e0894..7ec833b6d8292 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -6,6 +6,7 @@ config DRM_MSM depends on ARCH_QCOM || SOC_IMX5 || COMPILE_TEST depends on COMMON_CLK depends on IOMMU_SUPPORT + depends on OF depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n depends on QCOM_OCMEM || QCOM_OCMEM=n depends on QCOM_LLCC || QCOM_LLCC=n From 11fc5ce6e2a6316b7d02746c035ee1e51bf10718 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 11 Oct 2024 10:57:12 +0100 Subject: [PATCH 16/90] accel/qaic: Pass string literal as format argument of alloc_workqueue() Recently I noticed that both gcc-14 and clang-18 report that passing a non-string literal as the format argument of alloc_workqueue() is potentially insecure. E.g. clang-18 says: .../qaic_drv.c:61:23: warning: format string is not a string literal (potentially insecure) [-Wformat-security] 61 | wq = alloc_workqueue(fmt, WQ_UNBOUND, 0); | ^~~ .../qaic_drv.c:61:23: note: treat the string as an argument to avoid this 61 | wq = alloc_workqueue(fmt, WQ_UNBOUND, 0); | ^ | "%s", It is always the case where the contents of fmt is safe to pass as the format argument. That is, in my understanding, it never contains any format escape sequences. But, it seems better to be safe than sorry. And, as a bonus, compiler output becomes less verbose by addressing this issue as suggested by clang-18. Also, change the name of the parameter of qaicm_wq_init from fmt to name to better reflect it's purpose. Compile tested only. Signed-off-by: Simon Horman Reviewed-by: Jeffrey Hugo Tested-by: Jeffrey Hugo Signed-off-by: Jeffrey Hugo Link: https://patchwork.freedesktop.org/patch/msgid/20241011-string-thing-v1-3-acc506568033@kernel.org --- drivers/accel/qaic/qaic_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c index f139c564eadf9..3575e0c984d69 100644 --- a/drivers/accel/qaic/qaic_drv.c +++ b/drivers/accel/qaic/qaic_drv.c @@ -54,12 +54,12 @@ static void qaicm_wq_release(struct drm_device *dev, void *res) destroy_workqueue(wq); } -static struct workqueue_struct *qaicm_wq_init(struct drm_device *dev, const char *fmt) +static struct workqueue_struct *qaicm_wq_init(struct drm_device *dev, const char *name) { struct workqueue_struct *wq; int ret; - wq = alloc_workqueue(fmt, WQ_UNBOUND, 0); + wq = alloc_workqueue("%s", WQ_UNBOUND, 0, name); if (!wq) return ERR_PTR(-ENOMEM); ret = drmm_add_action_or_reset(dev, qaicm_wq_release, wq); From 12f6baa472e097d4d55dfef9eacc36b04071bdc1 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 15 Oct 2024 19:05:43 -0400 Subject: [PATCH 17/90] dt-bindings: display: Add Sharp Memory LCD bindings Add device tree bindings for the monochrome Sharp Memory LCD Co-developed-by: Mehdi Djait Signed-off-by: Mehdi Djait Signed-off-by: Alex Lanzano Reviewed-by: Krzysztof Kozlowski Link: https://patchwork.freedesktop.org/patch/msgid/20241015230617.3020230-2-lanzano.alex@gmail.com Signed-off-by: Dmitry Baryshkov --- .../bindings/display/sharp,ls010b7dh04.yaml | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/sharp,ls010b7dh04.yaml diff --git a/Documentation/devicetree/bindings/display/sharp,ls010b7dh04.yaml b/Documentation/devicetree/bindings/display/sharp,ls010b7dh04.yaml new file mode 100644 index 0000000000000..8097f091c2a55 --- /dev/null +++ b/Documentation/devicetree/bindings/display/sharp,ls010b7dh04.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/sharp,ls010b7dh04.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sharp Memory LCD panels + +maintainers: + - Alex Lanzano + +description: + Sharp Memory LCDs are a series of monochrome displays that operate over + a SPI bus. The displays require a signal (VCOM) to be generated to prevent + DC bias build up resulting in pixels being unable to change. Three modes + can be used to provide the VCOM signal ("software", "external", "pwm"). + +properties: + compatible: + enum: + - sharp,ls010b7dh04 + - sharp,ls011b7dh03 + - sharp,ls012b7dd01 + - sharp,ls013b7dh03 + - sharp,ls013b7dh05 + - sharp,ls018b7dh02 + - sharp,ls027b7dh01 + - sharp,ls027b7dh01a + - sharp,ls032b7dd02 + - sharp,ls044q7dh01 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 2000000 + + sharp,vcom-mode: + $ref: /schemas/types.yaml#/definitions/string + description: | + software - This mode relies on a software operation to send a + "maintain display" message to the display, toggling the vcom + bit on and off with each message + + external - This mode relies on an external clock to generate + the signal on the EXTCOMM pin + + pwm - This mode relies on a pwm device to generate the signal + on the EXTCOMM pin + + enum: [software, external, pwm] + + enable-gpios: true + + pwms: + maxItems: 1 + description: External VCOM signal + +required: + - compatible + - reg + - sharp,vcom-mode + +allOf: + - $ref: panel/panel-common.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + + - if: + properties: + sharp,vcom-mode: + const: pwm + then: + required: + - pwms + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + display@0 { + compatible = "sharp,ls013b7dh03"; + reg = <0>; + spi-cs-high; + spi-max-frequency = <1000000>; + sharp,vcom-mode = "software"; + }; + }; +... From b8f9f21716fecac41d083ec8c77809ecd0b100d8 Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Tue, 15 Oct 2024 19:05:44 -0400 Subject: [PATCH 18/90] drm/tiny: Add driver for Sharp Memory LCD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the monochrome Sharp Memory LCDs. Co-developed-by: Mehdi Djait Signed-off-by: Mehdi Djait Signed-off-by: Alex Lanzano Reviewed-by: Dmitry Baryshkov Acked-by: Uwe Kleine-König Link: https://patchwork.freedesktop.org/patch/msgid/20241015230617.3020230-3-lanzano.alex@gmail.com Signed-off-by: Dmitry Baryshkov --- MAINTAINERS | 6 + drivers/gpu/drm/tiny/Kconfig | 21 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/sharp-memory.c | 671 ++++++++++++++++++++++++++++ 4 files changed, 699 insertions(+) create mode 100644 drivers/gpu/drm/tiny/sharp-memory.c diff --git a/MAINTAINERS b/MAINTAINERS index e200e10696fd6..8b993d6de0f32 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7387,6 +7387,12 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/samsung,s6e3ha8.yaml F: drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c +DRM DRIVER FOR SHARP MEMORY LCD +M: Alex Lanzano +S: Maintained +F: Documentation/devicetree/bindings/display/sharp,ls010b7dh04.yaml +F: drivers/gpu/drm/tiny/sharp-memory.c + DRM DRIVER FOR SITRONIX ST7586 PANELS M: David Lechner S: Maintained diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index f542aae334d58..94cbdb1337c07 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -198,6 +198,27 @@ config TINYDRM_REPAPER If M is selected the module will be called repaper. +config TINYDRM_SHARP_MEMORY + tristate "DRM support for Sharp Memory LCD panels" + depends on DRM && SPI + select DRM_CLIENT_SELECTION + select DRM_GEM_DMA_HELPER + select DRM_KMS_HELPER + help + DRM Driver for the following Sharp Memory Panels: + * 1.00" Sharp Memory LCD (LS010B7DH04) + * 1.10" Sharp Memory LCD (LS011B7DH03) + * 1.20" Sharp Memory LCD (LS012B7DD01) + * 1.28" Sharp Memory LCD (LS013B7DH03) + * 1.26" Sharp Memory LCD (LS013B7DH05) + * 1.80" Sharp Memory LCD (LS018B7DH02) + * 2.70" Sharp Memory LCD (LS027B7DH01) + * 2.70" Sharp Memory LCD (LS027B7DH01A) + * 3.20" Sharp Memory LCD (LS032B7DD02) + * 4.40" Sharp Memory LCD (LS044Q7DH01) + + If M is selected the module will be called sharp_memory. + config TINYDRM_ST7586 tristate "DRM support for Sitronix ST7586 display panels" depends on DRM && SPI diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index 76dde89a044b7..4aaf56f8707d3 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o obj-$(CONFIG_TINYDRM_ILI9486) += ili9486.o obj-$(CONFIG_TINYDRM_MI0283QT) += mi0283qt.o obj-$(CONFIG_TINYDRM_REPAPER) += repaper.o +obj-$(CONFIG_TINYDRM_SHARP_MEMORY) += sharp-memory.o obj-$(CONFIG_TINYDRM_ST7586) += st7586.o obj-$(CONFIG_TINYDRM_ST7735R) += st7735r.o diff --git a/drivers/gpu/drm/tiny/sharp-memory.c b/drivers/gpu/drm/tiny/sharp-memory.c new file mode 100644 index 0000000000000..2d2315bd6aef6 --- /dev/null +++ b/drivers/gpu/drm/tiny/sharp-memory.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHARP_MODE_PERIOD 8 +#define SHARP_ADDR_PERIOD 8 +#define SHARP_DUMMY_PERIOD 8 + +#define SHARP_MEMORY_DISPLAY_MAINTAIN_MODE 0 +#define SHARP_MEMORY_DISPLAY_UPDATE_MODE 1 +#define SHARP_MEMORY_DISPLAY_CLEAR_MODE 4 + +enum sharp_memory_model { + LS010B7DH04, + LS011B7DH03, + LS012B7DD01, + LS013B7DH03, + LS013B7DH05, + LS018B7DH02, + LS027B7DH01, + LS027B7DH01A, + LS032B7DD02, + LS044Q7DH01, +}; + +enum sharp_memory_vcom_mode { + SHARP_MEMORY_SOFTWARE_VCOM, + SHARP_MEMORY_EXTERNAL_VCOM, + SHARP_MEMORY_PWM_VCOM +}; + +struct sharp_memory_device { + struct drm_device drm; + struct spi_device *spi; + + const struct drm_display_mode *mode; + + struct drm_crtc crtc; + struct drm_plane plane; + struct drm_encoder encoder; + struct drm_connector connector; + + struct gpio_desc *enable_gpio; + + struct task_struct *sw_vcom_signal; + struct pwm_device *pwm_vcom_signal; + + enum sharp_memory_vcom_mode vcom_mode; + u8 vcom; + + u32 pitch; + u32 tx_buffer_size; + u8 *tx_buffer; + + /* When vcom_mode == "software" a kthread is used to periodically send a + * 'maintain display' message over spi. This mutex ensures tx_buffer access + * and spi bus usage is synchronized in this case. + */ + struct mutex tx_mutex; +}; + +static inline int sharp_memory_spi_write(struct spi_device *spi, void *buf, size_t len) +{ + /* Reverse the bit order */ + for (u8 *b = buf; b < ((u8 *)buf) + len; ++b) + *b = bitrev8(*b); + + return spi_write(spi, buf, len); +} + +static inline struct sharp_memory_device *drm_to_sharp_memory_device(struct drm_device *drm) +{ + return container_of(drm, struct sharp_memory_device, drm); +} + +DEFINE_DRM_GEM_DMA_FOPS(sharp_memory_fops); + +static const struct drm_driver sharp_memory_drm_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .fops = &sharp_memory_fops, + DRM_GEM_DMA_DRIVER_OPS_VMAP, + DRM_FBDEV_DMA_DRIVER_OPS, + .name = "sharp_memory_display", + .desc = "Sharp Display Memory LCD", + .date = "20231129", + .major = 1, + .minor = 0, +}; + +static inline void sharp_memory_set_tx_buffer_mode(u8 *buffer, u8 mode, u8 vcom) +{ + *buffer = mode | (vcom << 1); +} + +static inline void sharp_memory_set_tx_buffer_addresses(u8 *buffer, + struct drm_rect clip, + u32 pitch) +{ + for (u32 line = 0; line < clip.y2; ++line) + buffer[line * pitch] = line + 1; +} + +static void sharp_memory_set_tx_buffer_data(u8 *buffer, + struct drm_framebuffer *fb, + struct drm_rect clip, + u32 pitch, + struct drm_format_conv_state *fmtcnv_state) +{ + int ret; + struct iosys_map dst, vmap; + struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, 0); + + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) + return; + + iosys_map_set_vaddr(&dst, buffer); + iosys_map_set_vaddr(&vmap, dma_obj->vaddr); + + drm_fb_xrgb8888_to_mono(&dst, &pitch, &vmap, fb, &clip, fmtcnv_state); + + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); +} + +static int sharp_memory_update_display(struct sharp_memory_device *smd, + struct drm_framebuffer *fb, + struct drm_rect clip, + struct drm_format_conv_state *fmtcnv_state) +{ + int ret; + u32 pitch = smd->pitch; + u8 vcom = smd->vcom; + u8 *tx_buffer = smd->tx_buffer; + u32 tx_buffer_size = smd->tx_buffer_size; + + mutex_lock(&smd->tx_mutex); + + /* Populate the transmit buffer with frame data */ + sharp_memory_set_tx_buffer_mode(&tx_buffer[0], + SHARP_MEMORY_DISPLAY_UPDATE_MODE, vcom); + sharp_memory_set_tx_buffer_addresses(&tx_buffer[1], clip, pitch); + sharp_memory_set_tx_buffer_data(&tx_buffer[2], fb, clip, pitch, fmtcnv_state); + + ret = sharp_memory_spi_write(smd->spi, tx_buffer, tx_buffer_size); + + mutex_unlock(&smd->tx_mutex); + + return ret; +} + +static int sharp_memory_maintain_display(struct sharp_memory_device *smd) +{ + int ret; + u8 vcom = smd->vcom; + u8 *tx_buffer = smd->tx_buffer; + + mutex_lock(&smd->tx_mutex); + + sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_MAINTAIN_MODE, vcom); + tx_buffer[1] = 0; /* Write dummy data */ + ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2); + + mutex_unlock(&smd->tx_mutex); + + return ret; +} + +static int sharp_memory_clear_display(struct sharp_memory_device *smd) +{ + int ret; + u8 vcom = smd->vcom; + u8 *tx_buffer = smd->tx_buffer; + + mutex_lock(&smd->tx_mutex); + + sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_CLEAR_MODE, vcom); + tx_buffer[1] = 0; /* write dummy data */ + ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2); + + mutex_unlock(&smd->tx_mutex); + + return ret; +} + +static void sharp_memory_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect, + struct drm_format_conv_state *fmtconv_state) +{ + struct drm_rect clip; + struct sharp_memory_device *smd = drm_to_sharp_memory_device(fb->dev); + + /* Always update a full line regardless of what is dirty */ + clip.x1 = 0; + clip.x2 = fb->width; + clip.y1 = rect->y1; + clip.y2 = rect->y2; + + sharp_memory_update_display(smd, fb, clip, fmtconv_state); +} + +static int sharp_memory_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct sharp_memory_device *smd; + struct drm_crtc_state *crtc_state; + + smd = container_of(plane, struct sharp_memory_device, plane); + crtc_state = drm_atomic_get_new_crtc_state(state, &smd->crtc); + + return drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); +} + +static void sharp_memory_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *plane_state = plane->state; + struct drm_format_conv_state fmtcnv_state = DRM_FORMAT_CONV_STATE_INIT; + struct sharp_memory_device *smd; + struct drm_rect rect; + + smd = container_of(plane, struct sharp_memory_device, plane); + if (!smd->crtc.state->active) + return; + + if (drm_atomic_helper_damage_merged(old_state, plane_state, &rect)) + sharp_memory_fb_dirty(plane_state->fb, &rect, &fmtcnv_state); + + drm_format_conv_state_release(&fmtcnv_state); +} + +static const struct drm_plane_helper_funcs sharp_memory_plane_helper_funcs = { + .prepare_fb = drm_gem_plane_helper_prepare_fb, + .atomic_check = sharp_memory_plane_atomic_check, + .atomic_update = sharp_memory_plane_atomic_update, +}; + +static bool sharp_memory_format_mod_supported(struct drm_plane *plane, + u32 format, + u64 modifier) +{ + return modifier == DRM_FORMAT_MOD_LINEAR; +} + +static const struct drm_plane_funcs sharp_memory_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .format_mod_supported = sharp_memory_format_mod_supported, +}; + +static enum drm_mode_status sharp_memory_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev); + + return drm_crtc_helper_mode_valid_fixed(crtc, mode, smd->mode); +} + +static int sharp_memory_crtc_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + int ret; + + if (!crtc_state->enable) + goto out; + + ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state); + if (ret) + return ret; + +out: + return drm_atomic_add_affected_planes(state, crtc); +} + +static int sharp_memory_sw_vcom_signal_thread(void *data) +{ + struct sharp_memory_device *smd = data; + + while (!kthread_should_stop()) { + smd->vcom ^= 1; /* Toggle vcom */ + sharp_memory_maintain_display(smd); + msleep(1000); + } + + return 0; +} + +static void sharp_memory_crtc_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev); + + sharp_memory_clear_display(smd); + + if (smd->enable_gpio) + gpiod_set_value(smd->enable_gpio, 1); +} + +static void sharp_memory_crtc_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev); + + sharp_memory_clear_display(smd); + + if (smd->enable_gpio) + gpiod_set_value(smd->enable_gpio, 0); +} + +static const struct drm_crtc_helper_funcs sharp_memory_crtc_helper_funcs = { + .mode_valid = sharp_memory_crtc_mode_valid, + .atomic_check = sharp_memory_crtc_check, + .atomic_enable = sharp_memory_crtc_enable, + .atomic_disable = sharp_memory_crtc_disable, +}; + +static const struct drm_crtc_funcs sharp_memory_crtc_funcs = { + .reset = drm_atomic_helper_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static const struct drm_encoder_funcs sharp_memory_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int sharp_memory_connector_get_modes(struct drm_connector *connector) +{ + struct sharp_memory_device *smd = drm_to_sharp_memory_device(connector->dev); + + return drm_connector_helper_get_modes_fixed(connector, smd->mode); +} + +static const struct drm_connector_helper_funcs sharp_memory_connector_hfuncs = { + .get_modes = sharp_memory_connector_get_modes, +}; + +static const struct drm_connector_funcs sharp_memory_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + +}; + +static const struct drm_mode_config_funcs sharp_memory_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static const struct drm_display_mode sharp_memory_ls010b7dh04_mode = { + DRM_SIMPLE_MODE(128, 128, 18, 18), +}; + +static const struct drm_display_mode sharp_memory_ls011b7dh03_mode = { + DRM_SIMPLE_MODE(160, 68, 25, 10), +}; + +static const struct drm_display_mode sharp_memory_ls012b7dd01_mode = { + DRM_SIMPLE_MODE(184, 38, 29, 6), +}; + +static const struct drm_display_mode sharp_memory_ls013b7dh03_mode = { + DRM_SIMPLE_MODE(128, 128, 23, 23), +}; + +static const struct drm_display_mode sharp_memory_ls013b7dh05_mode = { + DRM_SIMPLE_MODE(144, 168, 20, 24), +}; + +static const struct drm_display_mode sharp_memory_ls018b7dh02_mode = { + DRM_SIMPLE_MODE(230, 303, 27, 36), +}; + +static const struct drm_display_mode sharp_memory_ls027b7dh01_mode = { + DRM_SIMPLE_MODE(400, 240, 58, 35), +}; + +static const struct drm_display_mode sharp_memory_ls032b7dd02_mode = { + DRM_SIMPLE_MODE(336, 536, 42, 68), +}; + +static const struct drm_display_mode sharp_memory_ls044q7dh01_mode = { + DRM_SIMPLE_MODE(320, 240, 89, 67), +}; + +static const struct spi_device_id sharp_memory_ids[] = { + {"ls010b7dh04", (kernel_ulong_t)&sharp_memory_ls010b7dh04_mode}, + {"ls011b7dh03", (kernel_ulong_t)&sharp_memory_ls011b7dh03_mode}, + {"ls012b7dd01", (kernel_ulong_t)&sharp_memory_ls012b7dd01_mode}, + {"ls013b7dh03", (kernel_ulong_t)&sharp_memory_ls013b7dh03_mode}, + {"ls013b7dh05", (kernel_ulong_t)&sharp_memory_ls013b7dh05_mode}, + {"ls018b7dh02", (kernel_ulong_t)&sharp_memory_ls018b7dh02_mode}, + {"ls027b7dh01", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode}, + {"ls027b7dh01a", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode}, + {"ls032b7dd02", (kernel_ulong_t)&sharp_memory_ls032b7dd02_mode}, + {"ls044q7dh01", (kernel_ulong_t)&sharp_memory_ls044q7dh01_mode}, + {}, +}; +MODULE_DEVICE_TABLE(spi, sharp_memory_ids); + +static const struct of_device_id sharp_memory_of_match[] = { + {.compatible = "sharp,ls010b7dh04", &sharp_memory_ls010b7dh04_mode}, + {.compatible = "sharp,ls011b7dh03", &sharp_memory_ls011b7dh03_mode}, + {.compatible = "sharp,ls012b7dd01", &sharp_memory_ls012b7dd01_mode}, + {.compatible = "sharp,ls013b7dh03", &sharp_memory_ls013b7dh03_mode}, + {.compatible = "sharp,ls013b7dh05", &sharp_memory_ls013b7dh05_mode}, + {.compatible = "sharp,ls018b7dh02", &sharp_memory_ls018b7dh02_mode}, + {.compatible = "sharp,ls027b7dh01", &sharp_memory_ls027b7dh01_mode}, + {.compatible = "sharp,ls027b7dh01a", &sharp_memory_ls027b7dh01_mode}, + {.compatible = "sharp,ls032b7dd02", &sharp_memory_ls032b7dd02_mode}, + {.compatible = "sharp,ls044q7dh01", &sharp_memory_ls044q7dh01_mode}, + {}, +}; +MODULE_DEVICE_TABLE(of, sharp_memory_of_match); + +static const u32 sharp_memory_formats[] = { + DRM_FORMAT_XRGB8888, +}; + +static int sharp_memory_pipe_init(struct drm_device *dev, + struct sharp_memory_device *smd, + const u32 *formats, unsigned int format_count, + const u64 *format_modifiers) +{ + int ret; + struct drm_encoder *encoder = &smd->encoder; + struct drm_plane *plane = &smd->plane; + struct drm_crtc *crtc = &smd->crtc; + struct drm_connector *connector = &smd->connector; + + drm_plane_helper_add(plane, &sharp_memory_plane_helper_funcs); + ret = drm_universal_plane_init(dev, plane, 0, + &sharp_memory_plane_funcs, + formats, format_count, + format_modifiers, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) + return ret; + + drm_crtc_helper_add(crtc, &sharp_memory_crtc_helper_funcs); + ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, + &sharp_memory_crtc_funcs, NULL); + if (ret) + return ret; + + encoder->possible_crtcs = drm_crtc_mask(crtc); + ret = drm_encoder_init(dev, encoder, &sharp_memory_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); + if (ret) + return ret; + + ret = drm_connector_init(&smd->drm, &smd->connector, + &sharp_memory_connector_funcs, + DRM_MODE_CONNECTOR_SPI); + if (ret) + return ret; + + drm_connector_helper_add(&smd->connector, + &sharp_memory_connector_hfuncs); + + return drm_connector_attach_encoder(connector, encoder); +} + +static int sharp_memory_init_pwm_vcom_signal(struct sharp_memory_device *smd) +{ + int ret; + struct device *dev = &smd->spi->dev; + struct pwm_state pwm_state; + + smd->pwm_vcom_signal = devm_pwm_get(dev, NULL); + if (IS_ERR(smd->pwm_vcom_signal)) + return dev_err_probe(dev, PTR_ERR(smd->pwm_vcom_signal), + "Could not get pwm device\n"); + + pwm_init_state(smd->pwm_vcom_signal, &pwm_state); + pwm_set_relative_duty_cycle(&pwm_state, 1, 10); + pwm_state.enabled = true; + ret = pwm_apply_might_sleep(smd->pwm_vcom_signal, &pwm_state); + if (ret) + return dev_err_probe(dev, -EINVAL, "Could not apply pwm state\n"); + + return 0; +} + +static int sharp_memory_probe(struct spi_device *spi) +{ + int ret; + struct device *dev; + struct sharp_memory_device *smd; + struct drm_device *drm; + const char *vcom_mode_str; + + dev = &spi->dev; + + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to setup spi device\n"); + + if (!dev->coherent_dma_mask) { + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) + return dev_err_probe(dev, ret, "Failed to set dma mask\n"); + } + + smd = devm_drm_dev_alloc(dev, &sharp_memory_drm_driver, + struct sharp_memory_device, drm); + if (!smd) + return -ENOMEM; + + spi_set_drvdata(spi, smd); + + smd->spi = spi; + drm = &smd->drm; + ret = drmm_mode_config_init(drm); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize drm config\n"); + + smd->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + if (!smd->enable_gpio) + dev_warn(dev, "Enable gpio not defined\n"); + + drm->mode_config.funcs = &sharp_memory_mode_config_funcs; + smd->mode = spi_get_device_match_data(spi); + + smd->pitch = (SHARP_ADDR_PERIOD + smd->mode->hdisplay + SHARP_DUMMY_PERIOD) / 8; + smd->tx_buffer_size = (SHARP_MODE_PERIOD + + (SHARP_ADDR_PERIOD + (smd->mode->hdisplay) + SHARP_DUMMY_PERIOD) * + smd->mode->vdisplay) / 8; + + smd->tx_buffer = devm_kzalloc(dev, smd->tx_buffer_size, GFP_KERNEL); + if (!smd->tx_buffer) + return -ENOMEM; + + mutex_init(&smd->tx_mutex); + + /* + * VCOM is a signal that prevents DC bias from being built up in + * the panel resulting in pixels being forever stuck in one state. + * + * This driver supports three different methods to generate this + * signal depending on EXTMODE pin: + * + * software (EXTMODE = L) - This mode uses a kthread to + * periodically send a "maintain display" message to the display, + * toggling the vcom bit on and off with each message + * + * external (EXTMODE = H) - This mode relies on an external + * clock to generate the signal on the EXTCOMM pin + * + * pwm (EXTMODE = H) - This mode uses a pwm device to generate + * the signal on the EXTCOMM pin + * + */ + if (device_property_read_string(dev, "sharp,vcom-mode", &vcom_mode_str)) + return dev_err_probe(dev, -EINVAL, + "Unable to find sharp,vcom-mode node in device tree\n"); + + if (!strcmp("software", vcom_mode_str)) { + smd->vcom_mode = SHARP_MEMORY_SOFTWARE_VCOM; + smd->sw_vcom_signal = kthread_run(sharp_memory_sw_vcom_signal_thread, + smd, "sw_vcom_signal"); + + } else if (!strcmp("external", vcom_mode_str)) { + smd->vcom_mode = SHARP_MEMORY_EXTERNAL_VCOM; + + } else if (!strcmp("pwm", vcom_mode_str)) { + smd->vcom_mode = SHARP_MEMORY_PWM_VCOM; + ret = sharp_memory_init_pwm_vcom_signal(smd); + if (ret) + return ret; + } else { + return dev_err_probe(dev, -EINVAL, "Invalid value set for vcom-mode\n"); + } + + drm->mode_config.min_width = smd->mode->hdisplay; + drm->mode_config.max_width = smd->mode->hdisplay; + drm->mode_config.min_height = smd->mode->vdisplay; + drm->mode_config.max_height = smd->mode->vdisplay; + + ret = sharp_memory_pipe_init(drm, smd, sharp_memory_formats, + ARRAY_SIZE(sharp_memory_formats), + NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize display pipeline.\n"); + + drm_plane_enable_fb_damage_clips(&smd->plane); + drm_mode_config_reset(drm); + + ret = drm_dev_register(drm, 0); + if (ret) + return dev_err_probe(dev, ret, "Failed to register drm device.\n"); + + drm_client_setup(drm, NULL); + + return 0; +} + +static void sharp_memory_remove(struct spi_device *spi) +{ + struct sharp_memory_device *smd = spi_get_drvdata(spi); + + drm_dev_unplug(&smd->drm); + drm_atomic_helper_shutdown(&smd->drm); + + switch (smd->vcom_mode) { + case SHARP_MEMORY_SOFTWARE_VCOM: + kthread_stop(smd->sw_vcom_signal); + break; + + case SHARP_MEMORY_EXTERNAL_VCOM: + break; + + case SHARP_MEMORY_PWM_VCOM: + pwm_disable(smd->pwm_vcom_signal); + break; + } +} + +static struct spi_driver sharp_memory_spi_driver = { + .driver = { + .name = "sharp_memory", + .of_match_table = sharp_memory_of_match, + }, + .probe = sharp_memory_probe, + .remove = sharp_memory_remove, + .id_table = sharp_memory_ids, +}; +module_spi_driver(sharp_memory_spi_driver); + +MODULE_AUTHOR("Alex Lanzano "); +MODULE_DESCRIPTION("SPI Protocol driver for the sharp_memory display"); +MODULE_LICENSE("GPL"); From b895a1805e0b01d523afa71818cb97a5d2655fcf Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 25 Sep 2024 17:42:40 +0200 Subject: [PATCH 19/90] drm/bridge: dpc3433: Constify struct regmap_config `dlpc_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Reviewed-by: Neil Armstrong Reviewed-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://patchwork.freedesktop.org/patch/msgid/20240925-drm-const-regmap-v1-1-e609d502401b@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/bridge/ti-dlpc3433.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c index 6b559e0713012..a0a1b5dd794ef 100644 --- a/drivers/gpu/drm/bridge/ti-dlpc3433.c +++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c @@ -94,7 +94,7 @@ static const struct regmap_access_table dlpc_volatile_table = { .n_yes_ranges = ARRAY_SIZE(dlpc_volatile_ranges), }; -static struct regmap_config dlpc_regmap_config = { +static const struct regmap_config dlpc_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = WR_DSI_PORT_EN, From 9239d961ce9d95ec13e241407d0320228e664d68 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 25 Sep 2024 17:42:41 +0200 Subject: [PATCH 20/90] drm/fsl-dcu: Constify struct regmap_config `fsl_tcon_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Reviewed-by: Neil Armstrong Reviewed-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://patchwork.freedesktop.org/patch/msgid/20240925-drm-const-regmap-v1-2-e609d502401b@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/fsl-dcu/fsl_tcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c index 9eb5abaf7d660..49bbd00c77aec 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c @@ -29,7 +29,7 @@ void fsl_tcon_bypass_enable(struct fsl_tcon *tcon) FSL_TCON_CTRL1_TCON_BYPASS); } -static struct regmap_config fsl_tcon_regmap_config = { +static const struct regmap_config fsl_tcon_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, From 02f686d17c4305a0b5e2a9de749664dfe9c0f63e Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 25 Sep 2024 17:42:42 +0200 Subject: [PATCH 21/90] drm/mediatek: dp: Constify struct regmap_config `mtk_dp_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Reviewed-by: Matthias Brugger Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240925-drm-const-regmap-v1-3-e609d502401b@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/mediatek/mtk_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index d8796a904eca4..f0f6f402994a7 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -311,7 +311,7 @@ static const struct mtk_dp_efuse_fmt mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = { }, }; -static struct regmap_config mtk_dp_regmap_config = { +static const struct regmap_config mtk_dp_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, From 0bcbddb7ef0edb8b4ca994033128e955bb8b1b74 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 25 Sep 2024 17:42:43 +0200 Subject: [PATCH 22/90] drm/meson: Constify struct regmap_config `meson_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Reviewed-by: Neil Armstrong Reviewed-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://patchwork.freedesktop.org/patch/msgid/20240925-drm-const-regmap-v1-4-e609d502401b@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/meson/meson_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index bbb662d2f1848..7cace75a38af9 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -128,7 +128,7 @@ static bool meson_vpu_has_available_connectors(struct device *dev) return false; } -static struct regmap_config meson_regmap_config = { +static const struct regmap_config meson_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, From 6a92271233fb4789f69a9ba9410b23e2e5ab30e2 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 25 Sep 2024 17:42:44 +0200 Subject: [PATCH 23/90] drm/panel: ili9322: Constify struct regmap_bus `ili9322_regmap_bus` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Reviewed-by: Neil Armstrong Reviewed-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://patchwork.freedesktop.org/patch/msgid/20240925-drm-const-regmap-v1-5-e609d502401b@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/panel/panel-ilitek-ili9322.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c index 4a6dcfd781e83..94b7dfef3b5e2 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9322.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9322.c @@ -318,7 +318,7 @@ static int ili9322_regmap_spi_read(void *context, const void *reg, return spi_write_then_read(spi, buf, 1, val, 1); } -static struct regmap_bus ili9322_regmap_bus = { +static const struct regmap_bus ili9322_regmap_bus = { .write = ili9322_regmap_spi_write, .read = ili9322_regmap_spi_read, .reg_format_endian_default = REGMAP_ENDIAN_BIG, From 420fb223fe6049f5eecac0d28136df5bc5699ea2 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Wed, 25 Sep 2024 17:42:45 +0200 Subject: [PATCH 24/90] drm/sprd: Constify struct regmap_bus `regmap_txt_io` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Reviewed-by: Dmitry Baryshkov Reviewed-by: AngeloGioacchino Del Regno Link: https://patchwork.freedesktop.org/patch/msgid/20240925-drm-const-regmap-v1-6-e609d502401b@gmail.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/sprd/sprd_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/sprd/sprd_dsi.c b/drivers/gpu/drm/sprd/sprd_dsi.c index 0b69c140eab36..44a7a579660fd 100644 --- a/drivers/gpu/drm/sprd/sprd_dsi.c +++ b/drivers/gpu/drm/sprd/sprd_dsi.c @@ -209,7 +209,7 @@ static int regmap_tst_io_read(void *context, u32 reg, u32 *val) return 0; } -static struct regmap_bus regmap_tst_io = { +static const struct regmap_bus regmap_tst_io = { .reg_write = regmap_tst_io_write, .reg_read = regmap_tst_io_read, }; From 5b7abfb20ba15c0d6c52672874b99d9564ca876b Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Thu, 26 Sep 2024 07:55:50 +0200 Subject: [PATCH 25/90] drm: fsl-dcu: Use dev_err_probe fsl_dcu_drm_modeset_init can return -EPROBE_DEFER, so use dev_err_probe to remove an invalid error message and add it to deferral description. Signed-off-by: Alexander Stein Reviewed-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240926055552.1632448-1-alexander.stein@ew.tq-group.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 2b3f15f0dc568..e62f581282831 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -104,10 +104,8 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags) int ret; ret = fsl_dcu_drm_modeset_init(fsl_dev); - if (ret < 0) { - dev_err(dev->dev, "failed to initialize mode setting\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev->dev, ret, "failed to initialize mode setting\n"); ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret < 0) { From ffcde9e44d3e18fde3d18bfff8d9318935413bfd Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 26 Sep 2024 07:55:51 +0200 Subject: [PATCH 26/90] drm: fsl-dcu: enable PIXCLK on LS1021A The PIXCLK needs to be enabled in SCFG before accessing certain DCU registers, or the access will hang. For simplicity, the PIXCLK is enabled unconditionally, resulting in increased power consumption. Signed-off-by: Matthias Schiffer Signed-off-by: Alexander Stein Fixes: 109eee2f2a18 ("drm/layerscape: Add Freescale DCU DRM driver") Acked-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240926055552.1632448-2-alexander.stein@ew.tq-group.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/fsl-dcu/Kconfig | 1 + drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 15 +++++++++++++++ drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h | 3 +++ 3 files changed, 19 insertions(+) diff --git a/drivers/gpu/drm/fsl-dcu/Kconfig b/drivers/gpu/drm/fsl-dcu/Kconfig index 588ce2420af26..0e0f910ceb9f0 100644 --- a/drivers/gpu/drm/fsl-dcu/Kconfig +++ b/drivers/gpu/drm/fsl-dcu/Kconfig @@ -9,6 +9,7 @@ config DRM_FSL_DCU select DRM_PANEL select REGMAP_MMIO select VIDEOMODE_HELPERS + select MFD_SYSCON if SOC_LS1021A help Choose this option if you have an Freescale DCU chipset. If M is selected the module will be called fsl-dcu-drm. diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index e62f581282831..6886487adc94c 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -101,12 +101,27 @@ static void fsl_dcu_irq_uninstall(struct drm_device *dev) static int fsl_dcu_load(struct drm_device *dev, unsigned long flags) { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + struct regmap *scfg; int ret; ret = fsl_dcu_drm_modeset_init(fsl_dev); if (ret < 0) return dev_err_probe(dev->dev, ret, "failed to initialize mode setting\n"); + scfg = syscon_regmap_lookup_by_compatible("fsl,ls1021a-scfg"); + if (PTR_ERR(scfg) != -ENODEV) { + /* + * For simplicity, enable the PIXCLK unconditionally, + * resulting in increased power consumption. Disabling + * the clock in PM or on unload could be implemented as + * a future improvement. + */ + ret = regmap_update_bits(scfg, SCFG_PIXCLKCR, SCFG_PIXCLKCR_PXCEN, + SCFG_PIXCLKCR_PXCEN); + if (ret < 0) + return dev_err_probe(dev->dev, ret, "failed to enable pixclk\n"); + } + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret < 0) { dev_err(dev->dev, "failed to initialize vblank\n"); diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h index e2049a0e8a92a..566396013c04a 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h @@ -160,6 +160,9 @@ #define FSL_DCU_ARGB4444 12 #define FSL_DCU_YUV422 14 +#define SCFG_PIXCLKCR 0x28 +#define SCFG_PIXCLKCR_PXCEN BIT(31) + #define VF610_LAYER_REG_NUM 9 #define LS1021A_LAYER_REG_NUM 10 From c3f0b90f6ffc178bf158aeccae268276363d6945 Mon Sep 17 00:00:00 2001 From: Manikandan Muralidharan Date: Thu, 19 Sep 2024 14:45:47 +0530 Subject: [PATCH 27/90] dt-bindings: display: panel-simple: Document support for Microchip AC69T88A Add Microchip AC69T88A 5" LVDS interface (800x480) TFT LCD panel compatible string Signed-off-by: Manikandan Muralidharan Acked-by: Rob Herring (Arm) Link: https://patchwork.freedesktop.org/patch/msgid/20240919091548.430285-1-manikandan.m@microchip.com Signed-off-by: Dmitry Baryshkov --- .../devicetree/bindings/display/panel/panel-simple.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index b89e397905791..18b63f356bb4b 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -200,6 +200,8 @@ properties: - logictechno,lttd800480070-l2rt # Logic Technologies LTTD800480070-L6WH-RT 7” 800x480 TFT Resistive Touch Module - logictechno,lttd800480070-l6wh-rt + # Microchip AC69T88A 5" 800X480 LVDS interface TFT LCD Panel + - microchip,ac69t88a # Mitsubishi "AA070MC01 7.0" WVGA TFT LCD panel - mitsubishi,aa070mc01-ca1 # Mitsubishi AA084XE01 8.4" XGA TFT LCD panel From 40da1463cd6879f542238b36c1148f517927c595 Mon Sep 17 00:00:00 2001 From: Manikandan Muralidharan Date: Thu, 19 Sep 2024 14:45:48 +0530 Subject: [PATCH 28/90] drm/panel: simple: Add Microchip AC69T88A LVDS Display panel Add support for Microchip AC69T88A 5 inch TFT LCD 800x480 Display module with LVDS interface.The panel uses the Sitronix ST7262 800x480 Display driver Signed-off-by: Manikandan Muralidharan Signed-off-by: Dharma Balasubiramani Reviewed-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20240919091548.430285-2-manikandan.m@microchip.com Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/panel/panel-simple.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 86735430462fa..06381c6282097 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -4565,6 +4565,31 @@ static const struct panel_desc yes_optoelectronics_ytc700tlag_05_201c = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct drm_display_mode mchp_ac69t88a_mode = { + .clock = 25000, + .hdisplay = 800, + .hsync_start = 800 + 88, + .hsync_end = 800 + 88 + 5, + .htotal = 800 + 88 + 5 + 40, + .vdisplay = 480, + .vsync_start = 480 + 23, + .vsync_end = 480 + 23 + 5, + .vtotal = 480 + 23 + 5 + 1, +}; + +static const struct panel_desc mchp_ac69t88a = { + .modes = &mchp_ac69t88a_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 108, + .height = 65, + }, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode arm_rtsm_mode[] = { { .clock = 65000, @@ -5048,6 +5073,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "yes-optoelectronics,ytc700tlag-05-201c", .data = &yes_optoelectronics_ytc700tlag_05_201c, + }, { + .compatible = "microchip,ac69t88a", + .data = &mchp_ac69t88a, }, { /* Must be the last entry */ .compatible = "panel-dpi", From 4b173d34e35726d7727d3e5edc43bcab12654ab0 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Wed, 18 Sep 2024 15:48:41 +0800 Subject: [PATCH 29/90] drm/fsl-dcu: Remove redundant dev_err() There is no need to call the dev_err() function directly to print a custom message when handling an error from platform_get_irq() function as it is going to display an appropriate error message in case of a failure. Signed-off-by: Chen Ni Reviewed-by: Dmitry Baryshkov Acked-by: Alison Wang Link: https://patchwork.freedesktop.org/patch/msgid/20240918074841.2221210-1-nichen@iscas.ac.cn Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 6886487adc94c..91a48d774cf7d 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -287,10 +287,8 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) } fsl_dev->irq = platform_get_irq(pdev, 0); - if (fsl_dev->irq < 0) { - dev_err(dev, "failed to get irq\n"); + if (fsl_dev->irq < 0) return fsl_dev->irq; - } fsl_dev->regmap = devm_regmap_init_mmio(dev, base, &fsl_dcu_regmap_config); From 45608a3eb4902f32010a8328c0a01ccda4b38c9b Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Sun, 20 Oct 2024 21:37:41 +1300 Subject: [PATCH 30/90] drm: panel: nv3052c: correct spi_device_id for RG35XX panel The Anbernic RG35XX devices use an SPI LCD panel from an unknown OEM, with an NV3052C driver chip. As discussed previously, the integrating vendor and device name are preferred instead of the OEM serial. A previous patch corrected the device tree binding and of_device_id in the NV3052C driver, however the spi_device_id also needs correction. Correct the spi_device_id for the RG35XX panel. Signed-off-by: Ryan Walklin Fixes: 76dce2a96c0f ("drm: panel: nv3052c: Correct WL-355608-A8 panel compatible") Reviewed-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241020083836.175733-1-ryan@testtoast.com [DB: corrected the Fixes tag] Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/panel/panel-newvision-nv3052c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c index d3baccfe6286b..06e16a7c14a75 100644 --- a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c +++ b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c @@ -917,7 +917,7 @@ static const struct nv3052c_panel_info wl_355608_a8_panel_info = { static const struct spi_device_id nv3052c_ids[] = { { "ltk035c5444t", }, { "fs035vg158", }, - { "wl-355608-a8", }, + { "rg35xx-plus-panel", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(spi, nv3052c_ids); From 23f59d7baa1fe31823d223afe9cace1494bdf5b1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 16 Oct 2024 19:48:13 -0700 Subject: [PATCH 31/90] drm/fbdev-dma: remove obsolete kernel-doc references The kernel-doc comments in these 3 files was removed so remove the references to these files to prevent kernel-doc warnings. drivers/gpu/drm/drm_fbdev_dma.c:1: warning: no structured comments found drivers/gpu/drm/drm_fbdev_shmem.c:1: warning: no structured comments found drivers/gpu/drm/drm_fbdev_ttm.c:1: warning: no structured comments found Fixes: 731fddf4302e ("drm/fbdev-dma: Remove obsolete setup function") Signed-off-by: Randy Dunlap Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/all/20241002142250.07e1c46c@canb.auug.org.au/ Cc: David Airlie Cc: Simona Vetter Cc: Thomas Zimmermann Cc: dri-devel@lists.freedesktop.org Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20241017024813.61908-1-rdunlap@infradead.org --- Documentation/gpu/drm-kms-helpers.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index c3e58856f75b3..8cf2f041af470 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -110,15 +110,6 @@ fbdev Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c :doc: fbdev helpers -.. kernel-doc:: drivers/gpu/drm/drm_fbdev_dma.c - :export: - -.. kernel-doc:: drivers/gpu/drm/drm_fbdev_shmem.c - :export: - -.. kernel-doc:: drivers/gpu/drm/drm_fbdev_ttm.c - :export: - .. kernel-doc:: include/drm/drm_fb_helper.h :internal: From d9c12481f980bdad9cf6fdb742e49c5b50622795 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 8 Sep 2024 16:21:31 +0200 Subject: [PATCH 32/90] drm/meson: drop unused static dw_hdmi_dwc_write_bits static inline dw_hdmi_dwc_write_bits() function is not used at all: drivers/gpu/drm/meson/meson_dw_hdmi.c:276:20: error: unused function 'dw_hdmi_dwc_write_bits' [-Werror,-Wunused-function] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20240908-regmap-config-const-v1-1-28f349004811@linaro.org [narmstrong: s/staitc/static/] Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20240908-regmap-config-const-v1-1-28f349004811@linaro.org Link: https://patchwork.freedesktop.org/patch/msgid/20240902113320.903147-2-liaochen4@huawei.com --- drivers/gpu/drm/meson/meson_dw_hdmi.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index 5565f7777529f..b75db829b1daf 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -272,20 +272,6 @@ static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi, writeb(data, dw_hdmi->hdmitx + addr); } -/* Helper to change specific bits in controller registers */ -static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi, - unsigned int addr, - unsigned int mask, - unsigned int val) -{ - unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr); - - data &= ~mask; - data |= val; - - dw_hdmi->data->dwc_write(dw_hdmi, addr, data); -} - /* Bridge */ /* Setup PHY bandwidth modes */ From d704a2fe530caa7023949b513b8d1c364b5832fb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 3 Oct 2024 15:39:03 +0200 Subject: [PATCH 33/90] dt-bindings: display: bridge: tc358768: switch to bus-width "data-lines" property is way too similar to "data-lanes". It is also duplicating "bus-width" from video-interfaces.yaml schema. Deprecate "data-lines" and use the common property. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring (Arm) Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20241003133904.69244-1-krzysztof.kozlowski@linaro.org Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241003133904.69244-1-krzysztof.kozlowski@linaro.org --- .../devicetree/bindings/display/bridge/toshiba,tc358768.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml index 779d8c57f854d..bb5d3b5438003 100644 --- a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml +++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358768.yaml @@ -60,6 +60,10 @@ properties: data-lines: $ref: /schemas/types.yaml#/definitions/uint32 enum: [ 16, 18, 24 ] + deprecated: true + + bus-width: + enum: [ 16, 18, 24 ] port@1: $ref: /schemas/graph.yaml#/properties/port From bf7c079902c34f8b16ea3484de080a7bb4ad0367 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 3 Oct 2024 15:39:04 +0200 Subject: [PATCH 34/90] drm/bridge: tc358768: switch to bus-width "data-lines" property is way too similar to "data-lanes". It is also duplicating "bus-width" from video-interfaces.yaml schema. "data-lines" was deprecated in the bindings and "bus-width" is preferred, so parse it instead while keeping things backwards compatible. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20241003133904.69244-2-krzysztof.kozlowski@linaro.org Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241003133904.69244-2-krzysztof.kozlowski@linaro.org --- drivers/gpu/drm/bridge/tc358768.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 0e8813278a2fa..fc96fa5aab542 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -443,7 +443,9 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, ret = -EINVAL; ep = of_graph_get_endpoint_by_regs(host->dev->of_node, 0, 0); if (ep) { - ret = of_property_read_u32(ep, "data-lines", &priv->pd_lines); + ret = of_property_read_u32(ep, "bus-width", &priv->pd_lines); + if (ret) + ret = of_property_read_u32(ep, "data-lines", &priv->pd_lines); of_node_put(ep); } From c7671949791fb1142d0ae37343759d608a21bf28 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 10 Oct 2024 17:26:43 +0800 Subject: [PATCH 35/90] drm/bridge: ite-it66121: Drop hdmi_avi_infoframe_init() function call drm_hdmi_avi_infoframe_from_display_mode() called from it66121_bridge_mode_set() already calls hdmi_avi_infoframe_init() to initialize an HDMI AVI infoframe. So, drop the redundant hdmi_avi_infoframe_init() function call from it66121_bridge_mode_set(). Signed-off-by: Liu Ying Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20241010092643.1048116-1-victor.liu@nxp.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241010092643.1048116-1-victor.liu@nxp.com --- drivers/gpu/drm/bridge/ite-it66121.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 925e42f46cd87..35ae3f0e8f51f 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -770,8 +770,6 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, mutex_lock(&ctx->lock); - hdmi_avi_infoframe_init(&ctx->hdmi_avi_infoframe); - ret = drm_hdmi_avi_infoframe_from_display_mode(&ctx->hdmi_avi_infoframe, ctx->connector, adjusted_mode); if (ret) { From 096d518c43341c5d8b80948f6fbc90e916ffc7f2 Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Thu, 17 Oct 2024 10:55:55 +0200 Subject: [PATCH 36/90] dt-bindings: display: bridge: sil,sii9022: Add bus-width The SI9022 HDMI transmitter can be configured with a bus-width of 16, 18, or 24 bits. Introduce a bus-width property to the input endpoint, specifying the number of parallel RGB input bits connected to the transmitter. Signed-off-by: Wadim Egorov Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20241017085556.3045686-2-w.egorov@phytec.de Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241017085556.3045686-2-w.egorov@phytec.de --- .../bindings/display/bridge/sil,sii9022.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/display/bridge/sil,sii9022.yaml b/Documentation/devicetree/bindings/display/bridge/sil,sii9022.yaml index 5a69547ad3d79..1509c4535e53c 100644 --- a/Documentation/devicetree/bindings/display/bridge/sil,sii9022.yaml +++ b/Documentation/devicetree/bindings/display/bridge/sil,sii9022.yaml @@ -81,9 +81,22 @@ properties: properties: port@0: - $ref: /schemas/graph.yaml#/properties/port + unevaluatedProperties: false + $ref: /schemas/graph.yaml#/$defs/port-base description: Parallel RGB input port + properties: + endpoint: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + unevaluatedProperties: false + + properties: + bus-width: + description: + Endpoint bus width. + enum: [ 16, 18, 24 ] + default: 24 + port@1: $ref: /schemas/graph.yaml#/properties/port description: HDMI output port From d284ccd8588c9b87222ec666a1da57f197023e5f Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Thu, 17 Oct 2024 10:55:56 +0200 Subject: [PATCH 37/90] drm/bridge: sii902x: Set input bus format based on bus-width Introduce a bus-width property to define the number of parallel RGB input pins connected to the transmitter. The input bus formats are updated accordingly. If the property is not specified, default to 24-bit bus-width. Signed-off-by: Wadim Egorov Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20241017085556.3045686-3-w.egorov@phytec.de Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241017085556.3045686-3-w.egorov@phytec.de --- drivers/gpu/drm/bridge/sii902x.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 7f91b0db161ed..9be9cc5b90259 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -180,6 +180,8 @@ struct sii902x { struct gpio_desc *reset_gpio; struct i2c_mux_core *i2cmux; bool sink_is_hdmi; + u32 bus_width; + /* * Mutex protects audio and video functions from interfering * each other, by keeping their i2c command sequences atomic. @@ -477,6 +479,8 @@ static u32 *sii902x_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, u32 output_fmt, unsigned int *num_input_fmts) { + + struct sii902x *sii902x = bridge_to_sii902x(bridge); u32 *input_fmts; *num_input_fmts = 0; @@ -485,7 +489,20 @@ static u32 *sii902x_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, if (!input_fmts) return NULL; - input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + switch (sii902x->bus_width) { + case 16: + input_fmts[0] = MEDIA_BUS_FMT_RGB565_1X16; + break; + case 18: + input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X18; + break; + case 24: + input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24; + break; + default: + return NULL; + } + *num_input_fmts = 1; return input_fmts; @@ -1167,6 +1184,11 @@ static int sii902x_probe(struct i2c_client *client) return PTR_ERR(sii902x->reset_gpio); } + sii902x->bus_width = 24; + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 0, -1); + if (endpoint) + of_property_read_u32(endpoint, "bus-width", &sii902x->bus_width); + endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); if (endpoint) { struct device_node *remote = of_graph_get_remote_port_parent(endpoint); From 3ccddc3991beef2705e8097b01ae07054bf11022 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 21 Oct 2024 16:37:46 +0300 Subject: [PATCH 38/90] drm/fbdev: fix drm_fb_helper_deferred_io() build failure The drm_fb_helper_deferred_io() uses struct fb_deferred_io_pageref, which isn't available without CONFIG_FB_DEFERRED_IO. Put the function under corresponding #ifdef to fix build failure if deferred I/O isn't enabled. Fixes: 8058944f5226 ("drm/fbdev: Select fbdev I/O helpers from modules that require them") Signed-off-by: Dmitry Baryshkov Reviewed-by: Thomas Zimmermann Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20241021-fix-drm-deferred-v2-1-db1de4c6b042@linaro.org --- drivers/gpu/drm/drm_fb_helper.c | 2 ++ include/drm/drm_fb_helper.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d5e8994345bb9..c9008113111ba 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -697,6 +697,7 @@ void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u3 } EXPORT_SYMBOL(drm_fb_helper_damage_area); +#ifdef CONFIG_FB_DEFERRED_IO /** * drm_fb_helper_deferred_io() - fbdev deferred_io callback function * @info: fb_info struct pointer @@ -740,6 +741,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagerefli } } EXPORT_SYMBOL(drm_fb_helper_deferred_io); +#endif /** * drm_fb_helper_set_suspend - wrapper around fb_set_suspend diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 699f2790b9ac1..8426b9921a03b 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -256,7 +256,9 @@ void drm_fb_helper_fill_info(struct fb_info *info, void drm_fb_helper_damage_range(struct fb_info *info, off_t off, size_t len); void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u32 height); +#ifdef CONFIG_FB_DEFERRED_IO void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist); +#endif void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend); void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, @@ -361,10 +363,12 @@ static inline int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, return 0; } +#ifdef CONFIG_FB_DEFERRED_IO static inline void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagelist) { } +#endif static inline void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend) From ef214002e6b38c428cb2b973951c254ba3260de6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 15 Oct 2024 07:29:57 +0000 Subject: [PATCH 39/90] drm/imx: parallel-display: add legacy bridge Kconfig dependency The driver now uses the legacy bridge helper code but can be configured to get built without it: ERROR: modpost: "devm_imx_drm_legacy_bridge" [drivers/gpu/drm/imx/ipuv3/parallel-display.ko] undefined! Add the required dependency, same as in the ldb driver. Fixes: f94b9707a1c9 ("drm/imx: parallel-display: switch to imx_legacy_bridge / drm_bridge_connector") Signed-off-by: Arnd Bergmann Reviewed-by: Dmitry Baryshkov Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20241015073004.4066457-1-arnd@kernel.org --- drivers/gpu/drm/imx/ipuv3/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/imx/ipuv3/Kconfig b/drivers/gpu/drm/imx/ipuv3/Kconfig index 0ea1feb846722..acaf250890019 100644 --- a/drivers/gpu/drm/imx/ipuv3/Kconfig +++ b/drivers/gpu/drm/imx/ipuv3/Kconfig @@ -15,6 +15,7 @@ config DRM_IMX_PARALLEL_DISPLAY depends on DRM_IMX select DRM_BRIDGE select DRM_BRIDGE_CONNECTOR + select DRM_IMX_LEGACY_BRIDGE select DRM_PANEL_BRIDGE select VIDEOMODE_HELPERS From df606be44c168417604a708e3d895f94b262d407 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 15 Oct 2024 07:29:58 +0000 Subject: [PATCH 40/90] drm/imx: legacy-bridge: add MODULE_DESCRIPTION Every module should have a description, without this we get a build time warning. Fixes: cc3e8a216d6b ("drm/imx: add internal bridge handling display-timings DT node") Signed-off-by: Arnd Bergmann Reviewed-by: Dmitry Baryshkov Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20241015073004.4066457-2-arnd@kernel.org --- drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c index 07a78a02a8846..3ebf0b9866de1 100644 --- a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c +++ b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c @@ -85,3 +85,4 @@ struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev, EXPORT_SYMBOL_GPL(devm_imx_drm_legacy_bridge); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Freescale i.MX DRM bridge driver for legacy DT bindings"); From 2320c9e6a768d135c7b0039995182bb1a4e4fd22 Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Mon, 21 Oct 2024 12:50:28 +0200 Subject: [PATCH 41/90] drm/sched: memset() 'job' in drm_sched_job_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_sched_job_init() has no control over how users allocate struct drm_sched_job. Unfortunately, the function can also not set some struct members such as job->sched. This could theoretically lead to UB by users dereferencing the struct's pointer members too early. It is easier to debug such issues if these pointers are initialized to NULL, so dereferencing them causes a NULL pointer exception. Accordingly, drm_sched_entity_init() does precisely that and initializes its struct with memset(). Initialize parameter "job" to 0 in drm_sched_job_init(). Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20241021105028.19794-2-pstanner@redhat.com Reviewed-by: Christian König --- drivers/gpu/drm/scheduler/sched_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index dab8cca79eb73..2e0e5a9577d13 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -796,6 +796,14 @@ int drm_sched_job_init(struct drm_sched_job *job, return -EINVAL; } + /* + * We don't know for sure how the user has allocated. Thus, zero the + * struct so that unallowed (i.e., too early) usage of pointers that + * this function does not set is guaranteed to lead to a NULL pointer + * exception instead of UB. + */ + memset(job, 0, sizeof(*job)); + job->entity = entity; job->credits = credits; job->s_fence = drm_sched_fence_alloc(entity, owner); From 91e21479c81dd4e9e22a78d7446f92f6b96a7284 Mon Sep 17 00:00:00 2001 From: Dzmitry Sankouski Date: Mon, 21 Oct 2024 16:52:36 +0300 Subject: [PATCH 42/90] drm/panel: s6e3ha8: add static modifier to supply list Add 'static' modifier to panel supply list. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202410160810.0jaNgxB2-lkp@intel.com/ Fixes: 779679d3c164 ("drm/panel: Add support for S6E3HA8 panel driver") Signed-off-by: Dzmitry Sankouski Reviewed-by: Neil Armstrong Link: https://lore.kernel.org/r/20241021-starqltechn_upstream_integration_panel_fix-v1-1-81776e89052a@gmail.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/20241021-starqltechn_upstream_integration_panel_fix-v1-1-81776e89052a@gmail.com --- drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c index 76b5a6ea610d5..64c6f7d45bed3 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c @@ -24,7 +24,7 @@ struct s6e3ha8 { struct regulator_bulk_data *supplies; }; -const struct regulator_bulk_data s6e3ha8_supplies[] = { +static const struct regulator_bulk_data s6e3ha8_supplies[] = { { .supply = "vdd3" }, { .supply = "vci" }, { .supply = "vddr" }, From 3ced1c68751299c0cdf6a1ceeafdbe77db7d4956 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 19 Oct 2024 00:49:12 +0300 Subject: [PATCH 43/90] drm/display: bridge_connector: handle ycbcr_420_allowed Follow the interlace_allowed example and calculate drm_connector's ycbcr_420_allowed flag as AND of all drm_bridge's ycbcr_420_allowed flags in a chain. This is one of the gaps between several bridge-specific connector implementations and drm_bridge_connector. Reviewed-by: Neil Armstrong Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20241019-bridge-yuv420-v1-1-d74efac9e4e6@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/display/drm_bridge_connector.c | 6 ++++-- include/drm/drm_bridge.h | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 3da5b8bf8259f..320c297008aaa 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -397,11 +397,11 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, bridge_connector->encoder = encoder; /* - * TODO: Handle doublescan_allowed, stereo_allowed and - * ycbcr_420_allowed. + * TODO: Handle doublescan_allowed and stereo_allowed. */ connector = &bridge_connector->base; connector->interlace_allowed = true; + connector->ycbcr_420_allowed = true; /* * Initialise connector status handling. First locate the furthest @@ -414,6 +414,8 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, drm_for_each_bridge_in_chain(encoder, bridge) { if (!bridge->interlace_allowed) connector->interlace_allowed = false; + if (!bridge->ycbcr_420_allowed) + connector->ycbcr_420_allowed = false; if (bridge->ops & DRM_BRIDGE_OP_EDID) bridge_connector->bridge_edid = bridge; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 75019d16be643..e8d735b7f6a48 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -802,6 +802,11 @@ struct drm_bridge { * modes. */ bool interlace_allowed; + /** + * @ycbcr_420_allowed: Indicate that the bridge can handle YCbCr 420 + * output. + */ + bool ycbcr_420_allowed; /** * @pre_enable_prev_first: The bridge requires that the prev * bridge @pre_enable function is called before its @pre_enable, From 58e6d652d138ef163d0b6b4d19f0fc9d4e8519fa Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 19 Oct 2024 00:49:13 +0300 Subject: [PATCH 44/90] drm/atomic: add interlaced and ycbcr_420 flags to connector's state dump Although the interlace_allowed and ycbcr_420_allowed flags are a part of the struct drm_connector rather than struct drm_connector_state, still include them into state dump in order to ease debugging of the setup issues. Reviewed-by: Neil Armstrong Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20241019-bridge-yuv420-v1-2-d74efac9e4e6@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/drm_atomic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 0fc99da93afe1..9ea2611770f43 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1132,6 +1132,8 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); drm_printf(p, "\tself_refresh_aware=%d\n", state->self_refresh_aware); + drm_printf(p, "\tinterlace_allowed=%d\n", connector->interlace_allowed); + drm_printf(p, "\tycbcr_420_allowed=%d\n", connector->ycbcr_420_allowed); drm_printf(p, "\tmax_requested_bpc=%d\n", state->max_requested_bpc); drm_printf(p, "\tcolorspace=%s\n", drm_get_colorspace_name(state->colorspace)); From d5cd8280c52bad44d5943fa7501bf9f20718d432 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 19 Oct 2024 00:49:14 +0300 Subject: [PATCH 45/90] drm/bridge: display-connector: allow YCbCr 420 for HDMI and DP Allow YCbCr 420 output for HDMI and DisplayPort connectors. Other bridges in the chain still might limit YCbCr 420 support on the corresponding connector. Reviewed-by: Neil Armstrong Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20241019-bridge-yuv420-v1-3-d74efac9e4e6@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/bridge/display-connector.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index ab8e00baf3f16..aab9ce7be94c5 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -270,6 +270,10 @@ static int display_connector_probe(struct platform_device *pdev) /* All the supported connector types support interlaced modes. */ conn->bridge.interlace_allowed = true; + if (type == DRM_MODE_CONNECTOR_HDMIA || + type == DRM_MODE_CONNECTOR_DisplayPort) + conn->bridge.ycbcr_420_allowed = true; + /* Get the optional connector label. */ of_property_read_string(pdev->dev.of_node, "label", &label); From c44a0faf5397134b3100c00cc8a8d72528bc422a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 19 Oct 2024 00:49:15 +0300 Subject: [PATCH 46/90] drm/bridge: aux: allow interlaced and YCbCr 420 output As both aux bridges are merely passthrough bridges, mark them as supporting interlaced and YCbCr 420 data. Other bridges in the chain still might limit interlaced and YCbCr 420 data support on the corresponding connector. Reviewed-by: Neil Armstrong Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20241019-bridge-yuv420-v1-4-d74efac9e4e6@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/bridge/aux-bridge.c | 4 ++++ drivers/gpu/drm/bridge/aux-hpd-bridge.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/bridge/aux-bridge.c b/drivers/gpu/drm/bridge/aux-bridge.c index b29980f95379e..e3e95ccec1aef 100644 --- a/drivers/gpu/drm/bridge/aux-bridge.c +++ b/drivers/gpu/drm/bridge/aux-bridge.c @@ -120,6 +120,10 @@ static int drm_aux_bridge_probe(struct auxiliary_device *auxdev, data->bridge.funcs = &drm_aux_bridge_funcs; data->bridge.of_node = data->dev->of_node; + /* passthrough data, allow everything */ + data->bridge.interlace_allowed = true; + data->bridge.ycbcr_420_allowed = true; + return devm_drm_bridge_add(data->dev, &data->bridge); } diff --git a/drivers/gpu/drm/bridge/aux-hpd-bridge.c b/drivers/gpu/drm/bridge/aux-hpd-bridge.c index 6886db2d9e00c..48f297c78ee67 100644 --- a/drivers/gpu/drm/bridge/aux-hpd-bridge.c +++ b/drivers/gpu/drm/bridge/aux-hpd-bridge.c @@ -180,6 +180,10 @@ static int drm_aux_hpd_bridge_probe(struct auxiliary_device *auxdev, data->bridge.ops = DRM_BRIDGE_OP_HPD; data->bridge.type = id->driver_data; + /* passthrough data, allow everything */ + data->bridge.interlace_allowed = true; + data->bridge.ycbcr_420_allowed = true; + auxiliary_set_drvdata(auxdev, data); return devm_drm_bridge_add(data->dev, &data->bridge); From 785324db2d7a44e866161b6309fbda9a2178d455 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 19 Oct 2024 00:49:16 +0300 Subject: [PATCH 47/90] drm/msm/dp: migrate the ycbcr_420_allowed to drm_bridge Instead of forcing the ycbcr_420_allowed flag to be set on the created drm_connector, set it on the drm_bridge instance and allow drm_bridge_connecgtor to propagate it to the drm_connector. Reviewed-by: Neil Armstrong Acked-by: Abhinav Kumar Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20241019-bridge-yuv420-v1-5-d74efac9e4e6@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/dp/dp_display.c | 4 ++-- drivers/gpu/drm/msm/dp/dp_drm.c | 10 ++++------ drivers/gpu/drm/msm/dp/dp_drm.h | 7 ++++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index e1228fb093ee0..0ddd8abca499d 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -1467,14 +1467,14 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, dp_priv = container_of(dp_display, struct dp_display_private, dp_display); - ret = dp_bridge_init(dp_display, dev, encoder); + ret = dp_bridge_init(dp_display, dev, encoder, yuv_supported); if (ret) { DRM_DEV_ERROR(dev->dev, "failed to create dp bridge: %d\n", ret); return ret; } - dp_display->connector = dp_drm_connector_init(dp_display, encoder, yuv_supported); + dp_display->connector = dp_drm_connector_init(dp_display, encoder); if (IS_ERR(dp_display->connector)) { ret = PTR_ERR(dp_display->connector); DRM_DEV_ERROR(dev->dev, diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 1b9be5bd97f12..7eb1621f9e7f5 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -289,7 +289,7 @@ static const struct drm_bridge_funcs edp_bridge_ops = { }; int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev, - struct drm_encoder *encoder) + struct drm_encoder *encoder, bool yuv_supported) { int rc; struct msm_dp_bridge *dp_bridge; @@ -304,6 +304,7 @@ int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev, bridge = &dp_bridge->bridge; bridge->funcs = dp_display->is_edp ? &edp_bridge_ops : &dp_bridge_ops; bridge->type = dp_display->connector_type; + bridge->ycbcr_420_allowed = yuv_supported; /* * Many ops only make sense for DP. Why? @@ -351,8 +352,8 @@ int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev, } /* connector initialization */ -struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder, - bool yuv_supported) +struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, + struct drm_encoder *encoder) { struct drm_connector *connector = NULL; @@ -363,9 +364,6 @@ struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct dr if (!dp_display->is_edp) drm_connector_attach_dp_subconnector_property(connector); - if (yuv_supported) - connector->ycbcr_420_allowed = true; - drm_connector_attach_encoder(connector, encoder); return connector; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h index 45e57ac25a4d9..ae632fcc407c7 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.h +++ b/drivers/gpu/drm/msm/dp/dp_drm.h @@ -19,10 +19,11 @@ struct msm_dp_bridge { #define to_dp_bridge(x) container_of((x), struct msm_dp_bridge, bridge) -struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder, - bool yuv_supported); +struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, + struct drm_encoder *encoder); int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev, - struct drm_encoder *encoder); + struct drm_encoder *encoder, + bool yuv_supported); void dp_bridge_atomic_enable(struct drm_bridge *drm_bridge, struct drm_bridge_state *old_bridge_state); From 8a8fed657d0427f6765a48c93152a8d86cfe613c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 19 Oct 2024 00:49:17 +0300 Subject: [PATCH 48/90] drm/bridge: dw-hdmi: set bridge's ycbcr_420_allowed flag Set the drm_bridge's ycbcr_420_allowed flag if the YCbCr 420 output is supported by the hardware. Cc: Alexander Stein Reviewed-by: Neil Armstrong Reviewed-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20241019-bridge-yuv420-v1-6-d74efac9e4e6@linaro.org Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 0031f3c54882c..996733ed2c004 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3503,6 +3503,9 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, hdmi->bridge.of_node = pdev->dev.of_node; hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + if (hdmi->version >= 0x200a) + hdmi->bridge.ycbcr_420_allowed = plat_data->ycbcr_420_allowed; + memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = dev; pdevinfo.id = PLATFORM_DEVID_AUTO; From f2cab5a0e8c3a4a49dc5b0f7088bcc12d65e71e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 3 Oct 2024 14:18:47 +0300 Subject: [PATCH 49/90] drm/imx/dcss: Fix 64bit divisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the appropriate 64bit division helpers to make the code build on 32bit architectures. Cc: Laurentiu Palcu Cc: Lucas Stach Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20241003111851.10453-7-ville.syrjala@linux.intel.com Reviewed-by: Laurentiu Palcu --- drivers/gpu/drm/imx/dcss/dcss-scaler.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imx/dcss/dcss-scaler.c b/drivers/gpu/drm/imx/dcss/dcss-scaler.c index 825728c356ffb..32c3f46b21dae 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-scaler.c +++ b/drivers/gpu/drm/imx/dcss/dcss-scaler.c @@ -136,7 +136,7 @@ static int div_q(int A, int B) else temp -= B / 2; - result = (int)(temp / B); + result = div_s64(temp, B); return result; } @@ -239,7 +239,7 @@ static void dcss_scaler_gaussian_filter(int fc_q, bool use_5_taps, ll_temp = coef[phase][i]; ll_temp <<= PSC_COEFF_PRECISION; ll_temp += sum >> 1; - ll_temp /= sum; + ll_temp = div_s64(ll_temp, sum); coef[phase][i] = (int)ll_temp; } } From 004555a18d57fbad42553c2e98e00a054bbe618c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 3 Oct 2024 14:18:48 +0300 Subject: [PATCH 50/90] drm/imx/dcss: Allow build with COMPILE_TEST=y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow imx/dcss to be built with COMPILE_TEST=y for greater coverage. Builds fine on x86/x86_64 at least. Cc: Laurentiu Palcu Cc: Lucas Stach Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20241003111851.10453-8-ville.syrjala@linux.intel.com Reviewed-by: Laurentiu Palcu --- drivers/gpu/drm/imx/dcss/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/imx/dcss/Kconfig b/drivers/gpu/drm/imx/dcss/Kconfig index 9d58f41bd00cd..e014ed3ae66c9 100644 --- a/drivers/gpu/drm/imx/dcss/Kconfig +++ b/drivers/gpu/drm/imx/dcss/Kconfig @@ -7,7 +7,7 @@ config DRM_IMX_DCSS select DRM_BRIDGE_CONNECTOR select DRM_GEM_DMA_HELPER select VIDEOMODE_HELPERS - depends on DRM && ARCH_MXC && ARM64 + depends on DRM && ((ARCH_MXC && ARM64) || COMPILE_TEST) help Choose this if you have a NXP i.MX8MQ based system and want to use the Display Controller Subsystem. This option enables DCSS support. From d2db0b8fd70bb216eb96ad1a32c158889e247c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 3 Oct 2024 14:18:50 +0300 Subject: [PATCH 51/90] drm/mediatek: Allow build with COMPILE_TEST=y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow mediatek to be built with COMPILE_TEST=y for greater coverage. Builds fine on x86/x86_64 at least. Cc: Chun-Kuang Hu Cc: Philipp Zabel Cc: linux-mediatek@lists.infradead.org Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20241003111851.10453-10-ville.syrjala@linux.intel.com Reviewed-by: CK Hu --- drivers/gpu/drm/mediatek/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig index 3d2436272ff24..2e0e7c4079b62 100644 --- a/drivers/gpu/drm/mediatek/Kconfig +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -2,9 +2,9 @@ config DRM_MEDIATEK tristate "DRM Support for Mediatek SoCs" depends on DRM - depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST) + depends on (ARCH_MEDIATEK && ARM) || COMPILE_TEST depends on COMMON_CLK - depends on HAVE_ARM_SMCCC + depends on HAVE_ARM_SMCCC || COMPILE_TEST depends on OF depends on MTK_MMSYS select DRM_CLIENT_SELECTION From d4e4e498bec6c16d303ff55c0211d5c11e81e379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 3 Oct 2024 14:18:51 +0300 Subject: [PATCH 52/90] drm/meson: Allow build with COMPILE_TEST=y MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow meson to be built with COMPILE_TEST=y for greater coverage. Builds fine on x86/x86_64 at least. Cc: Neil Armstrong Cc: linux-amlogic@lists.infradead.org Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20241003111851.10453-11-ville.syrjala@linux.intel.com Reviewed-by: Neil Armstrong --- drivers/gpu/drm/meson/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig index b410e0d8015a1..417f79829cf8f 100644 --- a/drivers/gpu/drm/meson/Kconfig +++ b/drivers/gpu/drm/meson/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_MESON tristate "DRM Support for Amlogic Meson Display Controller" - depends on DRM && OF && (ARM || ARM64) + depends on DRM && OF && (ARM || ARM64 || COMPILE_TEST) depends on ARCH_MESON || COMPILE_TEST select DRM_CLIENT_SELECTION select DRM_KMS_HELPER From 581d1f8248550f2b67847e6d84f29fbe3751ea0a Mon Sep 17 00:00:00 2001 From: Steven Price Date: Fri, 25 Oct 2024 15:00:07 +0100 Subject: [PATCH 53/90] drm/panfrost: Remove unused id_mask from struct panfrost_model The id_mask field of struct panfrost_model has never been used. Fixes: f3ba91228e8e ("drm/panfrost: Add initial panfrost driver") Signed-off-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patchwork.freedesktop.org/patch/msgid/20241025140008.385081-1-steven.price@arm.com --- drivers/gpu/drm/panfrost/panfrost_gpu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index f19f918e2330e..f5abde3866fbb 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -177,7 +177,6 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev) struct panfrost_model { const char *name; u32 id; - u32 id_mask; u64 features; u64 issues; struct { From 3ae80b375739495e36fc6143ff27716fe390a13e Mon Sep 17 00:00:00 2001 From: Philipp Stanner Date: Wed, 23 Oct 2024 16:15:31 +0200 Subject: [PATCH 54/90] drm/sched: warn about drm_sched_job_init()'s partial init drm_sched_job_init()'s name suggests that after the function succeeded, parameter "job" will be fully initialized. This is not the case; some members are only later set, notably drm_sched_job.sched by drm_sched_job_arm(). Document that drm_sched_job_init() does not set all struct members. Document the lifetime of drm_sched_job.sched. Reviewed-by: Matthew Brost Signed-off-by: Philipp Stanner Link: https://patchwork.freedesktop.org/patch/msgid/20241023141530.113370-2-pstanner@redhat.com --- drivers/gpu/drm/scheduler/sched_main.c | 4 ++++ include/drm/gpu_scheduler.h | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 2e0e5a9577d13..2f1b514ff4cfa 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -771,6 +771,10 @@ EXPORT_SYMBOL(drm_sched_resubmit_jobs); * Drivers must make sure drm_sched_job_cleanup() if this function returns * successfully, even when @job is aborted before drm_sched_job_arm() is called. * + * Note that this function does not assign a valid value to each struct member + * of struct drm_sched_job. Take a look at that struct's documentation to see + * who sets which struct member with what lifetime. + * * WARNING: amdgpu abuses &drm_sched.ready to signal when the hardware * has died, which can mean that there's no valid runqueue for a @entity. * This function returns -ENOENT in this case (which probably should be -EIO as diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index ab161289d1bf6..95e17504e46a3 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -340,6 +340,14 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f); struct drm_sched_job { struct spsc_node queue_node; struct list_head list; + + /** + * @sched: + * + * The scheduler this job is or will be scheduled on. Gets set by + * drm_sched_job_arm(). Valid until drm_sched_backend_ops.free_job() + * has finished. + */ struct drm_gpu_scheduler *sched; struct drm_sched_fence *s_fence; From 93accc16a8d45a44e1f51fdf2c6014feedb13d27 Mon Sep 17 00:00:00 2001 From: Jeffrey Hugo Date: Mon, 21 Oct 2024 14:03:55 -0600 Subject: [PATCH 55/90] accel/qaic: Add crashdump to Sahara The Sahara protocol has a crashdump functionality. In the hello exchange, the device can advertise it has a memory dump available for the host to collect. Instead of the device making requests of the host, the host requests data from the device which can be later analyzed. Implement this functionality and utilize the devcoredump framework for handing the dump over to userspace. Similar to how firmware loading in Sahara involves multiple files, crashdump can consist of multiple files for different parts of the dump. Structure these into a single buffer that userspace can parse and extract the original files from. Reviewed-by: Carl Vanderlip Signed-off-by: Jeffrey Hugo Reviewed-by: Bjorn Andersson Link: https://patchwork.freedesktop.org/patch/msgid/20241021200355.544126-1-quic_jhugo@quicinc.com --- drivers/accel/qaic/sahara.c | 388 +++++++++++++++++++++++++++++++++++- 1 file changed, 381 insertions(+), 7 deletions(-) diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c index bf94bbab6be5a..6d772143d6125 100644 --- a/drivers/accel/qaic/sahara.c +++ b/drivers/accel/qaic/sahara.c @@ -2,6 +2,7 @@ /* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */ +#include #include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include #include #include "sahara.h" @@ -36,12 +38,14 @@ #define SAHARA_PACKET_MAX_SIZE 0xffffU /* MHI_MAX_MTU */ #define SAHARA_TRANSFER_MAX_SIZE 0x80000 +#define SAHARA_READ_MAX_SIZE 0xfff0U /* Avoid unaligned requests */ #define SAHARA_NUM_TX_BUF DIV_ROUND_UP(SAHARA_TRANSFER_MAX_SIZE,\ SAHARA_PACKET_MAX_SIZE) #define SAHARA_IMAGE_ID_NONE U32_MAX #define SAHARA_VERSION 2 #define SAHARA_SUCCESS 0 +#define SAHARA_TABLE_ENTRY_STR_LEN 20 #define SAHARA_MODE_IMAGE_TX_PENDING 0x0 #define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1 @@ -53,6 +57,8 @@ #define SAHARA_END_OF_IMAGE_LENGTH 0x10 #define SAHARA_DONE_LENGTH 0x8 #define SAHARA_RESET_LENGTH 0x8 +#define SAHARA_MEM_DEBUG64_LENGTH 0x18 +#define SAHARA_MEM_READ64_LENGTH 0x18 struct sahara_packet { __le32 cmd; @@ -80,18 +86,95 @@ struct sahara_packet { __le32 image; __le32 status; } end_of_image; + struct { + __le64 table_address; + __le64 table_length; + } memory_debug64; + struct { + __le64 memory_address; + __le64 memory_length; + } memory_read64; }; }; +struct sahara_debug_table_entry64 { + __le64 type; + __le64 address; + __le64 length; + char description[SAHARA_TABLE_ENTRY_STR_LEN]; + char filename[SAHARA_TABLE_ENTRY_STR_LEN]; +}; + +struct sahara_dump_table_entry { + u64 type; + u64 address; + u64 length; + char description[SAHARA_TABLE_ENTRY_STR_LEN]; + char filename[SAHARA_TABLE_ENTRY_STR_LEN]; +}; + +#define SAHARA_DUMP_V1_MAGIC 0x1234567890abcdef +#define SAHARA_DUMP_V1_VER 1 +struct sahara_memory_dump_meta_v1 { + u64 magic; + u64 version; + u64 dump_size; + u64 table_size; +}; + +/* + * Layout of crashdump provided to user via devcoredump + * +------------------------------------------+ + * | Crashdump Meta structure | + * | type: struct sahara_memory_dump_meta_v1 | + * +------------------------------------------+ + * | Crashdump Table | + * | type: array of struct | + * | sahara_dump_table_entry | + * | | + * | | + * +------------------------------------------+ + * | Crashdump | + * | | + * | | + * | | + * | | + * | | + * +------------------------------------------+ + * + * First is the metadata header. Userspace can use the magic number to verify + * the content type, and then check the version for the rest of the format. + * New versions should keep the magic number location/value, and version + * location, but increment the version value. + * + * For v1, the metadata lists the size of the entire dump (header + table + + * dump) and the size of the table. Then the dump image table, which describes + * the contents of the dump. Finally all the images are listed in order, with + * no deadspace in between. Userspace can use the sizes listed in the image + * table to reconstruct the individual images. + */ + struct sahara_context { struct sahara_packet *tx[SAHARA_NUM_TX_BUF]; struct sahara_packet *rx; - struct work_struct work; + struct work_struct fw_work; + struct work_struct dump_work; struct mhi_device *mhi_dev; const char **image_table; u32 table_size; u32 active_image_id; const struct firmware *firmware; + u64 dump_table_address; + u64 dump_table_length; + size_t rx_size; + size_t rx_size_requested; + void *mem_dump; + size_t mem_dump_sz; + struct sahara_dump_table_entry *dump_image; + u64 dump_image_offset; + void *mem_dump_freespace; + u64 dump_images_left; + bool is_mem_dump_mode; }; static const char *aic100_image_table[] = { @@ -153,6 +236,8 @@ static void sahara_send_reset(struct sahara_context *context) { int ret; + context->is_mem_dump_mode = false; + context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD); context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH); @@ -186,7 +271,8 @@ static void sahara_hello(struct sahara_context *context) } if (le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_PENDING && - le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_COMPLETE) { + le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_COMPLETE && + le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_MEMORY_DEBUG) { dev_err(&context->mhi_dev->dev, "Unsupported hello packet - mode %d\n", le32_to_cpu(context->rx->hello.mode)); return; @@ -320,9 +406,70 @@ static void sahara_end_of_image(struct sahara_context *context) dev_dbg(&context->mhi_dev->dev, "Unable to send done response %d\n", ret); } +static void sahara_memory_debug64(struct sahara_context *context) +{ + int ret; + + dev_dbg(&context->mhi_dev->dev, + "MEMORY DEBUG64 cmd received. length:%d table_address:%#llx table_length:%#llx\n", + le32_to_cpu(context->rx->length), + le64_to_cpu(context->rx->memory_debug64.table_address), + le64_to_cpu(context->rx->memory_debug64.table_length)); + + if (le32_to_cpu(context->rx->length) != SAHARA_MEM_DEBUG64_LENGTH) { + dev_err(&context->mhi_dev->dev, "Malformed memory debug64 packet - length %d\n", + le32_to_cpu(context->rx->length)); + return; + } + + context->dump_table_address = le64_to_cpu(context->rx->memory_debug64.table_address); + context->dump_table_length = le64_to_cpu(context->rx->memory_debug64.table_length); + + if (context->dump_table_length % sizeof(struct sahara_debug_table_entry64) != 0 || + !context->dump_table_length) { + dev_err(&context->mhi_dev->dev, "Malformed memory debug64 packet - table length %lld\n", + context->dump_table_length); + return; + } + + /* + * From this point, the protocol flips. We make memory_read requests to + * the device, and the device responds with the raw data. If the device + * has an error, it will send an End of Image command. First we need to + * request the memory dump table so that we know where all the pieces + * of the dump are that we can consume. + */ + + context->is_mem_dump_mode = true; + + /* + * Assume that the table is smaller than our MTU so that we can read it + * in one shot. The spec does not put an upper limit on the table, but + * no known device will exceed this. + */ + if (context->dump_table_length > SAHARA_PACKET_MAX_SIZE) { + dev_err(&context->mhi_dev->dev, "Memory dump table length %lld exceeds supported size. Discarding dump\n", + context->dump_table_length); + sahara_send_reset(context); + return; + } + + context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD); + context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH); + context->tx[0]->memory_read64.memory_address = cpu_to_le64(context->dump_table_address); + context->tx[0]->memory_read64.memory_length = cpu_to_le64(context->dump_table_length); + + context->rx_size_requested = context->dump_table_length; + + ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0], + SAHARA_MEM_READ64_LENGTH, MHI_EOT); + if (ret) + dev_err(&context->mhi_dev->dev, "Unable to send read for dump table %d\n", ret); +} + static void sahara_processing(struct work_struct *work) { - struct sahara_context *context = container_of(work, struct sahara_context, work); + struct sahara_context *context = container_of(work, struct sahara_context, fw_work); int ret; switch (le32_to_cpu(context->rx->cmd)) { @@ -338,6 +485,12 @@ static void sahara_processing(struct work_struct *work) case SAHARA_DONE_RESP_CMD: /* Intentional do nothing as we don't need to exit an app */ break; + case SAHARA_RESET_RESP_CMD: + /* Intentional do nothing as we don't need to exit an app */ + break; + case SAHARA_MEM_DEBUG64_CMD: + sahara_memory_debug64(context); + break; default: dev_err(&context->mhi_dev->dev, "Unknown command %d\n", le32_to_cpu(context->rx->cmd)); @@ -350,6 +503,217 @@ static void sahara_processing(struct work_struct *work) dev_err(&context->mhi_dev->dev, "Unable to requeue rx buf %d\n", ret); } +static void sahara_parse_dump_table(struct sahara_context *context) +{ + struct sahara_dump_table_entry *image_out_table; + struct sahara_debug_table_entry64 *dev_table; + struct sahara_memory_dump_meta_v1 *dump_meta; + u64 table_nents; + u64 dump_length; + int ret; + u64 i; + + table_nents = context->dump_table_length / sizeof(*dev_table); + context->dump_images_left = table_nents; + dump_length = 0; + + dev_table = (struct sahara_debug_table_entry64 *)(context->rx); + for (i = 0; i < table_nents; ++i) { + /* Do not trust the device, ensure the strings are terminated */ + dev_table[i].description[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0; + dev_table[i].filename[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0; + + dump_length = size_add(dump_length, le64_to_cpu(dev_table[i].length)); + if (dump_length == SIZE_MAX) { + /* Discard the dump */ + sahara_send_reset(context); + return; + } + + dev_dbg(&context->mhi_dev->dev, + "Memory dump table entry %lld type: %lld address: %#llx length: %#llx description: \"%s\" filename \"%s\"\n", + i, + le64_to_cpu(dev_table[i].type), + le64_to_cpu(dev_table[i].address), + le64_to_cpu(dev_table[i].length), + dev_table[i].description, + dev_table[i].filename); + } + + dump_length = size_add(dump_length, sizeof(*dump_meta)); + if (dump_length == SIZE_MAX) { + /* Discard the dump */ + sahara_send_reset(context); + return; + } + dump_length = size_add(dump_length, size_mul(sizeof(*image_out_table), table_nents)); + if (dump_length == SIZE_MAX) { + /* Discard the dump */ + sahara_send_reset(context); + return; + } + + context->mem_dump_sz = dump_length; + context->mem_dump = vzalloc(dump_length); + if (!context->mem_dump) { + /* Discard the dump */ + sahara_send_reset(context); + return; + } + + /* Populate the dump metadata and table for userspace */ + dump_meta = context->mem_dump; + dump_meta->magic = SAHARA_DUMP_V1_MAGIC; + dump_meta->version = SAHARA_DUMP_V1_VER; + dump_meta->dump_size = dump_length; + dump_meta->table_size = context->dump_table_length; + + image_out_table = context->mem_dump + sizeof(*dump_meta); + for (i = 0; i < table_nents; ++i) { + image_out_table[i].type = le64_to_cpu(dev_table[i].type); + image_out_table[i].address = le64_to_cpu(dev_table[i].address); + image_out_table[i].length = le64_to_cpu(dev_table[i].length); + strscpy(image_out_table[i].description, dev_table[i].description, + SAHARA_TABLE_ENTRY_STR_LEN); + strscpy(image_out_table[i].filename, + dev_table[i].filename, + SAHARA_TABLE_ENTRY_STR_LEN); + } + + context->mem_dump_freespace = &image_out_table[i]; + + /* Done parsing the table, switch to image dump mode */ + context->dump_table_length = 0; + + /* Request the first chunk of the first image */ + context->dump_image = &image_out_table[0]; + dump_length = min(context->dump_image->length, SAHARA_READ_MAX_SIZE); + /* Avoid requesting EOI sized data so that we can identify errors */ + if (dump_length == SAHARA_END_OF_IMAGE_LENGTH) + dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2; + + context->dump_image_offset = dump_length; + + context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD); + context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH); + context->tx[0]->memory_read64.memory_address = cpu_to_le64(context->dump_image->address); + context->tx[0]->memory_read64.memory_length = cpu_to_le64(dump_length); + + context->rx_size_requested = dump_length; + + ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0], + SAHARA_MEM_READ64_LENGTH, MHI_EOT); + if (ret) + dev_err(&context->mhi_dev->dev, "Unable to send read for dump content %d\n", ret); +} + +static void sahara_parse_dump_image(struct sahara_context *context) +{ + u64 dump_length; + int ret; + + memcpy(context->mem_dump_freespace, context->rx, context->rx_size); + context->mem_dump_freespace += context->rx_size; + + if (context->dump_image_offset >= context->dump_image->length) { + /* Need to move to next image */ + context->dump_image++; + context->dump_images_left--; + context->dump_image_offset = 0; + + if (!context->dump_images_left) { + /* Dump done */ + dev_coredumpv(context->mhi_dev->mhi_cntrl->cntrl_dev, + context->mem_dump, + context->mem_dump_sz, + GFP_KERNEL); + context->mem_dump = NULL; + sahara_send_reset(context); + return; + } + } + + /* Get next image chunk */ + dump_length = context->dump_image->length - context->dump_image_offset; + dump_length = min(dump_length, SAHARA_READ_MAX_SIZE); + /* Avoid requesting EOI sized data so that we can identify errors */ + if (dump_length == SAHARA_END_OF_IMAGE_LENGTH) + dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2; + + context->tx[0]->cmd = cpu_to_le32(SAHARA_MEM_READ64_CMD); + context->tx[0]->length = cpu_to_le32(SAHARA_MEM_READ64_LENGTH); + context->tx[0]->memory_read64.memory_address = + cpu_to_le64(context->dump_image->address + context->dump_image_offset); + context->tx[0]->memory_read64.memory_length = cpu_to_le64(dump_length); + + context->dump_image_offset += dump_length; + context->rx_size_requested = dump_length; + + ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0], + SAHARA_MEM_READ64_LENGTH, MHI_EOT); + if (ret) + dev_err(&context->mhi_dev->dev, + "Unable to send read for dump content %d\n", ret); +} + +static void sahara_dump_processing(struct work_struct *work) +{ + struct sahara_context *context = container_of(work, struct sahara_context, dump_work); + int ret; + + /* + * We should get the expected raw data, but if the device has an error + * it is supposed to send EOI with an error code. + */ + if (context->rx_size != context->rx_size_requested && + context->rx_size != SAHARA_END_OF_IMAGE_LENGTH) { + dev_err(&context->mhi_dev->dev, + "Unexpected response to read_data. Expected size: %#zx got: %#zx\n", + context->rx_size_requested, + context->rx_size); + goto error; + } + + if (context->rx_size == SAHARA_END_OF_IMAGE_LENGTH && + le32_to_cpu(context->rx->cmd) == SAHARA_END_OF_IMAGE_CMD) { + dev_err(&context->mhi_dev->dev, + "Unexpected EOI response to read_data. Status: %d\n", + le32_to_cpu(context->rx->end_of_image.status)); + goto error; + } + + if (context->rx_size == SAHARA_END_OF_IMAGE_LENGTH && + le32_to_cpu(context->rx->cmd) != SAHARA_END_OF_IMAGE_CMD) { + dev_err(&context->mhi_dev->dev, + "Invalid EOI response to read_data. CMD: %d\n", + le32_to_cpu(context->rx->cmd)); + goto error; + } + + /* + * Need to know if we received the dump table, or part of a dump image. + * Since we get raw data, we cannot tell from the data itself. Instead, + * we use the stored dump_table_length, which we zero after we read and + * process the entire table. + */ + if (context->dump_table_length) + sahara_parse_dump_table(context); + else + sahara_parse_dump_image(context); + + ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE, context->rx, + SAHARA_PACKET_MAX_SIZE, MHI_EOT); + if (ret) + dev_err(&context->mhi_dev->dev, "Unable to requeue rx buf %d\n", ret); + + return; + +error: + vfree(context->mem_dump); + context->mem_dump = NULL; + sahara_send_reset(context); +} + static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) { struct sahara_context *context; @@ -382,7 +746,8 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_ } context->mhi_dev = mhi_dev; - INIT_WORK(&context->work, sahara_processing); + INIT_WORK(&context->fw_work, sahara_processing); + INIT_WORK(&context->dump_work, sahara_dump_processing); context->image_table = aic100_image_table; context->table_size = ARRAY_SIZE(aic100_image_table); context->active_image_id = SAHARA_IMAGE_ID_NONE; @@ -405,7 +770,10 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev) { struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev); - cancel_work_sync(&context->work); + cancel_work_sync(&context->fw_work); + cancel_work_sync(&context->dump_work); + if (context->mem_dump) + vfree(context->mem_dump); sahara_release_image(context); mhi_unprepare_from_transfer(mhi_dev); } @@ -418,8 +786,14 @@ static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result { struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev); - if (!mhi_result->transaction_status) - schedule_work(&context->work); + if (!mhi_result->transaction_status) { + context->rx_size = mhi_result->bytes_xferd; + if (context->is_mem_dump_mode) + schedule_work(&context->dump_work); + else + schedule_work(&context->fw_work); + } + } static const struct mhi_device_id sahara_mhi_match_table[] = { From 2eb747a5988e16068471bce8d87fd9843dc5c3b3 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 26 Oct 2024 23:00:10 +0100 Subject: [PATCH 56/90] drm/omap: Clean up deadcode functions dispc_enable_fifomerge() last use was removed by 2012's commit 85099f11bd03 ("Revert "OMAPDSS: APPLY: add fifo merge support funcs"") dispc_has_writeback(), dispc_wb_get_framedone_irq(), dispc_wb_go(), dispc_wb_go_busy() and dispc_wb_setup() were changed from statics to public symbols and unwired from a structure by 2020's commit dac62bcafeaa ("drm/omap: remove dispc_ops") but didn't have any users. dispc_mgr_get_clock_div() got renamed from dispc_get_clock_div() and it's last use was removed in 2011 by commit 42c9dee82129 ("OMAP: DSS2: Remove FB_OMAP_BOOTLOADER_INIT support") Remove them. Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20241026220010.93773-1-linux@treblig.org --- drivers/gpu/drm/omapdrm/dss/dispc.c | 146 ---------------------------- drivers/gpu/drm/omapdrm/dss/dss.h | 13 --- 2 files changed, 159 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index 993691b3cc7ea..9344855c48878 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -691,11 +691,6 @@ u32 dispc_mgr_get_sync_lost_irq(struct dispc_device *dispc, return mgr_desc[channel].sync_lost_irq; } -u32 dispc_wb_get_framedone_irq(struct dispc_device *dispc) -{ - return DISPC_IRQ_FRAMEDONEWB; -} - void dispc_mgr_enable(struct dispc_device *dispc, enum omap_channel channel, bool enable) { @@ -726,30 +721,6 @@ void dispc_mgr_go(struct dispc_device *dispc, enum omap_channel channel) mgr_fld_write(dispc, channel, DISPC_MGR_FLD_GO, 1); } -bool dispc_wb_go_busy(struct dispc_device *dispc) -{ - return REG_GET(dispc, DISPC_CONTROL2, 6, 6) == 1; -} - -void dispc_wb_go(struct dispc_device *dispc) -{ - enum omap_plane_id plane = OMAP_DSS_WB; - bool enable, go; - - enable = REG_GET(dispc, DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1; - - if (!enable) - return; - - go = REG_GET(dispc, DISPC_CONTROL2, 6, 6) == 1; - if (go) { - DSSERR("GO bit not down for WB\n"); - return; - } - - REG_FLD_MOD(dispc, DISPC_CONTROL2, 1, 6, 6); -} - static void dispc_ovl_write_firh_reg(struct dispc_device *dispc, enum omap_plane_id plane, int reg, u32 value) @@ -1498,17 +1469,6 @@ void dispc_ovl_set_fifo_threshold(struct dispc_device *dispc, min(high, 0xfffu)); } -void dispc_enable_fifomerge(struct dispc_device *dispc, bool enable) -{ - if (!dispc_has_feature(dispc, FEAT_FIFO_MERGE)) { - WARN_ON(enable); - return; - } - - DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); - REG_FLD_MOD(dispc, DISPC_CONFIG, enable ? 1 : 0, 14, 14); -} - void dispc_ovl_compute_fifo_thresholds(struct dispc_device *dispc, enum omap_plane_id plane, u32 *fifo_low, u32 *fifo_high, @@ -2814,95 +2774,6 @@ int dispc_ovl_setup(struct dispc_device *dispc, return r; } -int dispc_wb_setup(struct dispc_device *dispc, - const struct omap_dss_writeback_info *wi, - bool mem_to_mem, const struct videomode *vm, - enum dss_writeback_channel channel_in) -{ - int r; - u32 l; - enum omap_plane_id plane = OMAP_DSS_WB; - const int pos_x = 0, pos_y = 0; - const u8 zorder = 0, global_alpha = 0; - const bool replication = true; - bool truncation; - int in_width = vm->hactive; - int in_height = vm->vactive; - enum omap_overlay_caps caps = - OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA; - - if (vm->flags & DISPLAY_FLAGS_INTERLACED) - in_height /= 2; - - DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, " - "rot %d\n", wi->paddr, wi->p_uv_addr, in_width, - in_height, wi->width, wi->height, wi->fourcc, wi->rotation); - - r = dispc_ovl_setup_common(dispc, plane, caps, wi->paddr, wi->p_uv_addr, - wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width, - wi->height, wi->fourcc, wi->rotation, zorder, - wi->pre_mult_alpha, global_alpha, wi->rotation_type, - replication, vm, mem_to_mem, DRM_COLOR_YCBCR_BT601, - DRM_COLOR_YCBCR_LIMITED_RANGE); - if (r) - return r; - - switch (wi->fourcc) { - case DRM_FORMAT_RGB565: - case DRM_FORMAT_RGB888: - case DRM_FORMAT_ARGB4444: - case DRM_FORMAT_RGBA4444: - case DRM_FORMAT_RGBX4444: - case DRM_FORMAT_ARGB1555: - case DRM_FORMAT_XRGB1555: - case DRM_FORMAT_XRGB4444: - truncation = true; - break; - default: - truncation = false; - break; - } - - /* setup extra DISPC_WB_ATTRIBUTES */ - l = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane)); - l = FLD_MOD(l, truncation, 10, 10); /* TRUNCATIONENABLE */ - l = FLD_MOD(l, channel_in, 18, 16); /* CHANNELIN */ - l = FLD_MOD(l, mem_to_mem, 19, 19); /* WRITEBACKMODE */ - if (mem_to_mem) - l = FLD_MOD(l, 1, 26, 24); /* CAPTUREMODE */ - else - l = FLD_MOD(l, 0, 26, 24); /* CAPTUREMODE */ - dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), l); - - if (mem_to_mem) { - /* WBDELAYCOUNT */ - REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane), 0, 7, 0); - } else { - u32 wbdelay; - - if (channel_in == DSS_WB_TV_MGR) - wbdelay = vm->vsync_len + vm->vback_porch; - else - wbdelay = vm->vfront_porch + vm->vsync_len + - vm->vback_porch; - - if (vm->flags & DISPLAY_FLAGS_INTERLACED) - wbdelay /= 2; - - wbdelay = min(wbdelay, 255u); - - /* WBDELAYCOUNT */ - REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane), wbdelay, 7, 0); - } - - return 0; -} - -bool dispc_has_writeback(struct dispc_device *dispc) -{ - return dispc->feat->has_writeback; -} - int dispc_ovl_enable(struct dispc_device *dispc, enum omap_plane_id plane, bool enable) { @@ -3742,23 +3613,6 @@ void dispc_mgr_set_clock_div(struct dispc_device *dispc, cinfo->pck_div); } -int dispc_mgr_get_clock_div(struct dispc_device *dispc, - enum omap_channel channel, - struct dispc_clock_info *cinfo) -{ - unsigned long fck; - - fck = dispc_fclk_rate(dispc); - - cinfo->lck_div = REG_GET(dispc, DISPC_DIVISORo(channel), 23, 16); - cinfo->pck_div = REG_GET(dispc, DISPC_DIVISORo(channel), 7, 0); - - cinfo->lck = fck / cinfo->lck_div; - cinfo->pck = cinfo->lck / cinfo->pck_div; - - return 0; -} - u32 dispc_read_irqstatus(struct dispc_device *dispc) { return dispc_read_reg(dispc, DISPC_IRQSTATUS); diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h index 4ff02fbc0e71d..a8b231ed4f4bc 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.h +++ b/drivers/gpu/drm/omapdrm/dss/dss.h @@ -416,7 +416,6 @@ u32 dispc_mgr_get_framedone_irq(struct dispc_device *dispc, enum omap_channel channel); u32 dispc_mgr_get_sync_lost_irq(struct dispc_device *dispc, enum omap_channel channel); -u32 dispc_wb_get_framedone_irq(struct dispc_device *dispc); u32 dispc_get_memory_bandwidth_limit(struct dispc_device *dispc); @@ -458,20 +457,11 @@ int dispc_ovl_setup(struct dispc_device *dispc, int dispc_ovl_enable(struct dispc_device *dispc, enum omap_plane_id plane, bool enable); -bool dispc_has_writeback(struct dispc_device *dispc); -int dispc_wb_setup(struct dispc_device *dispc, - const struct omap_dss_writeback_info *wi, - bool mem_to_mem, const struct videomode *vm, - enum dss_writeback_channel channel_in); -bool dispc_wb_go_busy(struct dispc_device *dispc); -void dispc_wb_go(struct dispc_device *dispc); - void dispc_enable_sidle(struct dispc_device *dispc); void dispc_disable_sidle(struct dispc_device *dispc); void dispc_lcd_enable_signal(struct dispc_device *dispc, bool enable); void dispc_pck_free_enable(struct dispc_device *dispc, bool enable); -void dispc_enable_fifomerge(struct dispc_device *dispc, bool enable); typedef bool (*dispc_div_calc_func)(int lckd, int pckd, unsigned long lck, unsigned long pck, void *data); @@ -494,9 +484,6 @@ void dispc_ovl_compute_fifo_thresholds(struct dispc_device *dispc, void dispc_mgr_set_clock_div(struct dispc_device *dispc, enum omap_channel channel, const struct dispc_clock_info *cinfo); -int dispc_mgr_get_clock_div(struct dispc_device *dispc, - enum omap_channel channel, - struct dispc_clock_info *cinfo); void dispc_set_tv_pclk(struct dispc_device *dispc, unsigned long pclk); #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS From b59d1d9d9ab273a8a013ee1d329a74f19110c9ee Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 26 Oct 2024 06:10:42 +0200 Subject: [PATCH 57/90] drm/bridge: tc358767: Fix use of unadjusted mode in the driver The driver configures mostly Pixel PLL from the clock cached in local copy of the mode. Make sure the driver uses adjusted mode which contains the updated Pixel PLL settings negotiated in tc_dpi_atomic_check()/tc_edp_atomic_check(). Signed-off-by: Marek Vasut Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20241026041057.247640-1-marex@denx.de --- drivers/gpu/drm/bridge/tc358767.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 0c48e747f5b0d..d52d55e06be60 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1707,7 +1707,7 @@ static void tc_bridge_mode_set(struct drm_bridge *bridge, { struct tc_data *tc = bridge_to_tc(bridge); - drm_mode_copy(&tc->mode, mode); + drm_mode_copy(&tc->mode, adj); } static const struct drm_edid *tc_edid_read(struct drm_bridge *bridge, From 123aff84f6304a94ac3d6bebb224d4e59da00821 Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 22 Oct 2024 15:15:04 +0530 Subject: [PATCH 58/90] drm/ci: add dedede Add job that executes the IGT test suite for acer-cb317-1h-c3z6-dedede. dedede boards use 64 bit Intel Jasper Lake processors. Signed-off-by: Vignesh Raman Acked-by: Helen Koike Signed-off-by: Helen Koike Link: https://patchwork.freedesktop.org/patch/msgid/20241022094509.85510-2-vignesh.raman@collabora.com --- drivers/gpu/drm/ci/test.yml | 9 ++++ drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt | 51 +++++++++++++++++++ drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt | 13 +++++ drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt | 20 ++++++++ 4 files changed, 93 insertions(+) create mode 100644 drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt create mode 100644 drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt create mode 100644 drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index 09d8447840e94..d0bfe6aab58a0 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -286,6 +286,15 @@ i915:tgl: GPU_VERSION: tgl RUNNER_TAG: mesa-ci-x86-64-lava-acer-cp514-2h-1130g7-volteer +i915:jsl: + extends: + - .i915 + parallel: 4 + variables: + DEVICE_TYPE: acer-cb317-1h-c3z6-dedede + GPU_VERSION: jsl + RUNNER_TAG: mesa-ci-x86-64-lava-acer-cb317-1h-c3z6-dedede + .amdgpu: extends: - .lava-igt:x86_64 diff --git a/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt new file mode 100644 index 0000000000000..ed9f7b576843d --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-jsl-fails.txt @@ -0,0 +1,51 @@ +core_setmaster@master-drop-set-user,Fail +i915_module_load@load,Fail +i915_module_load@reload,Fail +i915_module_load@reload-no-display,Fail +i915_module_load@resize-bar,Fail +i915_pm_rpm@gem-execbuf-stress,Timeout +i915_pm_rpm@module-reload,Fail +kms_flip@plain-flip-fb-recreate,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail +kms_lease@lease-uevent,Fail +kms_pm_rpm@legacy-planes,Timeout +kms_pm_rpm@legacy-planes-dpms,Timeout +kms_pm_rpm@modeset-stress-extra-wait,Timeout +kms_pm_rpm@universal-planes,Timeout +kms_pm_rpm@universal-planes-dpms,Timeout +kms_rotation_crc@multiplane-rotation,Fail +kms_rotation_crc@multiplane-rotation-cropping-bottom,Fail +kms_rotation_crc@multiplane-rotation-cropping-top,Fail +perf@i915-ref-count,Fail +perf_pmu@busy-accuracy-50,Fail +perf_pmu@module-unload,Fail +perf_pmu@most-busy-idle-check-all,Fail +perf_pmu@rc6,Crash +sysfs_heartbeat_interval@long,Timeout +sysfs_heartbeat_interval@off,Timeout +sysfs_preempt_timeout@off,Timeout +sysfs_timeslice_duration@off,Timeout +xe_module_load@force-load,Fail +xe_module_load@load,Fail +xe_module_load@many-reload,Fail +xe_module_load@reload,Fail +xe_module_load@reload-no-display,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt new file mode 100644 index 0000000000000..5c3ef4486b9d2 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-jsl-flakes.txt @@ -0,0 +1,13 @@ +# Board Name: acer-cb317-1h-c3z6-dedede +# Bug Report: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12475 +# Failure Rate: 100 +# IGT Version: 1.28-ga73311079 +# Linux Version: 6.12.0-rc1 +kms_flip@flip-vs-panning-interruptible + +# Board Name: acer-cb317-1h-c3z6-dedede +# Bug Report: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12476 +# Failure Rate: 100 +# IGT Version: 1.28-ga73311079 +# Linux Version: 6.12.0-rc1 +kms_universal_plane@cursor-fb-leak diff --git a/drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt b/drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt new file mode 100644 index 0000000000000..1a3d87c0ca6ee --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-jsl-skips.txt @@ -0,0 +1,20 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* + +# Skip driver specific tests +^amdgpu.* +^msm.* +nouveau_.* +^panfrost.* +^v3d.* +^vc4.* +^vmwgfx* + +# GEM tests takes ~1000 hours, so skip it +gem_.* + +# trap_err +i915_pm_rc6_residency.* + +# Hangs the machine and timeout occurs +i915_pm_rpm@system-hibernate* From 973db93de0a007fd1d0d85140c53f70dee6e2c25 Mon Sep 17 00:00:00 2001 From: Vignesh Raman Date: Tue, 22 Oct 2024 15:15:05 +0530 Subject: [PATCH 59/90] drm/ci: add sm8350-hdk Add job that executes the IGT test suite for sm8350-hdk. Reviewed-by: Abhinav Kumar Reviewed-by: Dmitry Baryshkov Signed-off-by: Vignesh Raman Acked-by: Helen Koike Signed-off-by: Helen Koike Link: https://patchwork.freedesktop.org/patch/msgid/20241022094509.85510-3-vignesh.raman@collabora.com --- drivers/gpu/drm/ci/arm64.config | 7 +- drivers/gpu/drm/ci/build.sh | 1 + drivers/gpu/drm/ci/test.yml | 16 ++ .../drm/ci/xfails/msm-sm8350-hdk-fails.txt | 15 ++ .../drm/ci/xfails/msm-sm8350-hdk-flakes.txt | 6 + .../drm/ci/xfails/msm-sm8350-hdk-skips.txt | 211 ++++++++++++++++++ 6 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt create mode 100644 drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt create mode 100644 drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt diff --git a/drivers/gpu/drm/ci/arm64.config b/drivers/gpu/drm/ci/arm64.config index 66e70ced796fa..a8fca079921b7 100644 --- a/drivers/gpu/drm/ci/arm64.config +++ b/drivers/gpu/drm/ci/arm64.config @@ -90,7 +90,12 @@ CONFIG_QCOM_GPI_DMA=y CONFIG_USB_ONBOARD_DEV=y CONFIG_NVMEM_QCOM_QFPROM=y CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y - +CONFIG_REGULATOR_QCOM_REFGEN=y +CONFIG_TYPEC_MUX_FSA4480=y +CONFIG_QCOM_PMIC_GLINK=y +CONFIG_UCSI_PMIC_GLINK=y +CONFIG_QRTR=y +CONFIG_QRTR_SMD=y # db410c ethernet CONFIG_USB_RTL8152=y diff --git a/drivers/gpu/drm/ci/build.sh b/drivers/gpu/drm/ci/build.sh index 5a3bdcffae32d..139b81db63127 100644 --- a/drivers/gpu/drm/ci/build.sh +++ b/drivers/gpu/drm/ci/build.sh @@ -30,6 +30,7 @@ if [[ "$KERNEL_ARCH" = "arm64" ]]; then DEVICE_TREES+=" arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dtb" DEVICE_TREES+=" arch/arm64/boot/dts/qcom/sc7180-trogdor-lazor-limozeen-nots-r5.dtb" DEVICE_TREES+=" arch/arm64/boot/dts/qcom/sc7180-trogdor-kingoftown.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/qcom/sm8350-hdk.dtb" elif [[ "$KERNEL_ARCH" = "arm" ]]; then GCC_ARCH="arm-linux-gnueabihf" DEBIAN_ARCH="armhf" diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml index d0bfe6aab58a0..f0ef60c8f56da 100644 --- a/drivers/gpu/drm/ci/test.yml +++ b/drivers/gpu/drm/ci/test.yml @@ -162,6 +162,22 @@ msm:sdm845: script: - ./install/bare-metal/cros-servo.sh +msm:sm8350-hdk: + extends: + - .lava-igt:arm64 + stage: msm + parallel: 4 + variables: + BOOT_METHOD: fastboot + DEVICE_TYPE: sm8350-hdk + DRIVER_NAME: msm + DTB: ${DEVICE_TYPE} + FARM: collabora + GPU_VERSION: ${DEVICE_TYPE} + KERNEL_IMAGE_NAME: "Image.gz" + KERNEL_IMAGE_TYPE: "" + RUNNER_TAG: mesa-ci-x86-64-lava-sm8350-hdk + .rockchip-device: variables: DTB: ${DEVICE_TYPE} diff --git a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt new file mode 100644 index 0000000000000..4892c0c70a6dc --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-fails.txt @@ -0,0 +1,15 @@ +kms_3d,Fail +kms_cursor_legacy@forked-bo,Fail +kms_cursor_legacy@forked-move,Fail +kms_cursor_legacy@single-bo,Fail +kms_cursor_legacy@single-move,Fail +kms_cursor_legacy@torture-bo,Fail +kms_cursor_legacy@torture-move,Fail +kms_hdmi_inject@inject-4k,Fail +kms_lease@lease-uevent,Fail +kms_plane_alpha_blend@alpha-7efc,Fail +kms_plane_alpha_blend@alpha-basic,Fail +kms_plane_alpha_blend@alpha-opaque-fb,Fail +kms_plane_alpha_blend@alpha-transparent-fb,Fail +kms_plane_alpha_blend@constant-alpha-max,Fail +msm/msm_recovery@gpu-fault-parallel,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt new file mode 100644 index 0000000000000..c1859d9b165f1 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-flakes.txt @@ -0,0 +1,6 @@ +# Board Name: sm8350-hdk +# Bug Report: https://gitlab.freedesktop.org/drm/msm/-/issues/65 +# Failure Rate: 100 +# IGT Version: 1.28-ga73311079 +# Linux Version: 6.12.0-rc1 +msm/msm_recovery@gpu-fault diff --git a/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt new file mode 100644 index 0000000000000..329770c520d93 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sm8350-hdk-skips.txt @@ -0,0 +1,211 @@ +# Skip driver specific tests +^amdgpu.* +nouveau_.* +^panfrost.* +^v3d.* +^vc4.* +^vmwgfx* + +# Skip intel specific tests +gem_.* +i915_.* +tools_test.* + +# Currently fails and causes coverage loss for other tests +# since core_getversion also fails. +core_hotunplug.* + +# Kernel panic +msm/msm_mapping@ring +# DEBUG - Begin test msm/msm_mapping@ring +# [ 200.874157] [IGT] msm_mapping: executing +# [ 200.880236] [IGT] msm_mapping: starting subtest ring +# [ 200.895243] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=PERMISSION source=CP (0,0,0,1) +# [ 200.906885] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 200.917625] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 200.928353] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 200.939084] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 200.949815] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 200.950227] platform 3d6a000.gmu: [drm:a6xx_hfi_send_msg.constprop.0] *ERROR* Message HFI_H2F_MSG_GX_BW_PERF_VOTE id 25 timed out waiting for response +# [ 200.960467] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 200.960500] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 200.995966] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 201.006702] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 204.213387] platform 3d6a000.gmu: GMU watchdog expired +# [ 205.909103] adreno_fault_handler: 224274 callbacks suppressed +# [ 205.909108] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 205.925794] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 205.936529] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 205.947263] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 205.957997] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 205.968731] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 205.979465] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 205.990199] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 206.000932] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 206.011666] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 210.925090] adreno_fault_handler: 224511 callbacks suppressed +# [ 210.925096] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 210.941781] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 210.952517] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 210.963250] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 210.973985] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 210.984719] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 210.995452] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 211.006186] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 211.016921] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 211.027655] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 215.937100] adreno_fault_handler: 223760 callbacks suppressed +# [ 215.937106] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 215.953824] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 215.964573] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 215.975321] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 215.986067] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 215.996815] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 216.007563] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 216.018310] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 216.029057] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 216.039805] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 220.945182] adreno_fault_handler: 222822 callbacks suppressed +# [ 220.945188] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 220.961897] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 220.972645] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 220.983392] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 220.994140] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 221.004889] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 221.015636] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 221.026383] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 221.037130] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 221.047879] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 225.953179] adreno_fault_handler: 223373 callbacks suppressed +# [ 225.953184] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 225.969883] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 225.980617] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 225.991350] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 226.002084] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 226.012818] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 226.023551] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 226.034285] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 226.045019] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 226.055753] *** gpu fault: ttbr0=00000001160d6000 iova=0001000000001000 dir=WRITE type=UNKNOWN source=CP (0,0,0,1) +# [ 228.001087] rcu: INFO: rcu_preempt detected stalls on CPUs/tasks: +# [ 228.007412] rcu: 0-....: (524 ticks this GP) idle=4ffc/1/0x4000000000000000 softirq=9367/9368 fqs=29 +# [ 228.017097] rcu: (detected by 1, t=6504 jiffies, g=29837, q=6 ncpus=8) +# [ 228.023959] Sending NMI from CPU 1 to CPUs 0: +# [ 228.161164] watchdog: BUG: soft lockup - CPU#0 stuck for 26s! [gpu-worker:150] +# [ 228.173169] Modules linked in: +# [ 228.176361] irq event stamp: 2809595 +# [ 228.180083] hardirqs last enabled at (2809594): [] exit_to_kernel_mode+0x38/0x130 +# [ 228.189547] hardirqs last disabled at (2809595): [] el1_interrupt+0x24/0x64 +# [ 228.198377] softirqs last enabled at (1669060): [] handle_softirqs+0x4a4/0x4bc +# [ 228.207565] softirqs last disabled at (1669063): [] __do_softirq+0x14/0x20 +# [ 228.216316] CPU: 0 UID: 0 PID: 150 Comm: gpu-worker Not tainted 6.12.0-rc1-g685d530dc83a #1 +# [ 228.224966] Hardware name: Qualcomm Technologies, Inc. SM8350 HDK (DT) +# [ 228.231730] pstate: 00400005 (nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) +# [ 228.238948] pc : tcp_fastretrans_alert+0x0/0x884 +# [ 228.243751] lr : tcp_ack+0x9d4/0x1238 +# [ 228.247562] sp : ffff8000800036d0 +# [ 228.251011] x29: ffff8000800036d0 x28: 000000000000000c x27: 0000000000000001 +# [ 228.258421] x26: ffff704683cd8000 x25: 0000000000000403 x24: ffff70468b7e7c00 +# [ 228.265829] x23: 0000000000000000 x22: 0000000000000004 x21: 000000000000140f +# [ 228.273237] x20: 00000000f1de79f7 x19: 00000000f1de7a5f x18: 0000000000000001 +# [ 228.280644] x17: 00000000302d6762 x16: 632d6b64682d3035 x15: ffff704683c39000 +# [ 228.288051] x14: 00000000000e2000 x13: ffff704683df6000 x12: 0000000000000000 +# [ 228.295458] x11: 00000000000000a0 x10: 0000000000000000 x9 : ffffd3bc551a9a20 +# [ 228.302865] x8 : ffff800080003640 x7 : 0000000000040faa x6 : 00000000ffff9634 +# [ 228.310271] x5 : 00000000000005a8 x4 : ffff800080003788 x3 : ffff80008000377c +# [ 228.317679] x2 : 0000000000000000 x1 : 00000000f1de79f7 x0 : ffff704683cd8000 +# [ 228.325087] Call trace: +# [ 228.327640] tcp_fastretrans_alert+0x0/0x884 +# [ 228.332082] tcp_rcv_established+0x7c4/0x8bc +# [ 228.336523] tcp_v4_do_rcv+0x244/0x31c +# [ 228.340429] tcp_v4_rcv+0xcc4/0x1084 +# [ 228.344155] ip_protocol_deliver_rcu+0x64/0x218 +# [ 228.348862] ip_local_deliver_finish+0xb8/0x1ac +# [ 228.353566] ip_local_deliver+0x84/0x254 +# [ 228.357651] ip_sublist_rcv_finish+0x84/0xb8 +# [ 228.362092] ip_sublist_rcv+0x11c/0x2f0 +# [ 228.366081] ip_list_rcv+0xfc/0x190 +# [ 228.369711] __netif_receive_skb_list_core+0x174/0x208 +# [ 228.375050] netif_receive_skb_list_internal+0x204/0x3ac +# [ 228.380564] napi_complete_done+0x64/0x1d0 +# [ 228.384826] lan78xx_poll+0x71c/0x9cc +# [ 228.388638] __napi_poll.constprop.0+0x3c/0x254 +# [ 228.393341] net_rx_action+0x164/0x2d4 +# [ 228.397244] handle_softirqs+0x128/0x4bc +# [ 228.401329] __do_softirq+0x14/0x20 +# [ 228.404958] ____do_softirq+0x10/0x1c +# [ 228.408769] call_on_irq_stack+0x24/0x4c +# [ 228.412854] do_softirq_own_stack+0x1c/0x28 +# [ 228.417199] __irq_exit_rcu+0x124/0x164 +# [ 228.421188] irq_exit_rcu+0x10/0x38 +# [ 228.424819] el1_interrupt+0x38/0x64 +# [ 228.428546] el1h_64_irq_handler+0x18/0x24 +# [ 228.432807] el1h_64_irq+0x64/0x68 +# [ 228.436354] lock_acquire+0x214/0x32c +# [ 228.440166] __mutex_lock+0x98/0x3d0 +# [ 228.443893] mutex_lock_nested+0x24/0x30 +# [ 228.447978] fault_worker+0x58/0x184 +# [ 228.451704] kthread_worker_fn+0xf4/0x320 +# [ 228.455873] kthread+0x114/0x118 +# [ 228.459243] ret_from_fork+0x10/0x20 +# [ 228.462970] Kernel panic - not syncing: softlockup: hung tasks +# [ 228.469018] CPU: 0 UID: 0 PID: 150 Comm: gpu-worker Tainted: G L 6.12.0-rc1-g685d530dc83a #1 +# [ 228.479190] Tainted: [L]=SOFTLOCKUP +# [ 228.482815] Hardware name: Qualcomm Technologies, Inc. SM8350 HDK (DT) +# [ 228.489574] Call trace: +# [ 228.492125] dump_backtrace+0x98/0xf0 +# [ 228.495931] show_stack+0x18/0x24 +# [ 228.499380] dump_stack_lvl+0x38/0xd0 +# [ 228.503189] dump_stack+0x18/0x24 +# [ 228.506639] panic+0x3bc/0x41c +# [ 228.509826] watchdog_timer_fn+0x254/0x2e4 +# [ 228.514087] __hrtimer_run_queues+0x3b0/0x40c +# [ 228.518612] hrtimer_interrupt+0xe8/0x248 +# [ 228.522777] arch_timer_handler_virt+0x2c/0x44 +# [ 228.527399] handle_percpu_devid_irq+0xa8/0x2c4 +# [ 228.532103] generic_handle_domain_irq+0x2c/0x44 +# [ 228.536902] gic_handle_irq+0x4c/0x11c +# [ 228.540802] do_interrupt_handler+0x50/0x84 +# [ 228.545146] el1_interrupt+0x34/0x64 +# [ 228.548870] el1h_64_irq_handler+0x18/0x24 +# [ 228.553128] el1h_64_irq+0x64/0x68 +# [ 228.556672] tcp_fastretrans_alert+0x0/0x884 +# [ 228.561110] tcp_rcv_established+0x7c4/0x8bc +# [ 228.565548] tcp_v4_do_rcv+0x244/0x31c +# [ 228.569449] tcp_v4_rcv+0xcc4/0x1084 +# [ 228.573171] ip_protocol_deliver_rcu+0x64/0x218 +# [ 228.577873] ip_local_deliver_finish+0xb8/0x1ac +# [ 228.582574] ip_local_deliver+0x84/0x254 +# [ 228.586655] ip_sublist_rcv_finish+0x84/0xb8 +# [ 228.591092] ip_sublist_rcv+0x11c/0x2f0 +# [ 228.595079] ip_list_rcv+0xfc/0x190 +# [ 228.598706] __netif_receive_skb_list_core+0x174/0x208 +# [ 228.604039] netif_receive_skb_list_internal+0x204/0x3ac +# [ 228.609549] napi_complete_done+0x64/0x1d0 +# [ 228.613808] lan78xx_poll+0x71c/0x9cc +# [ 228.617614] __napi_poll.constprop.0+0x3c/0x254 +# [ 228.622314] net_rx_action+0x164/0x2d4 +# [ 228.626214] handle_softirqs+0x128/0x4bc +# [ 228.630297] __do_softirq+0x14/0x20 +# [ 228.633923] ____do_softirq+0x10/0x1c +# [ 228.637729] call_on_irq_stack+0x24/0x4c +# [ 228.641811] do_softirq_own_stack+0x1c/0x28 +# [ 228.646152] __irq_exit_rcu+0x124/0x164 +# [ 228.650139] irq_exit_rcu+0x10/0x38 +# [ 228.653768] el1_interrupt+0x38/0x64 +# [ 228.657491] el1h_64_irq_handler+0x18/0x24 +# [ 228.661750] el1h_64_irq+0x64/0x68 +# [ 228.665293] lock_acquire+0x214/0x32c +# [ 228.669098] __mutex_lock+0x98/0x3d0 +# [ 228.672821] mutex_lock_nested+0x24/0x30 +# [ 228.676903] fault_worker+0x58/0x184 +# [ 228.680626] kthread_worker_fn+0xf4/0x320 +# [ 228.684790] kthread+0x114/0x118 +# [ 228.688156] ret_from_fork+0x10/0x20 +# [ 228.691882] SMP: stopping secondary CPUs +# [ 229.736843] SMP: failed to stop secondary CPUs 1,4 +# [ 229.741827] Kernel Offset: 0x53bbd1880000 from 0xffff800080000000 +# [ 229.748159] PHYS_OFFSET: 0xfff08fba80000000 +# [ 229.752499] CPU features: 0x18,00000017,00200928,4200720b +# [ 229.758095] Memory Limit: none +# [ 229.761291] ---[ end Kernel panic - not syncing: softlockup: hung tasks ]--- From a2f599046c671d6b46d93aed95b37241ce4504cf Mon Sep 17 00:00:00 2001 From: Li Huafei Date: Sun, 27 Oct 2024 01:38:44 +0800 Subject: [PATCH 60/90] drm/nouveau/gr/gf100: Fix missing unlock in gf100_gr_chan_new() When the call to gf100_grctx_generate() fails, unlock gr->fecs.mutex before returning the error. Fixes smatch warning: drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c:480 gf100_gr_chan_new() warn: inconsistent returns '&gr->fecs.mutex'. Fixes: ca081fff6ecc ("drm/nouveau/gr/gf100-: generate golden context during first object alloc") Signed-off-by: Li Huafei Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20241026173844.2392679-1-lihuafei1@huawei.com --- drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c index 060c74a80eb14..3ea447f6a45b5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -443,6 +443,7 @@ gf100_gr_chan_new(struct nvkm_gr *base, struct nvkm_chan *fifoch, ret = gf100_grctx_generate(gr, chan, fifoch->inst); if (ret) { nvkm_error(&base->engine.subdev, "failed to construct context\n"); + mutex_unlock(&gr->fecs.mutex); return ret; } } From ce68f86c445133117a3474987a1fe29be3d6e8e4 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:07 +0200 Subject: [PATCH 61/90] accel/ivpu: Do not fail when more than 1 tile is fused Allow TILE_FUSE register to disable more than 1 tile. The driver should not prevent such configurations from being functional. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-2-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_hw_btrs.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c index 6d5f1cc711435..3212c99f36823 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.c +++ b/drivers/accel/ivpu/ivpu_hw_btrs.c @@ -141,16 +141,10 @@ static int read_tile_config_fuse(struct ivpu_device *vdev, u32 *tile_fuse_config } config = REG_GET_FLD(VPU_HW_BTRS_LNL_TILE_FUSE, CONFIG, fuse); - if (!tile_disable_check(config)) { - ivpu_err(vdev, "Fuse: Invalid tile disable config (0x%x)\n", config); - return -EIO; - } + if (!tile_disable_check(config)) + ivpu_warn(vdev, "More than 1 tile disabled, tile fuse config mask: 0x%x\n", config); - if (config) - ivpu_dbg(vdev, MISC, "Fuse: %d tiles enabled. Tile number %d disabled\n", - BTRS_LNL_TILE_MAX_NUM - 1, ffs(config) - 1); - else - ivpu_dbg(vdev, MISC, "Fuse: All %d tiles enabled\n", BTRS_LNL_TILE_MAX_NUM); + ivpu_dbg(vdev, MISC, "Tile disable config mask: 0x%x\n", config); *tile_fuse_config = config; return 0; From a74f4d991352c95b20f445b8b0c99ffa2ef79f8e Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:08 +0200 Subject: [PATCH 62/90] accel/ivpu: Defer MMU root page table allocation Defer root page table allocation and unify context init/fini functions. Move allocation of the root page table from the file_priv_open function to perform a lazy allocation approach during ivpu_bo_pin(). By doing so, we avoid the overhead of allocating page tables for simple operations like GET_PARAM that do not require them. Additionally, the MMU context descriptor table initialization has been moved to the ivpu_mmu_context_map_page function. This change streamlines the process and ensures that the descriptor table is only initialized when it is actually needed. Refactor init/fini functions to remove redundant code and make the context management more straightforward. Overall, these changes lead to a reduction in the time taken by the file descriptor open operation, as the costly root page table allocation is now avoided for operations that do not require it. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-3-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_drv.c | 12 +-- drivers/accel/ivpu/ivpu_mmu.c | 94 ++++++----------- drivers/accel/ivpu/ivpu_mmu.h | 4 +- drivers/accel/ivpu/ivpu_mmu_context.c | 145 +++++++++++++------------- drivers/accel/ivpu/ivpu_mmu_context.h | 9 +- 5 files changed, 115 insertions(+), 149 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index e7d8967c02f29..34e3e9b1c3f23 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -86,7 +86,7 @@ static void file_priv_unbind(struct ivpu_device *vdev, struct ivpu_file_priv *fi ivpu_cmdq_release_all_locked(file_priv); ivpu_bo_unbind_all_bos_from_context(vdev, &file_priv->ctx); - ivpu_mmu_user_context_fini(vdev, &file_priv->ctx); + ivpu_mmu_context_fini(vdev, &file_priv->ctx); file_priv->bound = false; drm_WARN_ON(&vdev->drm, !xa_erase_irq(&vdev->context_xa, file_priv->ctx.id)); } @@ -254,9 +254,7 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) goto err_unlock; } - ret = ivpu_mmu_user_context_init(vdev, &file_priv->ctx, ctx_id); - if (ret) - goto err_xa_erase; + ivpu_mmu_context_init(vdev, &file_priv->ctx, ctx_id); file_priv->default_job_limit.min = FIELD_PREP(IVPU_JOB_ID_CONTEXT_MASK, (file_priv->ctx.id - 1)); @@ -273,8 +271,6 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) return 0; -err_xa_erase: - xa_erase_irq(&vdev->context_xa, ctx_id); err_unlock: mutex_unlock(&vdev->context_list_lock); mutex_destroy(&file_priv->ms_lock); @@ -652,9 +648,7 @@ static int ivpu_dev_init(struct ivpu_device *vdev) if (ret) goto err_shutdown; - ret = ivpu_mmu_global_context_init(vdev); - if (ret) - goto err_shutdown; + ivpu_mmu_global_context_init(vdev); ret = ivpu_mmu_init(vdev); if (ret) diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c index c078e214b2212..4ff0d7a519859 100644 --- a/drivers/accel/ivpu/ivpu_mmu.c +++ b/drivers/accel/ivpu/ivpu_mmu.c @@ -696,7 +696,7 @@ int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid) return ret; } -static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma) +static int ivpu_mmu_cdtab_entry_set(struct ivpu_device *vdev, u32 ssid, u64 cd_dma, bool valid) { struct ivpu_mmu_info *mmu = vdev->mmu; struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab; @@ -708,30 +708,29 @@ static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma) return -EINVAL; entry = cdtab->base + (ssid * IVPU_MMU_CDTAB_ENT_SIZE); - - if (cd_dma != 0) { - cd[0] = FIELD_PREP(IVPU_MMU_CD_0_TCR_T0SZ, IVPU_MMU_T0SZ_48BIT) | - FIELD_PREP(IVPU_MMU_CD_0_TCR_TG0, 0) | - FIELD_PREP(IVPU_MMU_CD_0_TCR_IRGN0, 0) | - FIELD_PREP(IVPU_MMU_CD_0_TCR_ORGN0, 0) | - FIELD_PREP(IVPU_MMU_CD_0_TCR_SH0, 0) | - FIELD_PREP(IVPU_MMU_CD_0_TCR_IPS, IVPU_MMU_IPS_48BIT) | - FIELD_PREP(IVPU_MMU_CD_0_ASID, ssid) | - IVPU_MMU_CD_0_TCR_EPD1 | - IVPU_MMU_CD_0_AA64 | - IVPU_MMU_CD_0_R | - IVPU_MMU_CD_0_ASET | - IVPU_MMU_CD_0_V; - cd[1] = cd_dma & IVPU_MMU_CD_1_TTB0_MASK; - cd[2] = 0; - cd[3] = 0x0000000000007444; - - /* For global context generate memory fault on VPU */ - if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) - cd[0] |= IVPU_MMU_CD_0_A; - } else { - memset(cd, 0, sizeof(cd)); - } + drm_WARN_ON(&vdev->drm, (entry[0] & IVPU_MMU_CD_0_V) == valid); + + cd[0] = FIELD_PREP(IVPU_MMU_CD_0_TCR_T0SZ, IVPU_MMU_T0SZ_48BIT) | + FIELD_PREP(IVPU_MMU_CD_0_TCR_TG0, 0) | + FIELD_PREP(IVPU_MMU_CD_0_TCR_IRGN0, 0) | + FIELD_PREP(IVPU_MMU_CD_0_TCR_ORGN0, 0) | + FIELD_PREP(IVPU_MMU_CD_0_TCR_SH0, 0) | + FIELD_PREP(IVPU_MMU_CD_0_TCR_IPS, IVPU_MMU_IPS_48BIT) | + FIELD_PREP(IVPU_MMU_CD_0_ASID, ssid) | + IVPU_MMU_CD_0_TCR_EPD1 | + IVPU_MMU_CD_0_AA64 | + IVPU_MMU_CD_0_R | + IVPU_MMU_CD_0_ASET; + cd[1] = cd_dma & IVPU_MMU_CD_1_TTB0_MASK; + cd[2] = 0; + cd[3] = 0x0000000000007444; + + /* For global context generate memory fault on VPU */ + if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) + cd[0] |= IVPU_MMU_CD_0_A; + + if (valid) + cd[0] |= IVPU_MMU_CD_0_V; WRITE_ONCE(entry[1], cd[1]); WRITE_ONCE(entry[2], cd[2]); @@ -741,8 +740,8 @@ static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma) if (!ivpu_is_force_snoop_enabled(vdev)) clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE); - ivpu_dbg(vdev, MMU, "CDTAB %s entry (SSID=%u, dma=%pad): 0x%llx, 0x%llx, 0x%llx, 0x%llx\n", - cd_dma ? "write" : "clear", ssid, &cd_dma, cd[0], cd[1], cd[2], cd[3]); + ivpu_dbg(vdev, MMU, "CDTAB set %s entry (SSID=%u, dma=%pad): 0x%llx, 0x%llx, 0x%llx, 0x%llx\n", + valid ? "valid" : "invalid", ssid, &cd_dma, cd[0], cd[1], cd[2], cd[3]); mutex_lock(&mmu->lock); if (!mmu->on) @@ -758,33 +757,6 @@ static int ivpu_mmu_cd_add(struct ivpu_device *vdev, u32 ssid, u64 cd_dma) return ret; } -static int ivpu_mmu_cd_add_gbl(struct ivpu_device *vdev) -{ - int ret; - - ret = ivpu_mmu_cd_add(vdev, 0, vdev->gctx.pgtable.pgd_dma); - if (ret) - ivpu_err(vdev, "Failed to add global CD entry: %d\n", ret); - - return ret; -} - -static int ivpu_mmu_cd_add_user(struct ivpu_device *vdev, u32 ssid, dma_addr_t cd_dma) -{ - int ret; - - if (ssid == 0) { - ivpu_err(vdev, "Invalid SSID: %u\n", ssid); - return -EINVAL; - } - - ret = ivpu_mmu_cd_add(vdev, ssid, cd_dma); - if (ret) - ivpu_err(vdev, "Failed to add CD entry SSID=%u: %d\n", ssid, ret); - - return ret; -} - int ivpu_mmu_init(struct ivpu_device *vdev) { struct ivpu_mmu_info *mmu = vdev->mmu; @@ -808,12 +780,6 @@ int ivpu_mmu_init(struct ivpu_device *vdev) return ret; } - ret = ivpu_mmu_cd_add_gbl(vdev); - if (ret) { - ivpu_err(vdev, "Failed to initialize strtab: %d\n", ret); - return ret; - } - ret = ivpu_mmu_enable(vdev); if (ret) { ivpu_err(vdev, "Failed to resume MMU: %d\n", ret); @@ -966,12 +932,12 @@ void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev) REGV_WR32(IVPU_MMU_REG_GERRORN, gerror_val); } -int ivpu_mmu_set_pgtable(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable) +int ivpu_mmu_cd_set(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable) { - return ivpu_mmu_cd_add_user(vdev, ssid, pgtable->pgd_dma); + return ivpu_mmu_cdtab_entry_set(vdev, ssid, pgtable->pgd_dma, true); } -void ivpu_mmu_clear_pgtable(struct ivpu_device *vdev, int ssid) +void ivpu_mmu_cd_clear(struct ivpu_device *vdev, int ssid) { - ivpu_mmu_cd_add_user(vdev, ssid, 0); /* 0 will clear CD entry */ + ivpu_mmu_cdtab_entry_set(vdev, ssid, 0, false); } diff --git a/drivers/accel/ivpu/ivpu_mmu.h b/drivers/accel/ivpu/ivpu_mmu.h index 6fa35c2407106..7afea9cd8731d 100644 --- a/drivers/accel/ivpu/ivpu_mmu.h +++ b/drivers/accel/ivpu/ivpu_mmu.h @@ -40,8 +40,8 @@ struct ivpu_mmu_info { int ivpu_mmu_init(struct ivpu_device *vdev); void ivpu_mmu_disable(struct ivpu_device *vdev); int ivpu_mmu_enable(struct ivpu_device *vdev); -int ivpu_mmu_set_pgtable(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable); -void ivpu_mmu_clear_pgtable(struct ivpu_device *vdev, int ssid); +int ivpu_mmu_cd_set(struct ivpu_device *vdev, int ssid, struct ivpu_mmu_pgtable *pgtable); +void ivpu_mmu_cd_clear(struct ivpu_device *vdev, int ssid); int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid); void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c index bbe652a7019d0..8992fe93b679a 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.c +++ b/drivers/accel/ivpu/ivpu_mmu_context.c @@ -90,19 +90,6 @@ static void ivpu_pgtable_free_page(struct ivpu_device *vdev, u64 *cpu_addr, dma_ } } -static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) -{ - dma_addr_t pgd_dma; - - pgtable->pgd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pgd_dma); - if (!pgtable->pgd_dma_ptr) - return -ENOMEM; - - pgtable->pgd_dma = pgd_dma; - - return 0; -} - static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) { int pgd_idx, pud_idx, pmd_idx; @@ -140,6 +127,27 @@ static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgt } ivpu_pgtable_free_page(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma); + pgtable->pgd_dma_ptr = NULL; + pgtable->pgd_dma = 0; +} + +static u64* +ivpu_mmu_ensure_pgd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) +{ + u64 *pgd_dma_ptr = pgtable->pgd_dma_ptr; + dma_addr_t pgd_dma; + + if (pgd_dma_ptr) + return pgd_dma_ptr; + + pgd_dma_ptr = ivpu_pgtable_alloc_page(vdev, &pgd_dma); + if (!pgd_dma_ptr) + return NULL; + + pgtable->pgd_dma_ptr = pgd_dma_ptr; + pgtable->pgd_dma = pgd_dma; + + return pgd_dma_ptr; } static u64* @@ -237,6 +245,12 @@ ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); + drm_WARN_ON(&vdev->drm, ctx->id == IVPU_RESERVED_CONTEXT_MMU_SSID); + + /* Allocate PGD - first level page table if needed */ + if (!ivpu_mmu_ensure_pgd(vdev, &ctx->pgtable)) + return -ENOMEM; + /* Allocate PUD - second level page table if needed */ if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx)) return -ENOMEM; @@ -448,12 +462,21 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); if (ret) { ivpu_err(vdev, "Failed to map context pages\n"); - mutex_unlock(&ctx->lock); - return ret; + goto err_unlock; } vpu_addr += size; } + if (!ctx->is_cd_valid) { + ret = ivpu_mmu_cd_set(vdev, ctx->id, &ctx->pgtable); + if (ret) { + ivpu_err(vdev, "Failed to set context descriptor for context %u: %d\n", + ctx->id, ret); + goto err_unlock; + } + ctx->is_cd_valid = true; + } + /* Ensure page table modifications are flushed from wc buffers to memory */ wmb(); @@ -463,6 +486,11 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, if (ret) ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); return ret; + +err_unlock: + mutex_unlock(&ctx->lock); + return ret; + } void @@ -530,20 +558,12 @@ ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *n mutex_unlock(&ctx->lock); } -static int -ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id) +void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id) { u64 start, end; - int ret; mutex_init(&ctx->lock); - ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable); - if (ret) { - ivpu_err(vdev, "Failed to initialize pgtable for ctx %u: %d\n", context_id, ret); - return ret; - } - if (!context_id) { start = vdev->hw->ranges.global.start; end = vdev->hw->ranges.shave.end; @@ -554,41 +574,59 @@ ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u3 drm_mm_init(&ctx->mm, start, end - start); ctx->id = context_id; - - return 0; } -static void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) +void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) { - if (drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd_dma_ptr)) - return; + if (ctx->is_cd_valid) { + ivpu_mmu_cd_clear(vdev, ctx->id); + ctx->is_cd_valid = false; + } mutex_destroy(&ctx->lock); ivpu_mmu_pgtables_free(vdev, &ctx->pgtable); drm_mm_takedown(&ctx->mm); - - ctx->pgtable.pgd_dma_ptr = NULL; - ctx->pgtable.pgd_dma = 0; } -int ivpu_mmu_global_context_init(struct ivpu_device *vdev) +void ivpu_mmu_global_context_init(struct ivpu_device *vdev) { - return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID); + ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID); } void ivpu_mmu_global_context_fini(struct ivpu_device *vdev) { - return ivpu_mmu_context_fini(vdev, &vdev->gctx); + ivpu_mmu_context_fini(vdev, &vdev->gctx); } int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev) { - return ivpu_mmu_user_context_init(vdev, &vdev->rctx, IVPU_RESERVED_CONTEXT_MMU_SSID); + int ret; + + ivpu_mmu_context_init(vdev, &vdev->rctx, IVPU_RESERVED_CONTEXT_MMU_SSID); + + mutex_lock(&vdev->rctx.lock); + + if (!ivpu_mmu_ensure_pgd(vdev, &vdev->rctx.pgtable)) { + ivpu_err(vdev, "Failed to allocate root page table for reserved context\n"); + ret = -ENOMEM; + goto unlock; + } + + ret = ivpu_mmu_cd_set(vdev, vdev->rctx.id, &vdev->rctx.pgtable); + if (ret) { + ivpu_err(vdev, "Failed to set context descriptor for reserved context\n"); + goto unlock; + } + +unlock: + mutex_unlock(&vdev->rctx.lock); + return ret; } void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev) { - return ivpu_mmu_user_context_fini(vdev, &vdev->rctx); + ivpu_mmu_cd_clear(vdev, vdev->rctx.id); + ivpu_mmu_context_fini(vdev, &vdev->rctx); } void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid) @@ -603,36 +641,3 @@ void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid) xa_unlock(&vdev->context_xa); } - -int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id) -{ - int ret; - - drm_WARN_ON(&vdev->drm, !ctx_id); - - ret = ivpu_mmu_context_init(vdev, ctx, ctx_id); - if (ret) { - ivpu_err(vdev, "Failed to initialize context %u: %d\n", ctx_id, ret); - return ret; - } - - ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable); - if (ret) { - ivpu_err(vdev, "Failed to set page table for context %u: %d\n", ctx_id, ret); - goto err_context_fini; - } - - return 0; - -err_context_fini: - ivpu_mmu_context_fini(vdev, ctx); - return ret; -} - -void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) -{ - drm_WARN_ON(&vdev->drm, !ctx->id); - - ivpu_mmu_clear_pgtable(vdev, ctx->id); - ivpu_mmu_context_fini(vdev, ctx); -} diff --git a/drivers/accel/ivpu/ivpu_mmu_context.h b/drivers/accel/ivpu/ivpu_mmu_context.h index 7f9aaf3d10c2f..8042fc0670622 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.h +++ b/drivers/accel/ivpu/ivpu_mmu_context.h @@ -23,19 +23,20 @@ struct ivpu_mmu_pgtable { }; struct ivpu_mmu_context { - struct mutex lock; /* Protects: mm, pgtable */ + struct mutex lock; /* Protects: mm, pgtable, is_cd_valid */ struct drm_mm mm; struct ivpu_mmu_pgtable pgtable; + bool is_cd_valid; u32 id; }; -int ivpu_mmu_global_context_init(struct ivpu_device *vdev); +void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id); +void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx); +void ivpu_mmu_global_context_init(struct ivpu_device *vdev); void ivpu_mmu_global_context_fini(struct ivpu_device *vdev); int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev); void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev); -int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id); -void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx); void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid); int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range, From 94b2a2c0e7cba3f163609dbd94120ee533ad2a07 Mon Sep 17 00:00:00 2001 From: Andrzej Kacprowski Date: Thu, 17 Oct 2024 16:58:09 +0200 Subject: [PATCH 63/90] accel/ivpu: Remove copy engine support Copy engine was deprecated by the FW and is no longer supported. Compute engine includes all copy engine functionality and should be used instead. This change does not affect user space as the copy engine was never used outside of a couple of tests. Signed-off-by: Andrzej Kacprowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-4-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_drv.h | 5 +--- drivers/accel/ivpu/ivpu_job.c | 43 +++++++++++-------------------- drivers/accel/ivpu/ivpu_jsm_msg.c | 8 +++--- include/uapi/drm/ivpu_accel.h | 6 +---- 4 files changed, 21 insertions(+), 41 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index f905021ac1748..5b4f5104b4708 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -49,11 +49,8 @@ #define IVPU_JOB_ID_JOB_MASK GENMASK(7, 0) #define IVPU_JOB_ID_CONTEXT_MASK GENMASK(31, 8) -#define IVPU_NUM_ENGINES 2 #define IVPU_NUM_PRIORITIES 4 -#define IVPU_NUM_CMDQS_PER_CTX (IVPU_NUM_ENGINES * IVPU_NUM_PRIORITIES) - -#define IVPU_CMDQ_INDEX(engine, priority) ((engine) * IVPU_NUM_PRIORITIES + (priority)) +#define IVPU_NUM_CMDQS_PER_CTX (IVPU_NUM_PRIORITIES) #define IVPU_PLATFORM_SILICON 0 #define IVPU_PLATFORM_SIMICS 2 diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 98e0b7b614071..f580959e87787 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -247,8 +247,7 @@ static int ivpu_cmdq_fini(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cm static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u16 engine, u8 priority) { - int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority); - struct ivpu_cmdq *cmdq = file_priv->cmdq[cmdq_idx]; + struct ivpu_cmdq *cmdq = file_priv->cmdq[priority]; int ret; lockdep_assert_held(&file_priv->lock); @@ -257,7 +256,7 @@ static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u16 cmdq = ivpu_cmdq_alloc(file_priv); if (!cmdq) return NULL; - file_priv->cmdq[cmdq_idx] = cmdq; + file_priv->cmdq[priority] = cmdq; } ret = ivpu_cmdq_init(file_priv, cmdq, engine, priority); @@ -267,15 +266,14 @@ static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u16 return cmdq; } -static void ivpu_cmdq_release_locked(struct ivpu_file_priv *file_priv, u16 engine, u8 priority) +static void ivpu_cmdq_release_locked(struct ivpu_file_priv *file_priv, u8 priority) { - int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority); - struct ivpu_cmdq *cmdq = file_priv->cmdq[cmdq_idx]; + struct ivpu_cmdq *cmdq = file_priv->cmdq[priority]; lockdep_assert_held(&file_priv->lock); if (cmdq) { - file_priv->cmdq[cmdq_idx] = NULL; + file_priv->cmdq[priority] = NULL; ivpu_cmdq_fini(file_priv, cmdq); ivpu_cmdq_free(file_priv, cmdq); } @@ -283,14 +281,12 @@ static void ivpu_cmdq_release_locked(struct ivpu_file_priv *file_priv, u16 engin void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv) { - u16 engine; u8 priority; lockdep_assert_held(&file_priv->lock); - for (engine = 0; engine < IVPU_NUM_ENGINES; engine++) - for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) - ivpu_cmdq_release_locked(file_priv, engine, priority); + for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) + ivpu_cmdq_release_locked(file_priv, priority); } /* @@ -301,19 +297,15 @@ void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv) */ static void ivpu_cmdq_reset(struct ivpu_file_priv *file_priv) { - u16 engine; u8 priority; mutex_lock(&file_priv->lock); - for (engine = 0; engine < IVPU_NUM_ENGINES; engine++) { - for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) { - int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority); - struct ivpu_cmdq *cmdq = file_priv->cmdq[cmdq_idx]; + for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) { + struct ivpu_cmdq *cmdq = file_priv->cmdq[priority]; - if (cmdq) - cmdq->db_registered = false; - } + if (cmdq) + cmdq->db_registered = false; } mutex_unlock(&file_priv->lock); @@ -334,16 +326,11 @@ void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev) static void ivpu_cmdq_fini_all(struct ivpu_file_priv *file_priv) { - u16 engine; u8 priority; - for (engine = 0; engine < IVPU_NUM_ENGINES; engine++) { - for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) { - int cmdq_idx = IVPU_CMDQ_INDEX(engine, priority); - - if (file_priv->cmdq[cmdq_idx]) - ivpu_cmdq_fini(file_priv, file_priv->cmdq[cmdq_idx]); - } + for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) { + if (file_priv->cmdq[priority]) + ivpu_cmdq_fini(file_priv, file_priv->cmdq[priority]); } } @@ -699,7 +686,7 @@ int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file) int idx, ret; u8 priority; - if (params->engine > DRM_IVPU_ENGINE_COPY) + if (params->engine != DRM_IVPU_ENGINE_COMPUTE) return -EINVAL; if (params->priority > DRM_IVPU_JOB_PRIORITY_REALTIME) diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c index 8f5d904ca1ed4..30a40be769301 100644 --- a/drivers/accel/ivpu/ivpu_jsm_msg.c +++ b/drivers/accel/ivpu/ivpu_jsm_msg.c @@ -132,7 +132,7 @@ int ivpu_jsm_get_heartbeat(struct ivpu_device *vdev, u32 engine, u64 *heartbeat) struct vpu_jsm_msg resp; int ret; - if (engine > VPU_ENGINE_COPY) + if (engine != VPU_ENGINE_COMPUTE) return -EINVAL; req.payload.query_engine_hb.engine_idx = engine; @@ -155,7 +155,7 @@ int ivpu_jsm_reset_engine(struct ivpu_device *vdev, u32 engine) struct vpu_jsm_msg resp; int ret; - if (engine > VPU_ENGINE_COPY) + if (engine != VPU_ENGINE_COMPUTE) return -EINVAL; req.payload.engine_reset.engine_idx = engine; @@ -174,7 +174,7 @@ int ivpu_jsm_preempt_engine(struct ivpu_device *vdev, u32 engine, u32 preempt_id struct vpu_jsm_msg resp; int ret; - if (engine > VPU_ENGINE_COPY) + if (engine != VPU_ENGINE_COMPUTE) return -EINVAL; req.payload.engine_preempt.engine_idx = engine; @@ -346,7 +346,7 @@ int ivpu_jsm_hws_resume_engine(struct ivpu_device *vdev, u32 engine) struct vpu_jsm_msg resp; int ret; - if (engine >= VPU_ENGINE_NB) + if (engine != VPU_ENGINE_COMPUTE) return -EINVAL; req.payload.hws_resume_engine.engine_idx = engine; diff --git a/include/uapi/drm/ivpu_accel.h b/include/uapi/drm/ivpu_accel.h index 234664d352507..a35b97b097bf6 100644 --- a/include/uapi/drm/ivpu_accel.h +++ b/include/uapi/drm/ivpu_accel.h @@ -258,7 +258,7 @@ struct drm_ivpu_bo_info { /* drm_ivpu_submit engines */ #define DRM_IVPU_ENGINE_COMPUTE 0 -#define DRM_IVPU_ENGINE_COPY 1 +#define DRM_IVPU_ENGINE_COPY 1 /* Deprecated */ /** * struct drm_ivpu_submit - Submit commands to the VPU @@ -289,10 +289,6 @@ struct drm_ivpu_submit { * %DRM_IVPU_ENGINE_COMPUTE: * * Performs Deep Learning Neural Compute Inference Operations - * - * %DRM_IVPU_ENGINE_COPY: - * - * Performs memory copy operations to/from system memory allocated for VPU */ __u32 engine; From add38f8211b5dcf447a50bea4da54c391e39336c Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:10 +0200 Subject: [PATCH 64/90] accel/ivpu: Clear CDTAB entry in case of failure Don't leave a context descriptor in case CFGI_ALL flush fails. Mark it as invalid (by clearing valid bit) so nothing is left in partially-initialized state. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-5-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_mmu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c index 4ff0d7a519859..26ef52fbb93e5 100644 --- a/drivers/accel/ivpu/ivpu_mmu.c +++ b/drivers/accel/ivpu/ivpu_mmu.c @@ -749,10 +749,17 @@ static int ivpu_mmu_cdtab_entry_set(struct ivpu_device *vdev, u32 ssid, u64 cd_d ret = ivpu_mmu_cmdq_write_cfgi_all(vdev); if (ret) - goto unlock; + goto err_invalidate; ret = ivpu_mmu_cmdq_sync(vdev); + if (ret) + goto err_invalidate; unlock: + mutex_unlock(&mmu->lock); + return 0; + +err_invalidate: + WRITE_ONCE(entry[0], 0); mutex_unlock(&mmu->lock); return ret; } From 1fc65fa96ff4703e8d26dda351d942e8940f322f Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:11 +0200 Subject: [PATCH 65/90] accel/ivpu: Unmap partially mapped BOs in case of errors Ensure that all buffers that were created only partially through allocated scatter-gather table are unmapped from MMU600 in case of errors. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-6-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_mmu_context.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c index 8992fe93b679a..697b57071d546 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.c +++ b/drivers/accel/ivpu/ivpu_mmu_context.c @@ -432,6 +432,7 @@ int ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, struct sg_table *sgt, bool llc_coherent) { + size_t start_vpu_addr = vpu_addr; struct scatterlist *sg; int ret; u64 prot; @@ -462,7 +463,7 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); if (ret) { ivpu_err(vdev, "Failed to map context pages\n"); - goto err_unlock; + goto err_unmap_pages; } vpu_addr += size; } @@ -472,7 +473,7 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, if (ret) { ivpu_err(vdev, "Failed to set context descriptor for context %u: %d\n", ctx->id, ret); - goto err_unlock; + goto err_unmap_pages; } ctx->is_cd_valid = true; } @@ -480,17 +481,19 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, /* Ensure page table modifications are flushed from wc buffers to memory */ wmb(); - mutex_unlock(&ctx->lock); - ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); - if (ret) + if (ret) { ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); - return ret; + goto err_unmap_pages; + } -err_unlock: mutex_unlock(&ctx->lock); - return ret; + return 0; +err_unmap_pages: + ivpu_mmu_context_unmap_pages(ctx, start_vpu_addr, vpu_addr - start_vpu_addr); + mutex_unlock(&ctx->lock); + return ret; } void From ae7af7d8dc2a13a427aa90d003fe4fb2c168342a Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:12 +0200 Subject: [PATCH 66/90] accel/ivpu: Use xa_alloc_cyclic() instead of custom function Remove custom ivpu_id_alloc() wrapper used for ID allocations and replace it with standard xa_alloc_cyclic() API. The idea behind ivpu_id_alloc() was to have monotonic IDs, so the driver is easier to debug because same IDs are not reused all over. The same can be achieved just by using appropriate Linux API. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-7-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_drv.c | 11 ++++------- drivers/accel/ivpu/ivpu_drv.h | 4 ++-- drivers/accel/ivpu/ivpu_job.c | 34 ++++++---------------------------- 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 34e3e9b1c3f23..383e3eb988983 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -256,10 +256,8 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) ivpu_mmu_context_init(vdev, &file_priv->ctx, ctx_id); - file_priv->default_job_limit.min = FIELD_PREP(IVPU_JOB_ID_CONTEXT_MASK, - (file_priv->ctx.id - 1)); - file_priv->default_job_limit.max = file_priv->default_job_limit.min | IVPU_JOB_ID_JOB_MASK; - file_priv->job_limit = file_priv->default_job_limit; + file_priv->job_limit.min = FIELD_PREP(IVPU_JOB_ID_CONTEXT_MASK, (file_priv->ctx.id - 1)); + file_priv->job_limit.max = file_priv->job_limit.min | IVPU_JOB_ID_JOB_MASK; mutex_unlock(&vdev->context_list_lock); drm_dev_exit(idx); @@ -618,9 +616,8 @@ static int ivpu_dev_init(struct ivpu_device *vdev) lockdep_set_class(&vdev->submitted_jobs_xa.xa_lock, &submitted_jobs_xa_lock_class_key); INIT_LIST_HEAD(&vdev->bo_list); - vdev->default_db_limit.min = IVPU_MIN_DB; - vdev->default_db_limit.max = IVPU_MAX_DB; - vdev->db_limit = vdev->default_db_limit; + vdev->db_limit.min = IVPU_MIN_DB; + vdev->db_limit.max = IVPU_MAX_DB; ret = drmm_mutex_init(&vdev->drm, &vdev->context_list_lock); if (ret) diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 5b4f5104b4708..6774402821706 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -137,7 +137,7 @@ struct ivpu_device { struct xarray db_xa; struct xa_limit db_limit; - struct xa_limit default_db_limit; + u32 db_next; struct mutex bo_list_lock; /* Protects bo_list */ struct list_head bo_list; @@ -174,7 +174,7 @@ struct ivpu_file_priv { struct list_head ms_instance_list; struct ivpu_bo *ms_info_bo; struct xa_limit job_limit; - struct xa_limit default_job_limit; + u32 job_id_next; bool has_mmu_faults; bool bound; bool aborted; diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index f580959e87787..9154c2e14245f 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -72,26 +72,6 @@ static void ivpu_preemption_buffers_free(struct ivpu_device *vdev, ivpu_bo_free(cmdq->secondary_preempt_buf); } -static int ivpu_id_alloc(struct xarray *xa, u32 *id, void *entry, struct xa_limit *limit, - const struct xa_limit default_limit) -{ - int ret; - - ret = __xa_alloc(xa, id, entry, *limit, GFP_KERNEL); - if (ret) { - limit->min = default_limit.min; - ret = __xa_alloc(xa, id, entry, *limit, GFP_KERNEL); - if (ret) - return ret; - } - - limit->min = *id + 1; - if (limit->min > limit->max) - limit->min = default_limit.min; - - return ret; -} - static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv) { struct ivpu_device *vdev = file_priv->vdev; @@ -102,11 +82,9 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv) if (!cmdq) return NULL; - xa_lock(&vdev->db_xa); /* lock here to protect db_limit */ - ret = ivpu_id_alloc(&vdev->db_xa, &cmdq->db_id, NULL, &vdev->db_limit, - vdev->default_db_limit); - xa_unlock(&vdev->db_xa); - if (ret) { + ret = xa_alloc_cyclic(&vdev->db_xa, &cmdq->db_id, NULL, vdev->db_limit, &vdev->db_next, + GFP_KERNEL); + if (ret < 0) { ivpu_err(vdev, "Failed to allocate doorbell id: %d\n", ret); goto err_free_cmdq; } @@ -554,9 +532,9 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority) xa_lock(&vdev->submitted_jobs_xa); is_first_job = xa_empty(&vdev->submitted_jobs_xa); - ret = ivpu_id_alloc(&vdev->submitted_jobs_xa, &job->job_id, job, &file_priv->job_limit, - file_priv->default_job_limit); - if (ret) { + ret = __xa_alloc_cyclic(&vdev->submitted_jobs_xa, &job->job_id, job, file_priv->job_limit, + &file_priv->job_id_next, GFP_KERNEL); + if (ret < 0) { ivpu_dbg(vdev, JOB, "Too many active jobs in ctx %d\n", file_priv->ctx.id); ret = -EBUSY; From 76ad741ec7349bb1112f3a0ff27adf1ca75cf025 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:13 +0200 Subject: [PATCH 67/90] accel/ivpu: Make command queue ID allocated on XArray Use XArray for dynamic command queue ID allocations instead of fixed ones. This is required by upcoming changes to UAPI that will allow to manage command queues by user space instead of having predefined number of queues in a context. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-8-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_drv.c | 6 +++ drivers/accel/ivpu/ivpu_drv.h | 7 ++- drivers/accel/ivpu/ivpu_job.c | 91 ++++++++++++++++++----------------- drivers/accel/ivpu/ivpu_job.h | 2 + 4 files changed, 60 insertions(+), 46 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 383e3eb988983..f5a8d93fe2a57 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -104,6 +104,8 @@ static void file_priv_release(struct kref *ref) pm_runtime_get_sync(vdev->drm.dev); mutex_lock(&vdev->context_list_lock); file_priv_unbind(vdev, file_priv); + drm_WARN_ON(&vdev->drm, !xa_empty(&file_priv->cmdq_xa)); + xa_destroy(&file_priv->cmdq_xa); mutex_unlock(&vdev->context_list_lock); pm_runtime_put_autosuspend(vdev->drm.dev); @@ -259,6 +261,10 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) file_priv->job_limit.min = FIELD_PREP(IVPU_JOB_ID_CONTEXT_MASK, (file_priv->ctx.id - 1)); file_priv->job_limit.max = file_priv->job_limit.min | IVPU_JOB_ID_JOB_MASK; + xa_init_flags(&file_priv->cmdq_xa, XA_FLAGS_ALLOC1); + file_priv->cmdq_limit.min = IVPU_CMDQ_MIN_ID; + file_priv->cmdq_limit.max = IVPU_CMDQ_MAX_ID; + mutex_unlock(&vdev->context_list_lock); drm_dev_exit(idx); diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 6774402821706..8e79d78906bfe 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -52,6 +52,9 @@ #define IVPU_NUM_PRIORITIES 4 #define IVPU_NUM_CMDQS_PER_CTX (IVPU_NUM_PRIORITIES) +#define IVPU_CMDQ_MIN_ID 1 +#define IVPU_CMDQ_MAX_ID 255 + #define IVPU_PLATFORM_SILICON 0 #define IVPU_PLATFORM_SIMICS 2 #define IVPU_PLATFORM_FPGA 3 @@ -168,13 +171,15 @@ struct ivpu_file_priv { struct kref ref; struct ivpu_device *vdev; struct mutex lock; /* Protects cmdq */ - struct ivpu_cmdq *cmdq[IVPU_NUM_CMDQS_PER_CTX]; + struct xarray cmdq_xa; struct ivpu_mmu_context ctx; struct mutex ms_lock; /* Protects ms_instance_list, ms_info_bo */ struct list_head ms_instance_list; struct ivpu_bo *ms_info_bo; struct xa_limit job_limit; u32 job_id_next; + struct xa_limit cmdq_limit; + u32 cmdq_id_next; bool has_mmu_faults; bool bound; bool aborted; diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 9154c2e14245f..82a57a30244d3 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -89,9 +89,16 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv) goto err_free_cmdq; } + ret = xa_alloc_cyclic(&file_priv->cmdq_xa, &cmdq->id, cmdq, file_priv->cmdq_limit, + &file_priv->cmdq_id_next, GFP_KERNEL); + if (ret < 0) { + ivpu_err(vdev, "Failed to allocate command queue id: %d\n", ret); + goto err_erase_db_xa; + } + cmdq->mem = ivpu_bo_create_global(vdev, SZ_4K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); if (!cmdq->mem) - goto err_erase_xa; + goto err_erase_cmdq_xa; ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq); if (ret) @@ -99,7 +106,9 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv) return cmdq; -err_erase_xa: +err_erase_cmdq_xa: + xa_erase(&file_priv->cmdq_xa, cmdq->id); +err_erase_db_xa: xa_erase(&vdev->db_xa, cmdq->db_id); err_free_cmdq: kfree(cmdq); @@ -123,13 +132,13 @@ static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq struct ivpu_device *vdev = file_priv->vdev; int ret; - ret = ivpu_jsm_hws_create_cmdq(vdev, file_priv->ctx.id, file_priv->ctx.id, cmdq->db_id, + ret = ivpu_jsm_hws_create_cmdq(vdev, file_priv->ctx.id, file_priv->ctx.id, cmdq->id, task_pid_nr(current), engine, cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem)); if (ret) return ret; - ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->db_id, + ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->id, priority); if (ret) return ret; @@ -143,20 +152,21 @@ static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq * int ret; if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) - ret = ivpu_jsm_hws_register_db(vdev, file_priv->ctx.id, cmdq->db_id, cmdq->db_id, + ret = ivpu_jsm_hws_register_db(vdev, file_priv->ctx.id, cmdq->id, cmdq->db_id, cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem)); else ret = ivpu_jsm_register_db(vdev, file_priv->ctx.id, cmdq->db_id, cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem)); if (!ret) - ivpu_dbg(vdev, JOB, "DB %d registered to ctx %d\n", cmdq->db_id, file_priv->ctx.id); + ivpu_dbg(vdev, JOB, "DB %d registered to cmdq %d ctx %d\n", + cmdq->db_id, cmdq->id, file_priv->ctx.id); return ret; } static int -ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 engine, u8 priority) +ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u8 priority) { struct ivpu_device *vdev = file_priv->vdev; struct vpu_job_queue_header *jobq_header; @@ -172,7 +182,7 @@ ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 eng cmdq->jobq = (struct vpu_job_queue *)ivpu_bo_vaddr(cmdq->mem); jobq_header = &cmdq->jobq->header; - jobq_header->engine_idx = engine; + jobq_header->engine_idx = VPU_ENGINE_COMPUTE; jobq_header->head = 0; jobq_header->tail = 0; if (ivpu_test_mode & IVPU_TEST_MODE_TURBO) { @@ -183,7 +193,7 @@ ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 eng wmb(); /* Flush WC buffer for jobq->header */ if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) { - ret = ivpu_hws_cmdq_init(file_priv, cmdq, engine, priority); + ret = ivpu_hws_cmdq_init(file_priv, cmdq, VPU_ENGINE_COMPUTE, priority); if (ret) return ret; } @@ -210,9 +220,9 @@ static int ivpu_cmdq_fini(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cm cmdq->db_registered = false; if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) { - ret = ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->db_id); + ret = ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); if (!ret) - ivpu_dbg(vdev, JOB, "Command queue %d destroyed\n", cmdq->db_id); + ivpu_dbg(vdev, JOB, "Command queue %d destroyed\n", cmdq->id); } ret = ivpu_jsm_unregister_db(vdev, cmdq->db_id); @@ -222,51 +232,46 @@ static int ivpu_cmdq_fini(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cm return 0; } -static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u16 engine, - u8 priority) +static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u8 priority) { - struct ivpu_cmdq *cmdq = file_priv->cmdq[priority]; + struct ivpu_cmdq *cmdq; + unsigned long cmdq_id; int ret; lockdep_assert_held(&file_priv->lock); + xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) + if (cmdq->priority == priority) + break; + if (!cmdq) { cmdq = ivpu_cmdq_alloc(file_priv); if (!cmdq) return NULL; - file_priv->cmdq[priority] = cmdq; + cmdq->priority = priority; } - ret = ivpu_cmdq_init(file_priv, cmdq, engine, priority); + ret = ivpu_cmdq_init(file_priv, cmdq, priority); if (ret) return NULL; return cmdq; } -static void ivpu_cmdq_release_locked(struct ivpu_file_priv *file_priv, u8 priority) +void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv) { - struct ivpu_cmdq *cmdq = file_priv->cmdq[priority]; + struct ivpu_cmdq *cmdq; + unsigned long cmdq_id; lockdep_assert_held(&file_priv->lock); - if (cmdq) { - file_priv->cmdq[priority] = NULL; + xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) { + xa_erase(&file_priv->cmdq_xa, cmdq_id); ivpu_cmdq_fini(file_priv, cmdq); ivpu_cmdq_free(file_priv, cmdq); } } -void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv) -{ - u8 priority; - - lockdep_assert_held(&file_priv->lock); - - for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) - ivpu_cmdq_release_locked(file_priv, priority); -} - /* * Mark the doorbell as unregistered * This function needs to be called when the VPU hardware is restarted @@ -275,16 +280,13 @@ void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv) */ static void ivpu_cmdq_reset(struct ivpu_file_priv *file_priv) { - u8 priority; + struct ivpu_cmdq *cmdq; + unsigned long cmdq_id; mutex_lock(&file_priv->lock); - for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) { - struct ivpu_cmdq *cmdq = file_priv->cmdq[priority]; - - if (cmdq) - cmdq->db_registered = false; - } + xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) + cmdq->db_registered = false; mutex_unlock(&file_priv->lock); } @@ -304,12 +306,11 @@ void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev) static void ivpu_cmdq_fini_all(struct ivpu_file_priv *file_priv) { - u8 priority; + struct ivpu_cmdq *cmdq; + unsigned long cmdq_id; - for (priority = 0; priority < IVPU_NUM_PRIORITIES; priority++) { - if (file_priv->cmdq[priority]) - ivpu_cmdq_fini(file_priv, file_priv->cmdq[priority]); - } + xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) + ivpu_cmdq_fini(file_priv, cmdq); } void ivpu_context_abort_locked(struct ivpu_file_priv *file_priv) @@ -334,8 +335,8 @@ static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job) /* Check if there is space left in job queue */ if (next_entry == header->head) { - ivpu_dbg(vdev, JOB, "Job queue full: ctx %d engine %d db %d head %d tail %d\n", - job->file_priv->ctx.id, job->engine_idx, cmdq->db_id, header->head, tail); + ivpu_dbg(vdev, JOB, "Job queue full: ctx %d cmdq %d db %d head %d tail %d\n", + job->file_priv->ctx.id, cmdq->id, cmdq->db_id, header->head, tail); return -EBUSY; } @@ -522,7 +523,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority) mutex_lock(&file_priv->lock); - cmdq = ivpu_cmdq_acquire(file_priv, job->engine_idx, priority); + cmdq = ivpu_cmdq_acquire(file_priv, priority); if (!cmdq) { ivpu_warn_ratelimited(vdev, "Failed to get job queue, ctx %d engine %d prio %d\n", file_priv->ctx.id, job->engine_idx, priority); diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h index 6accb94028c7a..8b19e3f8b4cfb 100644 --- a/drivers/accel/ivpu/ivpu_job.h +++ b/drivers/accel/ivpu/ivpu_job.h @@ -28,8 +28,10 @@ struct ivpu_cmdq { struct ivpu_bo *secondary_preempt_buf; struct ivpu_bo *mem; u32 entry_count; + u32 id; u32 db_id; bool db_registered; + u8 priority; }; /** From 7df06efe1c28b25ad02e49987cd0bc1661615129 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:14 +0200 Subject: [PATCH 68/90] accel/ivpu: Don't allocate preemption buffers when MIP is disabled Do not allocate preemption buffers when Mid Inference Preemption (MIP) is disabled through test mode. Rename IVPU_TEST_MODE_PREEMPTION_DISABLE to IVPU_TEST_MODE_MIP_DISABLE to better describe that this test mode only disables MIP - job level preemption will still occur. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-9-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_drv.h | 2 +- drivers/accel/ivpu/ivpu_job.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 8e79d78906bfe..3fdff3f6cffd8 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -197,7 +197,7 @@ extern bool ivpu_force_snoop; #define IVPU_TEST_MODE_NULL_SUBMISSION BIT(2) #define IVPU_TEST_MODE_D0I3_MSG_DISABLE BIT(4) #define IVPU_TEST_MODE_D0I3_MSG_ENABLE BIT(5) -#define IVPU_TEST_MODE_PREEMPTION_DISABLE BIT(6) +#define IVPU_TEST_MODE_MIP_DISABLE BIT(6) #define IVPU_TEST_MODE_DISABLE_TIMEOUTS BIT(8) #define IVPU_TEST_MODE_TURBO BIT(9) extern int ivpu_test_mode; diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 82a57a30244d3..39ba6d3d8b0de 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -35,7 +35,8 @@ static int ivpu_preemption_buffers_create(struct ivpu_device *vdev, u64 primary_size = ALIGN(vdev->fw->primary_preempt_buf_size, PAGE_SIZE); u64 secondary_size = ALIGN(vdev->fw->secondary_preempt_buf_size, PAGE_SIZE); - if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW) + if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW || + ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE) return 0; cmdq->primary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.user, @@ -347,8 +348,7 @@ static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job) if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_SUBMISSION)) entry->flags = VPU_JOB_FLAGS_NULL_SUBMISSION_MASK; - if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW && - (unlikely(!(ivpu_test_mode & IVPU_TEST_MODE_PREEMPTION_DISABLE)))) { + if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) { if (cmdq->primary_preempt_buf) { entry->primary_preempt_buf_addr = cmdq->primary_preempt_buf->vpu_addr; entry->primary_preempt_buf_size = ivpu_bo_size(cmdq->primary_preempt_buf); From 5f8600b9d5a20b01b720b4deeade7a88316aa4e3 Mon Sep 17 00:00:00 2001 From: Maciej Falkowski Date: Thu, 17 Oct 2024 16:58:15 +0200 Subject: [PATCH 69/90] accel/ivpu: Add debug Kconfig option Add CONFIG_DRM_ACCEL_IVPU_DEBUG option that: - Adds -DDEBUG that enables printk regardless of the kernel config - Enables unsafe module params (that are now disabled by default) Signed-off-by: Maciej Falkowski Reviewed-by: Jacek Lawrynowicz Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-10-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/Kconfig | 9 +++++++++ drivers/accel/ivpu/Makefile | 2 ++ drivers/accel/ivpu/ivpu_drv.c | 2 ++ drivers/accel/ivpu/ivpu_fw.c | 2 ++ drivers/accel/ivpu/ivpu_pm.c | 2 ++ 5 files changed, 17 insertions(+) diff --git a/drivers/accel/ivpu/Kconfig b/drivers/accel/ivpu/Kconfig index e4d418b44626e..9e055b5ce03d2 100644 --- a/drivers/accel/ivpu/Kconfig +++ b/drivers/accel/ivpu/Kconfig @@ -16,3 +16,12 @@ config DRM_ACCEL_IVPU and Deep Learning applications. If "M" is selected, the module will be called intel_vpu. + +config DRM_ACCEL_IVPU_DEBUG + bool "Intel NPU debug mode" + depends on DRM_ACCEL_IVPU + help + Choose this option to enable additional + debug features for the Intel NPU driver: + - Always print debug messages regardless of dyndbg config, + - Enable unsafe module params. diff --git a/drivers/accel/ivpu/Makefile b/drivers/accel/ivpu/Makefile index e73937c86d9ad..1029e0bab0615 100644 --- a/drivers/accel/ivpu/Makefile +++ b/drivers/accel/ivpu/Makefile @@ -24,4 +24,6 @@ intel_vpu-$(CONFIG_DEV_COREDUMP) += ivpu_coredump.o obj-$(CONFIG_DRM_ACCEL_IVPU) += intel_vpu.o +subdir-ccflags-$(CONFIG_DRM_ACCEL_IVPU_DEBUG) += -DDEBUG + CFLAGS_ivpu_trace_points.o = -I$(src) diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index f5a8d93fe2a57..ca2bf47ce2484 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -43,8 +43,10 @@ module_param_named(dbg_mask, ivpu_dbg_mask, int, 0644); MODULE_PARM_DESC(dbg_mask, "Driver debug mask. See IVPU_DBG_* macros."); int ivpu_test_mode; +#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG) module_param_named_unsafe(test_mode, ivpu_test_mode, int, 0644); MODULE_PARM_DESC(test_mode, "Test mode mask. See IVPU_TEST_MODE_* macros."); +#endif u8 ivpu_pll_min_ratio; module_param_named(pll_min_ratio, ivpu_pll_min_ratio, byte, 0644); diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index be367465e7df4..d358cf0b0f972 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -46,8 +46,10 @@ #define IVPU_FOCUS_PRESENT_TIMER_MS 1000 static char *ivpu_firmware; +#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG) module_param_named_unsafe(firmware, ivpu_firmware, charp, 0644); MODULE_PARM_DESC(firmware, "NPU firmware binary in /lib/firmware/.."); +#endif static struct { int gen; diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index e567df79a6129..dbc0711e28d13 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -24,8 +24,10 @@ #include "vpu_boot_api.h" static bool ivpu_disable_recovery; +#if IS_ENABLED(CONFIG_DRM_ACCEL_IVPU_DEBUG) module_param_named_unsafe(disable_recovery, ivpu_disable_recovery, bool, 0644); MODULE_PARM_DESC(disable_recovery, "Disables recovery when NPU hang is detected"); +#endif static unsigned long ivpu_tdr_timeout_ms; module_param_named(tdr_timeout_ms, ivpu_tdr_timeout_ms, ulong, 0644); From 83b6fa5844b53fe25417229e44c460e4f84da432 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:16 +0200 Subject: [PATCH 70/90] accel/ivpu: Increase DMA address range Increase DMA address range to: * 128 GB on 37xx (due to MMU limitations) * 256 GB on other generations Merge User and DMA ranges on 40xx and above as it is possible to access whole 256 GBs from both FW and DMA. Increase User range on 37xx from 255MB to 511MB to allow loading very large models. Do not set global_alias_pio_base/size on other generations than 37xx as it's only used on 37xx anyway. Signed-off-by: Karol Wachowski Signed-off-by: Andrzej Kacprowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-11-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_fw.c | 6 ++++-- drivers/accel/ivpu/ivpu_hw.c | 10 +++++----- drivers/accel/ivpu/ivpu_mmu_context.c | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index d358cf0b0f972..6037ec0b30968 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -584,8 +584,10 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params boot_params->ipc_payload_area_start = ipc_mem_rx->vpu_addr + ivpu_bo_size(ipc_mem_rx) / 2; boot_params->ipc_payload_area_size = ivpu_bo_size(ipc_mem_rx) / 2; - boot_params->global_aliased_pio_base = vdev->hw->ranges.user.start; - boot_params->global_aliased_pio_size = ivpu_hw_range_size(&vdev->hw->ranges.user); + if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { + boot_params->global_aliased_pio_base = vdev->hw->ranges.user.start; + boot_params->global_aliased_pio_size = ivpu_hw_range_size(&vdev->hw->ranges.user); + } /* Allow configuration for L2C_PAGE_TABLE with boot param value */ boot_params->autoconfig = 1; diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c index 1c259d7178151..09ada8b500b99 100644 --- a/drivers/accel/ivpu/ivpu_hw.c +++ b/drivers/accel/ivpu/ivpu_hw.c @@ -114,14 +114,14 @@ static void memory_ranges_init(struct ivpu_device *vdev) { if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M); - ivpu_hw_range_init(&vdev->hw->ranges.user, 0xc0000000, 255 * SZ_1M); + ivpu_hw_range_init(&vdev->hw->ranges.user, 0x88000000, 511 * SZ_1M); ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x180000000, SZ_2G); - ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_8G); + ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_128G); } else { ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M); - ivpu_hw_range_init(&vdev->hw->ranges.user, 0x80000000, SZ_256M); - ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x80000000 + SZ_256M, SZ_2G - SZ_256M); - ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_8G); + ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x80000000, SZ_2G); + ivpu_hw_range_init(&vdev->hw->ranges.user, 0x100000000, SZ_256G); + vdev->hw->ranges.dma = vdev->hw->ranges.user; } } diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c index 697b57071d546..891967a95bc3c 100644 --- a/drivers/accel/ivpu/ivpu_mmu_context.c +++ b/drivers/accel/ivpu/ivpu_mmu_context.c @@ -571,8 +571,8 @@ void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ct start = vdev->hw->ranges.global.start; end = vdev->hw->ranges.shave.end; } else { - start = vdev->hw->ranges.user.start; - end = vdev->hw->ranges.dma.end; + start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start); + end = max_t(u64, vdev->hw->ranges.user.end, vdev->hw->ranges.dma.end); } drm_mm_init(&ctx->mm, start, end - start); From e91191efe75a94ae10fac4b384962068a8151886 Mon Sep 17 00:00:00 2001 From: Karol Wachowski Date: Thu, 17 Oct 2024 16:58:17 +0200 Subject: [PATCH 71/90] accel/ivpu: Move secondary preemption buffer allocation to DMA range Secondary preemption buffer is accessible by NPU's DMA and can be allocated with addresses above 4 GB. Move secondary preemption buffer allocation from SHAVE range which is much smaller (2GB) to DMA range. This allows to allocate more command queues with corresponding preemption buffers without running out of address range. Signed-off-by: Karol Wachowski Reviewed-by: Jacek Lawrynowicz Reviewed-by: Jeffrey Hugo Signed-off-by: Jacek Lawrynowicz Link: https://patchwork.freedesktop.org/patch/msgid/20241017145817.121590-12-jacek.lawrynowicz@linux.intel.com --- drivers/accel/ivpu/ivpu_job.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 39ba6d3d8b0de..7149312f16e19 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -46,7 +46,7 @@ static int ivpu_preemption_buffers_create(struct ivpu_device *vdev, return -ENOMEM; } - cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.shave, + cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.dma, secondary_size, DRM_IVPU_BO_WC); if (!cmdq->secondary_preempt_buf) { ivpu_err(vdev, "Failed to create secondary preemption buffer\n"); From 2e07c88914fc5289c21820b1aa94f058feb38197 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:35:53 -0400 Subject: [PATCH 72/90] drm: zynqmp_kms: Unplug DRM device before removal Prevent userspace accesses to the DRM device from causing use-after-frees by unplugging the device before we remove it. This causes any further userspace accesses to result in an error without further calls into this driver's internals. Fixes: d76271d22694 ("drm: xlnx: DRM/KMS driver for Xilinx ZynqMP DisplayPort Subsystem") Closes: https://lore.kernel.org/dri-devel/4d8f4c9b-2efb-4774-9a37-2f257f79b2c9@linux.dev/ Signed-off-by: Sean Anderson Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-2-sean.anderson@linux.dev --- drivers/gpu/drm/xlnx/zynqmp_kms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c index 2452c2f09161f..0812dd6bad7cc 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -538,7 +538,7 @@ void zynqmp_dpsub_drm_cleanup(struct zynqmp_dpsub *dpsub) { struct drm_device *drm = &dpsub->drm->dev; - drm_dev_unregister(drm); + drm_dev_unplug(drm); drm_atomic_helper_shutdown(drm); drm_encoder_cleanup(&dpsub->drm->encoder); drm_kms_helper_poll_fini(drm); From a7d5eeaa57d7a0d3d6a1da82b350d9661c8559a1 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:35:54 -0400 Subject: [PATCH 73/90] drm: zynqmp_dp: Add locking Add some locking to prevent the IRQ/workers/bridge API calls from stepping on each other's toes. This lock protects: - Non-atomic registers configuring the link. That is, everything but the IRQ registers (since these are accessed in an atomic fashion), and the DP AUX registers (since these don't affect the link). We also access AUX while holding this lock, so it would be very tricky to support. - Link configuration. This is effectively everything in zynqmp_dp which isn't read-only after probe time. So from next_bridge onward. This lock is designed to protect configuration changes so we don't have to do anything tricky. Configuration should never be in the hot path, so I'm not worried about performance. Signed-off-by: Sean Anderson Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-3-sean.anderson@linux.dev --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 129beac4c0730..abfccd8bb5a78 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -280,6 +280,7 @@ struct zynqmp_dp_config { * @dpsub: Display subsystem * @iomem: device I/O memory for register access * @reset: reset controller + * @lock: Mutex protecting this struct and register access (but not AUX) * @irq: irq * @bridge: DRM bridge for the DP encoder * @next_bridge: The downstream bridge @@ -294,11 +295,16 @@ struct zynqmp_dp_config { * @link_config: common link configuration between IP core and sink device * @mode: current mode between IP core and sink device * @train_set: set of training data + * + * @lock covers the link configuration in this struct and the device's + * registers. It does not cover @aux. It is not strictly required for any of + * the members which are only modified at probe/remove time (e.g. @dev). */ struct zynqmp_dp { struct drm_dp_aux aux; struct drm_bridge bridge; struct work_struct hpd_work; + struct mutex lock; struct drm_bridge *next_bridge; struct device *dev; @@ -1386,8 +1392,10 @@ zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridge, } /* Check with link rate and lane count */ + mutex_lock(&dp->lock); rate = zynqmp_dp_max_rate(dp->link_config.max_rate, dp->link_config.max_lanes, dp->config.bpp); + mutex_unlock(&dp->lock); if (mode->clock > rate) { dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n", mode->name); @@ -1414,6 +1422,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge, pm_runtime_get_sync(dp->dev); + mutex_lock(&dp->lock); zynqmp_dp_disp_enable(dp, old_bridge_state); /* @@ -1474,6 +1483,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge, zynqmp_dp_write(dp, ZYNQMP_DP_SOFTWARE_RESET, ZYNQMP_DP_SOFTWARE_RESET_ALL); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1); + mutex_unlock(&dp->lock); } static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge, @@ -1481,6 +1491,7 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge, { struct zynqmp_dp *dp = bridge_to_dp(bridge); + mutex_lock(&dp->lock); dp->enabled = false; cancel_work(&dp->hpd_work); zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 0); @@ -1491,6 +1502,7 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge, zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0); zynqmp_dp_disp_disable(dp, old_bridge_state); + mutex_unlock(&dp->lock); pm_runtime_put_sync(dp->dev); } @@ -1533,6 +1545,8 @@ static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *brid u32 state, i; int ret; + mutex_lock(&dp->lock); + /* * This is from heuristic. It takes some delay (ex, 100 ~ 500 msec) to * get the HPD signal with some monitors. @@ -1560,11 +1574,13 @@ static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *brid dp->num_lanes); dp->status = connector_status_connected; + mutex_unlock(&dp->lock); return connector_status_connected; } disconnected: dp->status = connector_status_disconnected; + mutex_unlock(&dp->lock); return connector_status_disconnected; } @@ -1725,6 +1741,7 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) dp->dev = &pdev->dev; dp->dpsub = dpsub; dp->status = connector_status_disconnected; + mutex_init(&dp->lock); INIT_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func); @@ -1838,4 +1855,5 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) zynqmp_dp_phy_exit(dp); zynqmp_dp_reset(dp, true); + mutex_destroy(&dp->lock); } From 851daf0ceca10278714a359dabbe97d360a6949b Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:35:55 -0400 Subject: [PATCH 74/90] drm: zynqmp_dp: Don't retrain the link in our IRQ Retraining the link can take a while, and might involve waiting for DPCD reads/writes to complete. In preparation for unthreading the IRQ handler, move this into its own work function. Signed-off-by: Sean Anderson Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-4-sean.anderson@linux.dev --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 45 ++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index abfccd8bb5a78..cec5711c70266 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -289,6 +289,7 @@ struct zynqmp_dp_config { * @phy: PHY handles for DP lanes * @num_lanes: number of enabled phy lanes * @hpd_work: hot plug detection worker + * @hpd_irq_work: hot plug detection IRQ worker * @status: connection status * @enabled: flag to indicate if the device is enabled * @dpcd: DP configuration data from currently connected sink device @@ -304,6 +305,7 @@ struct zynqmp_dp { struct drm_dp_aux aux; struct drm_bridge bridge; struct work_struct hpd_work; + struct work_struct hpd_irq_work; struct mutex lock; struct drm_bridge *next_bridge; @@ -1671,6 +1673,29 @@ static void zynqmp_dp_hpd_work_func(struct work_struct *work) drm_bridge_hpd_notify(&dp->bridge, status); } +static void zynqmp_dp_hpd_irq_work_func(struct work_struct *work) +{ + struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp, + hpd_irq_work); + u8 status[DP_LINK_STATUS_SIZE + 2]; + int err; + + mutex_lock(&dp->lock); + err = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status, + DP_LINK_STATUS_SIZE + 2); + if (err < 0) { + dev_dbg_ratelimited(dp->dev, + "could not read sink status: %d\n", err); + } else { + if (status[4] & DP_LINK_STATUS_UPDATED || + !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) || + !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt)) { + zynqmp_dp_train_loop(dp); + } + } + mutex_unlock(&dp->lock); +} + static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) { struct zynqmp_dp *dp = (struct zynqmp_dp *)data; @@ -1702,23 +1727,9 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) if (status & ZYNQMP_DP_INT_HPD_EVENT) schedule_work(&dp->hpd_work); - if (status & ZYNQMP_DP_INT_HPD_IRQ) { - int ret; - u8 status[DP_LINK_STATUS_SIZE + 2]; - - ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status, - DP_LINK_STATUS_SIZE + 2); - if (ret < 0) - goto handled; - - if (status[4] & DP_LINK_STATUS_UPDATED || - !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) || - !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt)) { - zynqmp_dp_train_loop(dp); - } - } + if (status & ZYNQMP_DP_INT_HPD_IRQ) + schedule_work(&dp->hpd_irq_work); -handled: return IRQ_HANDLED; } @@ -1744,6 +1755,7 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) mutex_init(&dp->lock); INIT_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func); + INIT_WORK(&dp->hpd_irq_work, zynqmp_dp_hpd_irq_work_func); /* Acquire all resources (IOMEM, IRQ and PHYs). */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dp"); @@ -1848,6 +1860,7 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_ALL); disable_irq(dp->irq); + cancel_work_sync(&dp->hpd_irq_work); cancel_work_sync(&dp->hpd_work); zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0); From 948a944ea7a6c64cac73c46d06da0548d8f03b65 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:35:56 -0400 Subject: [PATCH 75/90] drm: zynqmp_dp: Convert to a hard IRQ Now that all of the sleeping work is done outside of the IRQ, we can convert it to a hard IRQ. Shared IRQs may be triggered even after calling disable_irq, so use free_irq instead which removes our callback altogether. Signed-off-by: Sean Anderson Reviewed-by: Tomi Valkeinen Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-5-sean.anderson@linux.dev --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index cec5711c70266..532e103713b38 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1831,9 +1831,8 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) * Now that the hardware is initialized and won't generate spurious * interrupts, request the IRQ. */ - ret = devm_request_threaded_irq(dp->dev, dp->irq, NULL, - zynqmp_dp_irq_handler, IRQF_ONESHOT, - dev_name(dp->dev), dp); + ret = devm_request_irq(dp->dev, dp->irq, zynqmp_dp_irq_handler, + IRQF_SHARED, dev_name(dp->dev), dp); if (ret < 0) goto err_phy_exit; @@ -1858,7 +1857,7 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub) struct zynqmp_dp *dp = dpsub->dp; zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_ALL); - disable_irq(dp->irq); + devm_free_irq(dp->dev, dp->irq, dp); cancel_work_sync(&dp->hpd_irq_work); cancel_work_sync(&dp->hpd_work); From 2425dee876bffc0d34413fccb4d2eae4f251db5c Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:35:57 -0400 Subject: [PATCH 76/90] drm: zynqmp_dp: Use AUX IRQs instead of polling Instead of polling the status register for the AUX status, just enable the IRQs and signal a completion. Signed-off-by: Sean Anderson Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-6-sean.anderson@linux.dev --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 35 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 532e103713b38..babfa3581014e 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -286,6 +286,7 @@ struct zynqmp_dp_config { * @next_bridge: The downstream bridge * @config: IP core configuration from DTS * @aux: aux channel + * @aux_done: Completed when we get an AUX reply or timeout * @phy: PHY handles for DP lanes * @num_lanes: number of enabled phy lanes * @hpd_work: hot plug detection worker @@ -306,6 +307,7 @@ struct zynqmp_dp { struct drm_bridge bridge; struct work_struct hpd_work; struct work_struct hpd_irq_work; + struct completion aux_done; struct mutex lock; struct drm_bridge *next_bridge; @@ -942,12 +944,15 @@ static int zynqmp_dp_aux_cmd_submit(struct zynqmp_dp *dp, u32 cmd, u16 addr, u8 *buf, u8 bytes, u8 *reply) { bool is_read = (cmd & AUX_READ_BIT) ? true : false; + unsigned long time_left; u32 reg, i; reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE); if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REQUEST) return -EBUSY; + reinit_completion(&dp->aux_done); + zynqmp_dp_write(dp, ZYNQMP_DP_AUX_ADDRESS, addr); if (!is_read) for (i = 0; i < bytes; i++) @@ -962,17 +967,14 @@ static int zynqmp_dp_aux_cmd_submit(struct zynqmp_dp *dp, u32 cmd, u16 addr, zynqmp_dp_write(dp, ZYNQMP_DP_AUX_COMMAND, reg); /* Wait for reply to be delivered upto 2ms */ - for (i = 0; ; i++) { - reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE); - if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY) - break; + time_left = wait_for_completion_timeout(&dp->aux_done, + msecs_to_jiffies(2)); + if (!time_left) + return -ETIMEDOUT; - if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT || - i == 2) - return -ETIMEDOUT; - - usleep_range(1000, 1100); - } + reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE); + if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT) + return -ETIMEDOUT; reg = zynqmp_dp_read(dp, ZYNQMP_DP_AUX_REPLY_CODE); if (reply) @@ -1056,6 +1058,9 @@ static int zynqmp_dp_aux_init(struct zynqmp_dp *dp) (w << ZYNQMP_DP_AUX_CLK_DIVIDER_AUX_FILTER_SHIFT) | (rate / (1000 * 1000))); + zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_REPLY_RECEIVED | + ZYNQMP_DP_INT_REPLY_TIMEOUT); + dp->aux.name = "ZynqMP DP AUX"; dp->aux.dev = dp->dev; dp->aux.drm_dev = dp->bridge.dev; @@ -1073,6 +1078,9 @@ static int zynqmp_dp_aux_init(struct zynqmp_dp *dp) static void zynqmp_dp_aux_cleanup(struct zynqmp_dp *dp) { drm_dp_aux_unregister(&dp->aux); + + zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_REPLY_RECEIVED | + ZYNQMP_DP_INT_REPLY_TIMEOUT); } /* ----------------------------------------------------------------------------- @@ -1730,6 +1738,12 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data) if (status & ZYNQMP_DP_INT_HPD_IRQ) schedule_work(&dp->hpd_irq_work); + if (status & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY) + complete(&dp->aux_done); + + if (status & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT) + complete(&dp->aux_done); + return IRQ_HANDLED; } @@ -1753,6 +1767,7 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub) dp->dpsub = dpsub; dp->status = connector_status_disconnected; mutex_init(&dp->lock); + init_completion(&dp->aux_done); INIT_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func); INIT_WORK(&dp->hpd_irq_work, zynqmp_dp_hpd_irq_work_func); From 17f12a46cffd916fe735213c1b1c3dad1de187f3 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:35:58 -0400 Subject: [PATCH 77/90] drm: zynqmp_dp: Split off several helper functions In preparation for supporting compliance testing, split off several helper functions. No functional change intended. Signed-off-by: Sean Anderson Reviewed-by: Laurent Pinchart Reviewed-by: Tomi Valkeinen Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-7-sean.anderson@linux.dev --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 48 ++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index babfa3581014e..3edefa0f17951 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -636,6 +636,7 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp, /** * zynqmp_dp_update_vs_emph - Update the training values * @dp: DisplayPort IP core structure + * @train_set: A set of training values * * Update the training values based on the request from sink. The mapped values * are predefined, and values(vs, pe, pc) are from the device manual. @@ -643,12 +644,12 @@ static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp, * Return: 0 if vs and emph are updated successfully, or the error code returned * by drm_dp_dpcd_write(). */ -static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp) +static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp, u8 *train_set) { unsigned int i; int ret; - ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->train_set, + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->mode.lane_cnt); if (ret < 0) return ret; @@ -656,7 +657,7 @@ static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp) for (i = 0; i < dp->mode.lane_cnt; i++) { u32 reg = ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 + i * 4; union phy_configure_opts opts = { 0 }; - u8 train = dp->train_set[i]; + u8 train = train_set[i]; opts.dp.voltage[0] = (train & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT; @@ -700,7 +701,7 @@ static int zynqmp_dp_link_train_cr(struct zynqmp_dp *dp) * So, This loop should exit before 512 iterations */ for (max_tries = 0; max_tries < 512; max_tries++) { - ret = zynqmp_dp_update_vs_emph(dp); + ret = zynqmp_dp_update_vs_emph(dp, dp->train_set); if (ret) return ret; @@ -765,7 +766,7 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp) return ret; for (tries = 0; tries < DP_MAX_TRAINING_TRIES; tries++) { - ret = zynqmp_dp_update_vs_emph(dp); + ret = zynqmp_dp_update_vs_emph(dp, dp->train_set); if (ret) return ret; @@ -788,28 +789,29 @@ static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp) } /** - * zynqmp_dp_train - Train the link + * zynqmp_dp_setup() - Set up major link parameters * @dp: DisplayPort IP core structure + * @bw_code: The link bandwidth as a multiple of 270 MHz + * @lane_cnt: The number of lanes to use + * @enhanced: Use enhanced framing + * @downspread: Enable spread-spectrum clocking * - * Return: 0 if all trains are done successfully, or corresponding error code. + * Return: 0 on success, or -errno on failure */ -static int zynqmp_dp_train(struct zynqmp_dp *dp) +static int zynqmp_dp_setup(struct zynqmp_dp *dp, u8 bw_code, u8 lane_cnt, + bool enhanced, bool downspread) { u32 reg; - u8 bw_code = dp->mode.bw_code; - u8 lane_cnt = dp->mode.lane_cnt; u8 aux_lane_cnt = lane_cnt; - bool enhanced; int ret; zynqmp_dp_write(dp, ZYNQMP_DP_LANE_COUNT_SET, lane_cnt); - enhanced = drm_dp_enhanced_frame_cap(dp->dpcd); if (enhanced) { zynqmp_dp_write(dp, ZYNQMP_DP_ENHANCED_FRAME_EN, 1); aux_lane_cnt |= DP_LANE_COUNT_ENHANCED_FRAME_EN; } - if (dp->dpcd[3] & 0x1) { + if (downspread) { zynqmp_dp_write(dp, ZYNQMP_DP_DOWNSPREAD_CTL, 1); drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5); @@ -852,8 +854,24 @@ static int zynqmp_dp_train(struct zynqmp_dp *dp) } zynqmp_dp_write(dp, ZYNQMP_DP_PHY_CLOCK_SELECT, reg); - ret = zynqmp_dp_phy_ready(dp); - if (ret < 0) + return zynqmp_dp_phy_ready(dp); +} + +/** + * zynqmp_dp_train - Train the link + * @dp: DisplayPort IP core structure + * + * Return: 0 if all trains are done successfully, or corresponding error code. + */ +static int zynqmp_dp_train(struct zynqmp_dp *dp) +{ + int ret; + + ret = zynqmp_dp_setup(dp, dp->mode.bw_code, dp->mode.lane_cnt, + drm_dp_enhanced_frame_cap(dp->dpcd), + dp->dpcd[DP_MAX_DOWNSPREAD] & + DP_MAX_DOWNSPREAD_0_5); + if (ret) return ret; zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, 1); From 76f2c96d6cea2a84ac8f7c899a4a5ae2bff1ed33 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:35:59 -0400 Subject: [PATCH 78/90] drm: zynqmp_dp: Take dp->lock in zynqmp_dp_hpd_work_func Add a non-locking version of zynqmp_dp_bridge_detect and use it in zynqmp_dp_hpd_work_func so we can take the lock explicitly. This will make it easier to check for hpd_ignore when we add debugfs support. Signed-off-by: Sean Anderson Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-8-sean.anderson@linux.dev --- drivers/gpu/drm/xlnx/zynqmp_dp.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 3edefa0f17951..49b4b24f499f5 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -1566,14 +1566,13 @@ static int zynqmp_dp_bridge_atomic_check(struct drm_bridge *bridge, return 0; } -static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *bridge) +static enum drm_connector_status __zynqmp_dp_bridge_detect(struct zynqmp_dp *dp) { - struct zynqmp_dp *dp = bridge_to_dp(bridge); struct zynqmp_dp_link_config *link_config = &dp->link_config; u32 state, i; int ret; - mutex_lock(&dp->lock); + lockdep_assert_held(&dp->lock); /* * This is from heuristic. It takes some delay (ex, 100 ~ 500 msec) to @@ -1602,16 +1601,26 @@ static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *brid dp->num_lanes); dp->status = connector_status_connected; - mutex_unlock(&dp->lock); return connector_status_connected; } disconnected: dp->status = connector_status_disconnected; - mutex_unlock(&dp->lock); return connector_status_disconnected; } +static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *bridge) +{ + struct zynqmp_dp *dp = bridge_to_dp(bridge); + enum drm_connector_status ret; + + mutex_lock(&dp->lock); + ret = __zynqmp_dp_bridge_detect(dp); + mutex_unlock(&dp->lock); + + return ret; +} + static const struct drm_edid *zynqmp_dp_bridge_edid_read(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -1695,7 +1704,10 @@ static void zynqmp_dp_hpd_work_func(struct work_struct *work) struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp, hpd_work); enum drm_connector_status status; - status = zynqmp_dp_bridge_detect(&dp->bridge); + mutex_lock(&dp->lock); + status = __zynqmp_dp_bridge_detect(dp); + mutex_unlock(&dp->lock); + drm_bridge_hpd_notify(&dp->bridge, status); } From 28edaacb821c69241f6c0be6bbd29f7145f1b44f Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Fri, 9 Aug 2024 15:36:00 -0400 Subject: [PATCH 79/90] drm: zynqmp_dp: Add debugfs interface for compliance testing Add a debugfs interface for exercising the various test modes supported by the DisplayPort controller. This allows performing compliance testing, or performing signal integrity measurements on a failing link. At the moment, we do not support sink-driven link quality testing, although such support would be fairly easy to add. Additionally, add some debugfs files for ignoring AUX errors and HPD events, as this can allow testing with equipment that cannot emulate a DPRX. Signed-off-by: Sean Anderson [Tomi: fixed a few minor formatting issues] Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20240809193600.3360015-9-sean.anderson@linux.dev --- Documentation/gpu/drivers.rst | 1 + Documentation/gpu/zynqmp.rst | 149 +++++++ MAINTAINERS | 1 + drivers/gpu/drm/xlnx/zynqmp_dp.c | 680 ++++++++++++++++++++++++++++++- 4 files changed, 828 insertions(+), 3 deletions(-) create mode 100644 Documentation/gpu/zynqmp.rst diff --git a/Documentation/gpu/drivers.rst b/Documentation/gpu/drivers.rst index b899cbc5c2b48..187201aedbe56 100644 --- a/Documentation/gpu/drivers.rst +++ b/Documentation/gpu/drivers.rst @@ -22,6 +22,7 @@ GPU Driver Documentation afbc komeda-kms panfrost + zynqmp .. only:: subproject and html diff --git a/Documentation/gpu/zynqmp.rst b/Documentation/gpu/zynqmp.rst new file mode 100644 index 0000000000000..f57bfa0ad6ece --- /dev/null +++ b/Documentation/gpu/zynqmp.rst @@ -0,0 +1,149 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +=============================================== +Xilinx ZynqMP Ultrascale+ DisplayPort Subsystem +=============================================== + +This subsystem handles DisplayPort video and audio output on the ZynqMP. It +supports in-memory framebuffers with the DisplayPort DMA controller +(xilinx-dpdma), as well as "live" video and audio from the programmable logic +(PL). This subsystem can perform several transformations, including color space +conversion, alpha blending, and audio mixing, although not all features are +currently supported. + +debugfs +------- + +To support debugging and compliance testing, several test modes can be enabled +though debugfs. The following files in /sys/kernel/debug/dri/X/DP-1/test/ +control the DisplayPort test modes: + +active: + Writing a 1 to this file will activate test mode, and writing a 0 will + deactivate test mode. Writing a 1 or 0 when the test mode is already + active/inactive will re-activate/re-deactivate test mode. When test + mode is inactive, changes made to other files will have no (immediate) + effect, although the settings will be saved for when test mode is + activated. When test mode is active, changes made to other files will + apply immediately. + +custom: + Custom test pattern value + +downspread: + Enable/disable clock downspreading (spread-spectrum clocking) by + writing 1/0 + +enhanced: + Enable/disable enhanced framing + +ignore_aux_errors: + Ignore AUX errors when set to 1. Writes to this file take effect + immediately (regardless of whether test mode is active) and affect all + AUX transfers. + +ignore_hpd: + Ignore hotplug events (such as cable removals or monitor link + retraining requests) when set to 1. Writes to this file take effect + immediately (regardless of whether test mode is active). + +laneX_preemphasis: + Preemphasis from 0 (lowest) to 2 (highest) for lane X + +laneX_swing: + Voltage swing from 0 (lowest) to 3 (highest) for lane X + +lanes: + Number of lanes to use (1, 2, or 4) + +pattern: + Test pattern. May be one of: + + video + Use regular video input + + symbol-error + Symbol error measurement pattern + + prbs7 + Output of the PRBS7 (x^7 + x^6 + 1) polynomial + + 80bit-custom + A custom 80-bit pattern + + cp2520 + HBR2 compliance eye pattern + + tps1 + Link training symbol pattern TPS1 (/D10.2/) + + tps2 + Link training symbol pattern TPS2 + + tps3 + Link training symbol pattern TPS3 (for HBR2) + +rate: + Rate in hertz. One of + + * 5400000000 (HBR2) + * 2700000000 (HBR) + * 1620000000 (RBR) + +You can dump the displayport test settings with the following command:: + + for prop in /sys/kernel/debug/dri/1/DP-1/test/*; do + printf '%-17s ' ${prop##*/} + if [ ${prop##*/} = custom ]; then + hexdump -C $prop | head -1 + else + cat $prop + fi + done + +The output could look something like:: + + active 1 + custom 00000000 00 00 00 00 00 00 00 00 00 00 |..........| + downspread 0 + enhanced 1 + ignore_aux_errors 1 + ignore_hpd 1 + lane0_preemphasis 0 + lane0_swing 3 + lane1_preemphasis 0 + lane1_swing 3 + lanes 2 + pattern prbs7 + rate 1620000000 + +The recommended test procedure is to connect the board to a monitor, +configure test mode, activate test mode, and then disconnect the cable +and connect it to your test equipment of choice. For example, one +sequence of commands could be:: + + echo 1 > /sys/kernel/debug/dri/1/DP-1/test/enhanced + echo tps1 > /sys/kernel/debug/dri/1/DP-1/test/pattern + echo 1620000000 > /sys/kernel/debug/dri/1/DP-1/test/rate + echo 1 > /sys/kernel/debug/dri/1/DP-1/test/ignore_aux_errors + echo 1 > /sys/kernel/debug/dri/1/DP-1/test/ignore_hpd + echo 1 > /sys/kernel/debug/dri/1/DP-1/test/active + +at which point the cable could be disconnected from the monitor. + +Internals +--------- + +.. kernel-doc:: drivers/gpu/drm/xlnx/zynqmp_disp.h + +.. kernel-doc:: drivers/gpu/drm/xlnx/zynqmp_dpsub.h + +.. kernel-doc:: drivers/gpu/drm/xlnx/zynqmp_kms.h + +.. kernel-doc:: drivers/gpu/drm/xlnx/zynqmp_disp.c + +.. kernel-doc:: drivers/gpu/drm/xlnx/zynqmp_dp.c + +.. kernel-doc:: drivers/gpu/drm/xlnx/zynqmp_dpsub.c + +.. kernel-doc:: drivers/gpu/drm/xlnx/zynqmp_kms.c diff --git a/MAINTAINERS b/MAINTAINERS index 8b993d6de0f32..a3c87aab42d97 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7838,6 +7838,7 @@ L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/xlnx/ +F: Documentation/gpu/zynqmp.rst F: drivers/gpu/drm/xlnx/ DRM GPU SCHEDULER diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 49b4b24f499f5..25c5dc61ee88b 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include #include #include @@ -51,6 +53,7 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in msec (default: 4)"); #define ZYNQMP_DP_LANE_COUNT_SET 0x4 #define ZYNQMP_DP_ENHANCED_FRAME_EN 0x8 #define ZYNQMP_DP_TRAINING_PATTERN_SET 0xc +#define ZYNQMP_DP_LINK_QUAL_PATTERN_SET 0x10 #define ZYNQMP_DP_SCRAMBLING_DISABLE 0x14 #define ZYNQMP_DP_DOWNSPREAD_CTL 0x18 #define ZYNQMP_DP_SOFTWARE_RESET 0x1c @@ -64,6 +67,9 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in msec (default: 4)"); ZYNQMP_DP_SOFTWARE_RESET_STREAM3 | \ ZYNQMP_DP_SOFTWARE_RESET_STREAM4 | \ ZYNQMP_DP_SOFTWARE_RESET_AUX) +#define ZYNQMP_DP_COMP_PATTERN_80BIT_1 0x20 +#define ZYNQMP_DP_COMP_PATTERN_80BIT_2 0x24 +#define ZYNQMP_DP_COMP_PATTERN_80BIT_3 0x28 /* Core enable registers */ #define ZYNQMP_DP_TRANSMITTER_ENABLE 0x80 @@ -207,6 +213,7 @@ MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in msec (default: 4)"); #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_2 BIT(2) #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_3 BIT(3) #define ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL 0xf +#define ZYNQMP_DP_TRANSMIT_PRBS7 0x230 #define ZYNQMP_DP_PHY_PRECURSOR_LANE_0 0x23c #define ZYNQMP_DP_PHY_PRECURSOR_LANE_1 0x240 #define ZYNQMP_DP_PHY_PRECURSOR_LANE_2 0x244 @@ -274,6 +281,69 @@ struct zynqmp_dp_config { u8 bpp; }; +/** + * enum test_pattern - Test patterns for test testing + * @TEST_VIDEO: Use regular video input + * @TEST_SYMBOL_ERROR: Symbol error measurement pattern + * @TEST_PRBS7: Output of the PRBS7 (x^7 + x^6 + 1) polynomial + * @TEST_80BIT_CUSTOM: A custom 80-bit pattern + * @TEST_CP2520: HBR2 compliance eye pattern + * @TEST_TPS1: Link training symbol pattern TPS1 (/D10.2/) + * @TEST_TPS2: Link training symbol pattern TPS2 + * @TEST_TPS3: Link training symbol pattern TPS3 (for HBR2) + */ +enum test_pattern { + TEST_VIDEO, + TEST_TPS1, + TEST_TPS2, + TEST_TPS3, + TEST_SYMBOL_ERROR, + TEST_PRBS7, + TEST_80BIT_CUSTOM, + TEST_CP2520, +}; + +static const char *const test_pattern_str[] = { + [TEST_VIDEO] = "video", + [TEST_TPS1] = "tps1", + [TEST_TPS2] = "tps2", + [TEST_TPS3] = "tps3", + [TEST_SYMBOL_ERROR] = "symbol-error", + [TEST_PRBS7] = "prbs7", + [TEST_80BIT_CUSTOM] = "80bit-custom", + [TEST_CP2520] = "cp2520", +}; + +/** + * struct zynqmp_dp_test - Configuration for test mode + * @pattern: The test pattern + * @enhanced: Use enhanced framing + * @downspread: Use SSC + * @active: Whether test mode is active + * @custom: Custom pattern for %TEST_80BIT_CUSTOM + * @train_set: Voltage/preemphasis settings + * @bw_code: Bandwidth code for the link + * @link_cnt: Number of lanes + */ +struct zynqmp_dp_test { + enum test_pattern pattern; + bool enhanced, downspread, active; + u8 custom[10]; + u8 train_set[ZYNQMP_DP_MAX_LANES]; + u8 bw_code; + u8 link_cnt; +}; + +/** + * struct zynqmp_dp_train_set_priv - Private data for train_set debugfs files + * @dp: DisplayPort IP core structure + * @lane: The lane for this file + */ +struct zynqmp_dp_train_set_priv { + struct zynqmp_dp *dp; + int lane; +}; + /** * struct zynqmp_dp - Xilinx DisplayPort core * @dev: device structure @@ -284,23 +354,28 @@ struct zynqmp_dp_config { * @irq: irq * @bridge: DRM bridge for the DP encoder * @next_bridge: The downstream bridge + * @test: Configuration for test mode * @config: IP core configuration from DTS * @aux: aux channel * @aux_done: Completed when we get an AUX reply or timeout + * @ignore_aux_errors: If set, AUX errors are suppressed * @phy: PHY handles for DP lanes * @num_lanes: number of enabled phy lanes * @hpd_work: hot plug detection worker * @hpd_irq_work: hot plug detection IRQ worker + * @ignore_hpd: If set, HPD events and IRQs are ignored * @status: connection status * @enabled: flag to indicate if the device is enabled * @dpcd: DP configuration data from currently connected sink device * @link_config: common link configuration between IP core and sink device * @mode: current mode between IP core and sink device * @train_set: set of training data + * @debugfs_train_set: Debugfs private data for @train_set * * @lock covers the link configuration in this struct and the device's - * registers. It does not cover @aux. It is not strictly required for any of - * the members which are only modified at probe/remove time (e.g. @dev). + * registers. It does not cover @aux or @ignore_aux_errors. It is not strictly + * required for any of the members which are only modified at probe/remove time + * (e.g. @dev). */ struct zynqmp_dp { struct drm_dp_aux aux; @@ -320,9 +395,13 @@ struct zynqmp_dp { enum drm_connector_status status; int irq; bool enabled; + bool ignore_aux_errors; + bool ignore_hpd; + struct zynqmp_dp_train_set_priv debugfs_train_set[ZYNQMP_DP_MAX_LANES]; struct zynqmp_dp_mode mode; struct zynqmp_dp_link_config link_config; + struct zynqmp_dp_test test; struct zynqmp_dp_config config; u8 dpcd[DP_RECEIVER_CAP_SIZE]; u8 train_set[ZYNQMP_DP_MAX_LANES]; @@ -1034,6 +1113,8 @@ zynqmp_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) if (dp->status == connector_status_disconnected) { dev_dbg(dp->dev, "no connected aux device\n"); + if (dp->ignore_aux_errors) + goto fake_response; return -ENODEV; } @@ -1042,7 +1123,13 @@ zynqmp_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) dev_dbg(dp->dev, "failed to do aux transfer (%d)\n", ret); - return ret; + if (!dp->ignore_aux_errors) + return ret; + +fake_response: + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + memset(msg->buffer, 0, msg->size); + return msg->size; } /** @@ -1658,6 +1745,582 @@ zynqmp_dp_bridge_get_input_bus_fmts(struct drm_bridge *bridge, return zynqmp_dp_bridge_default_bus_fmts(num_input_fmts); } +/* ----------------------------------------------------------------------------- + * debugfs + */ + +/** + * zynqmp_dp_set_test_pattern() - Configure the link for a test pattern + * @dp: DisplayPort IP core structure + * @pattern: The test pattern to configure + * @custom: The custom pattern to use if @pattern is %TEST_80BIT_CUSTOM + * + * Return: 0 on success, or negative errno on (DPCD) failure + */ +static int zynqmp_dp_set_test_pattern(struct zynqmp_dp *dp, + enum test_pattern pattern, + u8 *const custom) +{ + bool scramble = false; + u32 train_pattern = 0; + u32 link_pattern = 0; + u8 dpcd_train = 0; + u8 dpcd_link = 0; + int ret; + + switch (pattern) { + case TEST_TPS1: + train_pattern = 1; + break; + case TEST_TPS2: + train_pattern = 2; + break; + case TEST_TPS3: + train_pattern = 3; + break; + case TEST_SYMBOL_ERROR: + scramble = true; + link_pattern = DP_PHY_TEST_PATTERN_ERROR_COUNT; + break; + case TEST_PRBS7: + /* We use a dedicated register to enable PRBS7 */ + dpcd_link = DP_LINK_QUAL_PATTERN_ERROR_RATE; + break; + case TEST_80BIT_CUSTOM: { + const u8 *p = custom; + + link_pattern = DP_LINK_QUAL_PATTERN_80BIT_CUSTOM; + + zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_1, + (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); + zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_2, + (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4]); + zynqmp_dp_write(dp, ZYNQMP_DP_COMP_PATTERN_80BIT_3, + (p[9] << 8) | p[8]); + break; + } + case TEST_CP2520: + link_pattern = DP_LINK_QUAL_PATTERN_CP2520_PAT_1; + break; + default: + WARN_ON_ONCE(1); + fallthrough; + case TEST_VIDEO: + scramble = true; + } + + zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, !scramble); + zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, train_pattern); + zynqmp_dp_write(dp, ZYNQMP_DP_LINK_QUAL_PATTERN_SET, link_pattern); + zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMIT_PRBS7, pattern == TEST_PRBS7); + + dpcd_link = dpcd_link ?: link_pattern; + dpcd_train = train_pattern; + if (!scramble) + dpcd_train |= DP_LINK_SCRAMBLING_DISABLE; + + if (dp->dpcd[DP_DPCD_REV] < 0x12) { + if (pattern == TEST_CP2520) + dev_warn(dp->dev, + "can't set sink link quality pattern to CP2520 for DPCD < r1.2; error counters will be invalid\n"); + else + dpcd_train |= FIELD_PREP(DP_LINK_QUAL_PATTERN_11_MASK, + dpcd_link); + } else { + u8 dpcd_link_lane[ZYNQMP_DP_MAX_LANES]; + + memset(dpcd_link_lane, dpcd_link, ZYNQMP_DP_MAX_LANES); + ret = drm_dp_dpcd_write(&dp->aux, DP_LINK_QUAL_LANE0_SET, + dpcd_link_lane, ZYNQMP_DP_MAX_LANES); + if (ret < 0) + return ret; + } + + ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, dpcd_train); + return ret < 0 ? ret : 0; +} + +static int zynqmp_dp_test_setup(struct zynqmp_dp *dp) +{ + return zynqmp_dp_setup(dp, dp->test.bw_code, dp->test.link_cnt, + dp->test.enhanced, dp->test.downspread); +} + +static ssize_t zynqmp_dp_pattern_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry = file->f_path.dentry; + struct zynqmp_dp *dp = file->private_data; + char buf[16]; + ssize_t ret; + + ret = debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + mutex_lock(&dp->lock); + ret = snprintf(buf, sizeof(buf), "%s\n", + test_pattern_str[dp->test.pattern]); + mutex_unlock(&dp->lock); + + debugfs_file_put(dentry); + return simple_read_from_buffer(user_buf, count, ppos, buf, ret); +} + +static ssize_t zynqmp_dp_pattern_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry = file->f_path.dentry; + struct zynqmp_dp *dp = file->private_data; + char buf[16]; + ssize_t ret; + int pattern; + + ret = debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, + count); + if (ret < 0) + goto out; + buf[ret] = '\0'; + + pattern = sysfs_match_string(test_pattern_str, buf); + if (pattern < 0) { + ret = -EINVAL; + goto out; + } + + mutex_lock(&dp->lock); + dp->test.pattern = pattern; + if (dp->test.active) + ret = zynqmp_dp_set_test_pattern(dp, dp->test.pattern, + dp->test.custom) ?: ret; + mutex_unlock(&dp->lock); + +out: + debugfs_file_put(dentry); + return ret; +} + +static const struct file_operations fops_zynqmp_dp_pattern = { + .read = zynqmp_dp_pattern_read, + .write = zynqmp_dp_pattern_write, + .open = simple_open, + .llseek = noop_llseek, +}; + +static int zynqmp_dp_enhanced_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp = data; + + mutex_lock(&dp->lock); + *val = dp->test.enhanced; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_enhanced_set(void *data, u64 val) +{ + struct zynqmp_dp *dp = data; + int ret = 0; + + mutex_lock(&dp->lock); + dp->test.enhanced = val; + if (dp->test.active) + ret = zynqmp_dp_test_setup(dp); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_enhanced, zynqmp_dp_enhanced_get, + zynqmp_dp_enhanced_set, "%llu\n"); + +static int zynqmp_dp_downspread_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp = data; + + mutex_lock(&dp->lock); + *val = dp->test.downspread; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_downspread_set(void *data, u64 val) +{ + struct zynqmp_dp *dp = data; + int ret = 0; + + mutex_lock(&dp->lock); + dp->test.downspread = val; + if (dp->test.active) + ret = zynqmp_dp_test_setup(dp); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_downspread, zynqmp_dp_downspread_get, + zynqmp_dp_downspread_set, "%llu\n"); + +static int zynqmp_dp_active_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp = data; + + mutex_lock(&dp->lock); + *val = dp->test.active; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_active_set(void *data, u64 val) +{ + struct zynqmp_dp *dp = data; + int ret = 0; + + mutex_lock(&dp->lock); + if (val) { + if (val < 2) { + ret = zynqmp_dp_test_setup(dp); + if (ret) + goto out; + } + + ret = zynqmp_dp_set_test_pattern(dp, dp->test.pattern, + dp->test.custom); + if (ret) + goto out; + + ret = zynqmp_dp_update_vs_emph(dp, dp->test.train_set); + if (ret) + goto out; + + dp->test.active = true; + } else { + int err; + + dp->test.active = false; + err = zynqmp_dp_set_test_pattern(dp, TEST_VIDEO, NULL); + if (err) + dev_warn(dp->dev, "could not clear test pattern: %d\n", + err); + zynqmp_dp_train_loop(dp); + } +out: + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_active, zynqmp_dp_active_get, + zynqmp_dp_active_set, "%llu\n"); + +static ssize_t zynqmp_dp_custom_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry = file->f_path.dentry; + struct zynqmp_dp *dp = file->private_data; + ssize_t ret; + + ret = debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + mutex_lock(&dp->lock); + ret = simple_read_from_buffer(user_buf, count, ppos, &dp->test.custom, + sizeof(dp->test.custom)); + mutex_unlock(&dp->lock); + + debugfs_file_put(dentry); + return ret; +} + +static ssize_t zynqmp_dp_custom_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct dentry *dentry = file->f_path.dentry; + struct zynqmp_dp *dp = file->private_data; + ssize_t ret; + char buf[sizeof(dp->test.custom)]; + + ret = debugfs_file_get(dentry); + if (unlikely(ret)) + return ret; + + ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); + if (ret < 0) + goto out; + + mutex_lock(&dp->lock); + memcpy(dp->test.custom, buf, ret); + if (dp->test.active) + ret = zynqmp_dp_set_test_pattern(dp, dp->test.pattern, + dp->test.custom) ?: ret; + mutex_unlock(&dp->lock); + +out: + debugfs_file_put(dentry); + return ret; +} + +static const struct file_operations fops_zynqmp_dp_custom = { + .read = zynqmp_dp_custom_read, + .write = zynqmp_dp_custom_write, + .open = simple_open, + .llseek = noop_llseek, +}; + +static int zynqmp_dp_swing_get(void *data, u64 *val) +{ + struct zynqmp_dp_train_set_priv *priv = data; + struct zynqmp_dp *dp = priv->dp; + + mutex_lock(&dp->lock); + *val = dp->test.train_set[priv->lane] & DP_TRAIN_VOLTAGE_SWING_MASK; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_swing_set(void *data, u64 val) +{ + struct zynqmp_dp_train_set_priv *priv = data; + struct zynqmp_dp *dp = priv->dp; + u8 *train_set = &dp->test.train_set[priv->lane]; + int ret = 0; + + if (val > 3) + return -EINVAL; + + mutex_lock(&dp->lock); + *train_set &= ~(DP_TRAIN_MAX_SWING_REACHED | + DP_TRAIN_VOLTAGE_SWING_MASK); + *train_set |= val; + if (val == 3) + *train_set |= DP_TRAIN_MAX_SWING_REACHED; + + if (dp->test.active) + ret = zynqmp_dp_update_vs_emph(dp, dp->test.train_set); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_swing, zynqmp_dp_swing_get, + zynqmp_dp_swing_set, "%llu\n"); + +static int zynqmp_dp_preemphasis_get(void *data, u64 *val) +{ + struct zynqmp_dp_train_set_priv *priv = data; + struct zynqmp_dp *dp = priv->dp; + + mutex_lock(&dp->lock); + *val = FIELD_GET(DP_TRAIN_PRE_EMPHASIS_MASK, + dp->test.train_set[priv->lane]); + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_preemphasis_set(void *data, u64 val) +{ + struct zynqmp_dp_train_set_priv *priv = data; + struct zynqmp_dp *dp = priv->dp; + u8 *train_set = &dp->test.train_set[priv->lane]; + int ret = 0; + + if (val > 2) + return -EINVAL; + + mutex_lock(&dp->lock); + *train_set &= ~(DP_TRAIN_MAX_PRE_EMPHASIS_REACHED | + DP_TRAIN_PRE_EMPHASIS_MASK); + *train_set |= val; + if (val == 2) + *train_set |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + if (dp->test.active) + ret = zynqmp_dp_update_vs_emph(dp, dp->test.train_set); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_preemphasis, zynqmp_dp_preemphasis_get, + zynqmp_dp_preemphasis_set, "%llu\n"); + +static int zynqmp_dp_lanes_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp = data; + + mutex_lock(&dp->lock); + *val = dp->test.link_cnt; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_lanes_set(void *data, u64 val) +{ + struct zynqmp_dp *dp = data; + int ret = 0; + + if (val > ZYNQMP_DP_MAX_LANES) + return -EINVAL; + + mutex_lock(&dp->lock); + if (val > dp->num_lanes) { + ret = -EINVAL; + } else { + dp->test.link_cnt = val; + if (dp->test.active) + ret = zynqmp_dp_test_setup(dp); + } + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_lanes, zynqmp_dp_lanes_get, + zynqmp_dp_lanes_set, "%llu\n"); + +static int zynqmp_dp_rate_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp = data; + + mutex_lock(&dp->lock); + *val = drm_dp_bw_code_to_link_rate(dp->test.bw_code) * 10000; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_rate_set(void *data, u64 val) +{ + struct zynqmp_dp *dp = data; + int link_rate; + int ret = 0; + u8 bw_code; + + if (do_div(val, 10000)) + return -EINVAL; + + bw_code = drm_dp_link_rate_to_bw_code(val); + link_rate = drm_dp_bw_code_to_link_rate(bw_code); + if (val != link_rate) + return -EINVAL; + + if (bw_code != DP_LINK_BW_1_62 && bw_code != DP_LINK_BW_2_7 && + bw_code != DP_LINK_BW_5_4) + return -EINVAL; + + mutex_lock(&dp->lock); + dp->test.bw_code = bw_code; + if (dp->test.active) + ret = zynqmp_dp_test_setup(dp); + mutex_unlock(&dp->lock); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_rate, zynqmp_dp_rate_get, + zynqmp_dp_rate_set, "%llu\n"); + +static int zynqmp_dp_ignore_aux_errors_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp = data; + + mutex_lock(&dp->aux.hw_mutex); + *val = dp->ignore_aux_errors; + mutex_unlock(&dp->aux.hw_mutex); + return 0; +} + +static int zynqmp_dp_ignore_aux_errors_set(void *data, u64 val) +{ + struct zynqmp_dp *dp = data; + + if (val != !!val) + return -EINVAL; + + mutex_lock(&dp->aux.hw_mutex); + dp->ignore_aux_errors = val; + mutex_unlock(&dp->aux.hw_mutex); + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_ignore_aux_errors, + zynqmp_dp_ignore_aux_errors_get, + zynqmp_dp_ignore_aux_errors_set, "%llu\n"); + +static int zynqmp_dp_ignore_hpd_get(void *data, u64 *val) +{ + struct zynqmp_dp *dp = data; + + mutex_lock(&dp->lock); + *val = dp->ignore_hpd; + mutex_unlock(&dp->lock); + return 0; +} + +static int zynqmp_dp_ignore_hpd_set(void *data, u64 val) +{ + struct zynqmp_dp *dp = data; + + if (val != !!val) + return -EINVAL; + + mutex_lock(&dp->lock); + dp->ignore_hpd = val; + mutex_lock(&dp->lock); + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_zynqmp_dp_ignore_hpd, zynqmp_dp_ignore_hpd_get, + zynqmp_dp_ignore_hpd_set, "%llu\n"); + +static void zynqmp_dp_bridge_debugfs_init(struct drm_bridge *bridge, + struct dentry *root) +{ + struct zynqmp_dp *dp = bridge_to_dp(bridge); + struct dentry *test; + int i; + + dp->test.bw_code = DP_LINK_BW_5_4; + dp->test.link_cnt = dp->num_lanes; + + test = debugfs_create_dir("test", root); +#define CREATE_FILE(name) \ + debugfs_create_file(#name, 0600, test, dp, &fops_zynqmp_dp_##name) + CREATE_FILE(pattern); + CREATE_FILE(enhanced); + CREATE_FILE(downspread); + CREATE_FILE(active); + CREATE_FILE(custom); + CREATE_FILE(rate); + CREATE_FILE(lanes); + CREATE_FILE(ignore_aux_errors); + CREATE_FILE(ignore_hpd); + + for (i = 0; i < dp->num_lanes; i++) { + static const char fmt[] = "lane%d_preemphasis"; + char name[sizeof(fmt)]; + + dp->debugfs_train_set[i].dp = dp; + dp->debugfs_train_set[i].lane = i; + + snprintf(name, sizeof(name), fmt, i); + debugfs_create_file(name, 0600, test, + &dp->debugfs_train_set[i], + &fops_zynqmp_dp_preemphasis); + + snprintf(name, sizeof(name), "lane%d_swing", i); + debugfs_create_file(name, 0600, test, + &dp->debugfs_train_set[i], + &fops_zynqmp_dp_swing); + } +} + static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = { .attach = zynqmp_dp_bridge_attach, .detach = zynqmp_dp_bridge_detach, @@ -1671,6 +2334,7 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = { .detect = zynqmp_dp_bridge_detect, .edid_read = zynqmp_dp_bridge_edid_read, .atomic_get_input_bus_fmts = zynqmp_dp_bridge_get_input_bus_fmts, + .debugfs_init = zynqmp_dp_bridge_debugfs_init, }; /* ----------------------------------------------------------------------------- @@ -1705,6 +2369,11 @@ static void zynqmp_dp_hpd_work_func(struct work_struct *work) enum drm_connector_status status; mutex_lock(&dp->lock); + if (dp->ignore_hpd) { + mutex_unlock(&dp->lock); + return; + } + status = __zynqmp_dp_bridge_detect(dp); mutex_unlock(&dp->lock); @@ -1719,6 +2388,11 @@ static void zynqmp_dp_hpd_irq_work_func(struct work_struct *work) int err; mutex_lock(&dp->lock); + if (dp->ignore_hpd) { + mutex_unlock(&dp->lock); + return; + } + err = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status, DP_LINK_STATUS_SIZE + 2); if (err < 0) { From 223842c7702b52846b1c5aef8aca7474ec1fd29b Mon Sep 17 00:00:00 2001 From: Steffen Dirkwinkel Date: Mon, 28 Oct 2024 14:39:40 +0100 Subject: [PATCH 80/90] drm: xlnx: zynqmp_disp: layer may be null while releasing layer->info can be null if we have an error on the first layer in zynqmp_disp_create_layers Fixes: 1836fd5ed98d ("drm: xlnx: zynqmp_dpsub: Minimize usage of global flag") Signed-off-by: Steffen Dirkwinkel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20241028133941.54264-1-lists@steffen.cc --- drivers/gpu/drm/xlnx/zynqmp_disp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c index 9368acf56eaf7..e4e0e299e8a7d 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_disp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c @@ -1200,6 +1200,9 @@ static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp, { unsigned int i; + if (!layer->info) + return; + for (i = 0; i < layer->info->num_channels; i++) { struct zynqmp_disp_layer_dma *dma = &layer->dmas[i]; From 71ba1c9b1c717831920c3d432404ee5a707e04b4 Mon Sep 17 00:00:00 2001 From: Steffen Dirkwinkel Date: Mon, 28 Oct 2024 14:42:17 +0100 Subject: [PATCH 81/90] drm: xlnx: zynqmp_dpsub: fix hotplug detection drm_kms_helper_poll_init needs to be called after zynqmp_dpsub_kms_init. zynqmp_dpsub_kms_init creates the connector and without it we don't enable hotplug detection. Fixes: eb2d64bfcc17 ("drm: xlnx: zynqmp_dpsub: Report HPD through the bridge") Cc: stable@vger.kernel.org Signed-off-by: Steffen Dirkwinkel Signed-off-by: Tomi Valkeinen Link: https://patchwork.freedesktop.org/patch/msgid/20241028134218.54727-1-lists@steffen.cc --- drivers/gpu/drm/xlnx/zynqmp_kms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c index 0812dd6bad7cc..fc81983d9e5e8 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_kms.c +++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c @@ -511,12 +511,12 @@ int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub) if (ret) return ret; - drm_kms_helper_poll_init(drm); - ret = zynqmp_dpsub_kms_init(dpsub); if (ret < 0) goto err_poll_fini; + drm_kms_helper_poll_init(drm); + /* Reset all components and register the DRM device. */ drm_mode_config_reset(drm); From d28292aa48d1917db39fac6bcdaf991ba0ed279f Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Tue, 29 Oct 2024 12:24:29 +0100 Subject: [PATCH 82/90] drm/v3d: Drop allocation of object without mountpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function drm_gem_shmem_create_with_mnt() creates an object without using the mountpoint if gemfs is NULL. Drop the else branch calling drm_gem_shmem_create(). Signed-off-by: Matthias Brugger Signed-off-by: Maíra Canal Link: https://patchwork.freedesktop.org/patch/msgid/20241029-v3d-v2-1-c0d3dd328d1b@gmail.com --- drivers/gpu/drm/v3d/v3d_bo.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c index 7055f7c7bcfe5..73ab7dd31b17b 100644 --- a/drivers/gpu/drm/v3d/v3d_bo.c +++ b/drivers/gpu/drm/v3d/v3d_bo.c @@ -157,13 +157,8 @@ struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv, struct v3d_bo *bo; int ret; - /* Let the user opt out of allocating the BOs with THP */ - if (v3d->gemfs) - shmem_obj = drm_gem_shmem_create_with_mnt(dev, unaligned_size, - v3d->gemfs); - else - shmem_obj = drm_gem_shmem_create(dev, unaligned_size); - + shmem_obj = drm_gem_shmem_create_with_mnt(dev, unaligned_size, + v3d->gemfs); if (IS_ERR(shmem_obj)) return ERR_CAST(shmem_obj); bo = to_v3d_bo(&shmem_obj->base); From 904bc5479896d8da7dcd3e162ce224c32c3dc6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Larumbe?= Date: Wed, 9 Oct 2024 22:43:30 +0100 Subject: [PATCH 83/90] Documentation/gpu: Fix Panthor documentation build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix Panthor documentation build errors uncovered by the makedocs target when building with extra warnings enabled. Signed-off-by: Adrián Larumbe Fixes: 6a797bdfde77 ("drm/panthor: add sysfs knob for enabling job profiling") Reported-by: Stephen Rothwell Reviewed-by: Steven Price Signed-off-by: Boris Brezillon Link: https://patchwork.freedesktop.org/patch/msgid/20241009214346.2308917-1-adrian.larumbe@collabora.com --- Documentation/gpu/drivers.rst | 1 + Documentation/gpu/drm-usage-stats.rst | 1 + Documentation/gpu/panthor.rst | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/gpu/drivers.rst b/Documentation/gpu/drivers.rst index 187201aedbe56..1f17ad0790d75 100644 --- a/Documentation/gpu/drivers.rst +++ b/Documentation/gpu/drivers.rst @@ -22,6 +22,7 @@ GPU Driver Documentation afbc komeda-kms panfrost + panthor zynqmp .. only:: subproject and html diff --git a/Documentation/gpu/drm-usage-stats.rst b/Documentation/gpu/drm-usage-stats.rst index 566e122e6e605..cfdd1a05fceda 100644 --- a/Documentation/gpu/drm-usage-stats.rst +++ b/Documentation/gpu/drm-usage-stats.rst @@ -191,4 +191,5 @@ Driver specific implementations * :ref:`i915-usage-stats` * :ref:`panfrost-usage-stats` +* :ref:`panthor-usage-stats` * :ref:`xe-usage-stats` diff --git a/Documentation/gpu/panthor.rst b/Documentation/gpu/panthor.rst index cbf5c4429a2d8..3f8979fa2b86e 100644 --- a/Documentation/gpu/panthor.rst +++ b/Documentation/gpu/panthor.rst @@ -4,7 +4,7 @@ drm/Panthor CSF driver ========================= -.. _panfrost-usage-stats: +.. _panthor-usage-stats: Panthor DRM client usage stats implementation ============================================== From 3c0ecd83eee9b8584588f629e01d209f2c3a632b Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 28 Oct 2024 00:42:02 +0100 Subject: [PATCH 84/90] dt-bindings: display: panel: Move flip properties to panel-common The flip properties were used by "samsung,s6e8aa0.yaml" only so far. By introducing "samsung,s6e88a0-ams427ap24.yaml" they become more common. Signed-off-by: Jakob Hauser Reviewed-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/e4edd8fc07b89006207f5f8962a816ff7f8f4601.1730070570.git.jahau@rocketmail.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/e4edd8fc07b89006207f5f8962a816ff7f8f4601.1730070570.git.jahau@rocketmail.com --- .../bindings/display/panel/panel-common.yaml | 8 ++++++++ .../bindings/display/panel/samsung,s6e8aa0.yaml | 10 ++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/panel-common.yaml b/Documentation/devicetree/bindings/display/panel/panel-common.yaml index 0a57a31f4f3dd..087415753d606 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-common.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-common.yaml @@ -51,6 +51,14 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 90, 180, 270] + flip-horizontal: + description: boolean to flip image horizontally + type: boolean + + flip-vertical: + description: boolean to flip image vertically + type: boolean + # Display Timings panel-timing: description: diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml index 4601fa4606807..19c8cc83db978 100644 --- a/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml +++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e8aa0.yaml @@ -21,6 +21,8 @@ properties: reset-gpios: true display-timings: true + flip-horizontal: true + flip-vertical: true vdd3-supply: description: core voltage supply @@ -46,14 +48,6 @@ properties: panel-height-mm: description: physical panel height [mm] - flip-horizontal: - description: boolean to flip image horizontally - type: boolean - - flip-vertical: - description: boolean to flip image vertically - type: boolean - required: - compatible - reg From 4998d53df6753c3dbb6e960291715027518c61df Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 28 Oct 2024 00:42:03 +0100 Subject: [PATCH 85/90] dt-bindings: display: panel: Add Samsung S6E88A0-AMS427AP24 Add bindings for Samsung AMS427AP24 panel with S6E88A0 controller. Signed-off-by: Jakob Hauser Reviewed-by: Krzysztof Kozlowski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/d60428e7dad57efd42948adfd34b81335dabcd1f.1730070570.git.jahau@rocketmail.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/d60428e7dad57efd42948adfd34b81335dabcd1f.1730070570.git.jahau@rocketmail.com --- .../panel/samsung,s6e88a0-ams427ap24.yaml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/panel/samsung,s6e88a0-ams427ap24.yaml diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e88a0-ams427ap24.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e88a0-ams427ap24.yaml new file mode 100644 index 0000000000000..db284ba5be209 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e88a0-ams427ap24.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/samsung,s6e88a0-ams427ap24.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung AMS427AP24 panel with S6E88A0 controller + +maintainers: + - Jakob Hauser + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: samsung,s6e88a0-ams427ap24 + + reg: + maxItems: 1 + + port: true + reset-gpios: true + flip-horizontal: true + + vdd3-supply: + description: core voltage supply + + vci-supply: + description: voltage supply for analog circuits + +required: + - compatible + - reg + - port + - reset-gpios + - vdd3-supply + - vci-supply + +additionalProperties: false + +examples: + - | + #include + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "samsung,s6e88a0-ams427ap24"; + reg = <0>; + + vdd3-supply = <&pm8916_l17>; + vci-supply = <&pm8916_l6>; + reset-gpios = <&tlmm 25 GPIO_ACTIVE_LOW>; + flip-horizontal; + + port { + panel_in: endpoint { + remote-endpoint = <&mdss_dsi0_out>; + }; + }; + }; + }; From d5658db2a0768a73a862f8b5fe6daae10d3abfec Mon Sep 17 00:00:00 2001 From: Jakob Hauser Date: Mon, 28 Oct 2024 00:42:04 +0100 Subject: [PATCH 86/90] drm/panel: samsung-s6e88a0-ams427ap24: Add initial driver This initial part of the panel driver was mostly generated by the "linux-mdss-dsi-panel-driver-generator" tool [1], reading downstream Android kernel file "dsi_panel_S6E88A0_AMS427AP24_qhd_octa_video.dtsi" [2]. On top of the generic output of the tool, there were a couple of changes applied: - Added mipi_dsi_dcs_set_display_on() to function s6e88a0_ams427ap24_on(), otherwise the display does not show up. - In functions s6e88a0_ams427ap24_on() and s6e88a0_ams427ap24_off() changed DSI commands to multi context and used "accum_err" returns. - In functions s6e88a0_ams427ap24_on() and s6e88a0_ams427ap24_off() replaced msleep() by mipi_dsi_msleep(). - The function s6e88a0_ams427ap24_get_modes() was changed to make use of drm_connector_helper_get_modes_fixed(). This also required to include drm/drm_probe_helper.h. - In function s6e88a0_ams427ap24_probe() registring the regulators was changed to devm_regulator_bulk_get_const(). This required to change supplies in struct s6e88a0_ams427ap24 to a pointer. - Removed bool "prepared" from struct s6e88a0_ams427ap24 and according parts in functions s6e88a0_ams427ap24_prepare() and s6e88a0_ams427ap24_unprepare(). - Removed include , it's not needed. - Added comments to the mipi_dsi_dcs_write_seq_multi() lines in function s6e88a0_ams427ap24_on(). [1] https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator [2] https://github.com/msm8916-mainline/linux-downstream/blob/GT-I9195I/drivers/video/msm/mdss/samsung/S6E88A0_AMS427AP24/dsi_panel_S6E88A0_AMS427AP24_qhd_octa_video.dtsi Signed-off-by: Jakob Hauser Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/d92ef0036b66520bb6d1ec908165e776cf30c303.1730070570.git.jahau@rocketmail.com Signed-off-by: Neil Armstrong Link: https://patchwork.freedesktop.org/patch/msgid/d92ef0036b66520bb6d1ec908165e776cf30c303.1730070570.git.jahau@rocketmail.com --- drivers/gpu/drm/panel/Kconfig | 9 + drivers/gpu/drm/panel/Makefile | 1 + .../panel/panel-samsung-s6e88a0-ams427ap24.c | 251 ++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index bce4cb64184a1..f8adc38447fb1 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -632,6 +632,15 @@ config DRM_PANEL_SAMSUNG_AMS639RQ08 Say Y or M here if you want to enable support for the Samsung AMS639RQ08 FHD Plus (2340x1080@60Hz) CMD mode panel. +config DRM_PANEL_SAMSUNG_S6E88A0_AMS427AP24 + tristate "Samsung AMS427AP24 panel with S6E88A0 controller" + depends on GPIOLIB && OF && REGULATOR + depends on DRM_MIPI_DSI + help + Say Y here if you want to enable support for Samsung AMS427AP24 panel + with S6E88A0 controller (found in Samsung Galaxy S4 Mini Value Edition + GT-I9195I). To compile this driver as a module, choose M here. + config DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 tristate "Samsung AMS452EF01 panel with S6E88A0 DSI video mode controller" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 4db02c2411aef..7dcf72646cacf 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0) += panel-samsung-s6e63m0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_SPI) += panel-samsung-s6e63m0-spi.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o +obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS427AP24) += panel-samsung-s6e88a0-ams427ap24.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c new file mode 100644 index 0000000000000..8e4d012667d79 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Samsung AMS427AP24 panel with S6E88A0 controller + * Copyright (c) 2024 Jakob Hauser + */ + +#include +#include +#include +#include + +#include