Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b701d0a
drm: Plumb debugfs_init through to panels
dianders Feb 5, 2022
8f96aed
drm/ast: Implement polling for VGA and SIL164 connectors
tdz Mar 25, 2024
5cdb417
drm/hisilicon/hibmc: add dp aux in hibmc drivers
mark1188-ui Mar 14, 2025
637ce86
drm/hisilicon/hibmc: add dp link moduel in hibmc drivers
mark1188-ui Mar 14, 2025
dca6fcc
drm/hisilicon/hibmc: add dp hw moduel in hibmc driver
mark1188-ui Mar 14, 2025
df99ccc
drm/hisilicon/hibmc: refactored struct hibmc_drm_private
mark1188-ui Mar 14, 2025
52bd7f8
drm/hisilicon/hibmc: add dp module in hibmc
mark1188-ui Mar 14, 2025
ccf36ae
drm/hisilicon/hibmc: Replace module initialization with DRM helpers
tdz Dec 22, 2021
44f4120
drm/hisilicon/hibmc: Include <linux/io.h> for readl() and writel()
tdz Nov 7, 2022
a9e43f2
drm/hisilicon/hibmc: Fix preferred depth and bpp
tdz Nov 23, 2022
7131034
drm/hisilicon/hibmc: Restructuring the header dp_reg.h
mark1188-ui Apr 16, 2025
453269b
drm/hisilicon/hibmc: Add dp serdes cfg to adjust serdes rate, voltage…
mark1188-ui Apr 16, 2025
34b97a3
drm/hisilicon/hibmc: Add dp serdes cfg in dp process
mark1188-ui Apr 16, 2025
b23f5b0
drm/hisilicon/hibmc: Refactor the member of drm_aux in struct hibmc_dp
mark1188-ui Apr 16, 2025
b5ce630
drm/hisilicon/hibmc: Getting connector info and EDID by using AUX cha…
mark1188-ui Apr 16, 2025
e1465b6
drm/hisilicon/hibmc: Add colorbar-cfg feature and its debugfs file
mark1188-ui Mar 31, 2025
bc4de53
drm/hisilicon/hibmc: Enable this hot plug detect of irq feature
mark1188-ui Mar 31, 2025
01dc540
drm/hisilicon/hibmc: Add MSI irq getting and requesting for HPD
mark1188-ui Apr 16, 2025
06cffd2
drm/hisilicon/hibmc: Add vga connector detect functions
mark1188-ui Apr 16, 2025
15b540b
drm/hisilicon/hibmc: fix the i2c device resource leak when vdac init …
mark1188-ui Aug 13, 2025
347761b
drm/hisilicon/hibmc: fix irq_request()'s irq name variable is local
mark1188-ui Aug 13, 2025
b34aa3e
drm/hisilicon/hibmc: fix the hibmc loaded failed bug
mark1188-ui Aug 13, 2025
0d9d9c7
drm/hisilicon/hibmc: fix rare monitors cannot display problem
mark1188-ui Aug 13, 2025
45c04a9
drm/hisilicon/hibmc: fix dp and vga cannot show together
mark1188-ui Aug 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/gpu/drm/ast/ast_mode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ static int ast_connector_init(struct drm_device *dev)
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;

connector->polled = DRM_CONNECTOR_POLL_CONNECT;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;

drm_connector_attach_encoder(connector, encoder);

Expand Down
12 changes: 12 additions & 0 deletions drivers/gpu/drm/bridge/panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ static int panel_bridge_get_modes(struct drm_bridge *bridge,
return drm_panel_get_modes(panel_bridge->panel, connector);
}

static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
struct dentry *root)
{
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_panel *panel = panel_bridge->panel;

root = debugfs_create_dir("panel", root);
if (panel->funcs->debugfs_init)
panel->funcs->debugfs_init(panel, root);
}

static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
Expand All @@ -150,6 +161,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
.debugfs_init = panel_bridge_debugfs_init,
};

/**
Expand Down
15 changes: 15 additions & 0 deletions drivers/gpu/drm/drm_bridge_connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,28 @@ static void drm_bridge_connector_destroy(struct drm_connector *connector)
kfree(bridge_connector);
}

static void drm_bridge_connector_debugfs_init(struct drm_connector *connector,
struct dentry *root)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
struct drm_encoder *encoder = bridge_connector->encoder;
struct drm_bridge *bridge;

list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) {
if (bridge->funcs->debugfs_init)
bridge->funcs->debugfs_init(bridge, root);
}
}

static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.detect = drm_bridge_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_bridge_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.debugfs_init = drm_bridge_connector_debugfs_init,
};

/* -----------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/drm_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ void drm_debugfs_connector_add(struct drm_connector *connector)
/* vrr range */
debugfs_create_file("vrr_range", S_IRUGO, root, connector,
&vrr_range_fops);

if (connector->funcs->debugfs_init)
connector->funcs->debugfs_init(connector, root);
}

void drm_debugfs_connector_remove(struct drm_connector *connector)
Expand Down
29 changes: 29 additions & 0 deletions drivers/gpu/drm/drm_probe_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -876,3 +876,32 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
return changed;
}
EXPORT_SYMBOL(drm_helper_hpd_irq_event);

/**
* drm_connector_helper_detect_from_ddc - Read EDID and detect connector status.
* @connector: The connector
* @ctx: Acquire context
* @force: Perform screen-destructive operations, if necessary
*
* Detects the connector status by reading the EDID using drm_probe_ddc(),
* which requires connector->ddc to be set. Returns connector_status_connected
* on success or connector_status_disconnected on failure.
*
* Returns:
* The connector status as defined by enum drm_connector_status.
*/
int drm_connector_helper_detect_from_ddc(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx,
bool force)
{
struct i2c_adapter *ddc = connector->ddc;

if (!ddc)
return connector_status_unknown;

if (drm_probe_ddc(ddc))
return connector_status_connected;

return connector_status_disconnected;
}
EXPORT_SYMBOL(drm_connector_helper_detect_from_ddc);
4 changes: 3 additions & 1 deletion drivers/gpu/drm/hisilicon/hibmc/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o
hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \
dp/dp_aux.o dp/dp_link.o dp/dp_hw.o dp/dp_serdes.o hibmc_drm_dp.o \
hibmc_drm_debugfs.o

obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
168 changes: 168 additions & 0 deletions drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2024 Hisilicon Limited.

#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/minmax.h>
#include <drm/drm_device.h>
#include <drm/drm_print.h>
#include "dp_comm.h"
#include "dp_reg.h"
#include "dp_hw.h"

#define HIBMC_AUX_CMD_REQ_LEN GENMASK(7, 4)
#define HIBMC_AUX_CMD_ADDR GENMASK(27, 8)
#define HIBMC_AUX_CMD_I2C_ADDR_ONLY BIT(28)
#define HIBMC_BYTES_IN_U32 4
#define HIBMC_AUX_I2C_WRITE_SUCCESS 0x1
#define HIBMC_DP_MIN_PULSE_NUM 0x9
#define BITS_IN_U8 8

static inline void hibmc_dp_aux_reset(struct hibmc_dp_dev *dp)
{
hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x0);
usleep_range(10, 15);
hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x1);
}

static void hibmc_dp_aux_read_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size)
{
u32 reg_num;
u32 value;
u32 num;
u8 i, j;

reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32);
for (i = 0; i < reg_num; i++) {
/* number of bytes read from a single register */
num = min(size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32);
value = readl(dp->base + HIBMC_DP_AUX_RD_DATA0 + i * HIBMC_BYTES_IN_U32);
/* convert the 32-bit value of the register to the buffer. */
for (j = 0; j < num; j++)
buf[i * HIBMC_BYTES_IN_U32 + j] = value >> (j * BITS_IN_U8);
}
}

static void hibmc_dp_aux_write_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size)
{
u32 reg_num;
u32 value;
u32 num;
u8 i, j;

reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32);
for (i = 0; i < reg_num; i++) {
/* number of bytes written to a single register */
num = min_t(u8, size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32);
value = 0;
/* obtain the 32-bit value written to a single register. */
for (j = 0; j < num; j++)
value |= buf[i * HIBMC_BYTES_IN_U32 + j] << (j * BITS_IN_U8);
/* writing data to a single register */
writel(value, dp->base + HIBMC_DP_AUX_WR_DATA0 + i * HIBMC_BYTES_IN_U32);
}
}

static u32 hibmc_dp_aux_build_cmd(const struct drm_dp_aux_msg *msg)
{
u32 aux_cmd = msg->request;

if (msg->size)
aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_REQ_LEN, (msg->size - 1));
else
aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_I2C_ADDR_ONLY, 1);

aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_ADDR, msg->address);

return aux_cmd;
}

/* ret >= 0, ret is size; ret < 0, ret is err code */
static int hibmc_dp_aux_parse_xfer(struct hibmc_dp_dev *dp, struct drm_dp_aux_msg *msg)
{
u32 buf_data_cnt;
u32 aux_status;

aux_status = readl(dp->base + HIBMC_DP_AUX_STATUS);
msg->reply = FIELD_GET(HIBMC_DP_CFG_AUX_STATUS, aux_status);

if (aux_status & HIBMC_DP_CFG_AUX_TIMEOUT)
return -ETIMEDOUT;

/* only address */
if (!msg->size)
return 0;

if (msg->reply != DP_AUX_NATIVE_REPLY_ACK)
return -EIO;

buf_data_cnt = FIELD_GET(HIBMC_DP_CFG_AUX_READY_DATA_BYTE, aux_status);

switch (msg->request) {
case DP_AUX_NATIVE_WRITE:
return msg->size;
case DP_AUX_I2C_WRITE | DP_AUX_I2C_MOT:
if (buf_data_cnt == HIBMC_AUX_I2C_WRITE_SUCCESS)
return msg->size;
else
return FIELD_GET(HIBMC_DP_CFG_AUX, aux_status);
case DP_AUX_NATIVE_READ:
case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
buf_data_cnt--;
if (buf_data_cnt != msg->size) {
/* only the successful part of data is read */
return -EBUSY;
}

/* all data is successfully read */
hibmc_dp_aux_read_data(dp, msg->buffer, msg->size);
return msg->size;
default:
return -EINVAL;
}
}

/* ret >= 0 ,ret is size; ret < 0, ret is err code */
static ssize_t hibmc_dp_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
{
struct hibmc_dp *dp_priv = container_of(aux, struct hibmc_dp, aux);
struct hibmc_dp_dev *dp = dp_priv->dp_dev;
u32 aux_cmd;
int ret;
u32 val; /* val will be assigned at the beginning of readl_poll_timeout function */

writel(0, dp->base + HIBMC_DP_AUX_WR_DATA0);
writel(0, dp->base + HIBMC_DP_AUX_WR_DATA1);
writel(0, dp->base + HIBMC_DP_AUX_WR_DATA2);
writel(0, dp->base + HIBMC_DP_AUX_WR_DATA3);

hibmc_dp_aux_write_data(dp, msg->buffer, msg->size);

aux_cmd = hibmc_dp_aux_build_cmd(msg);
writel(aux_cmd, dp->base + HIBMC_DP_AUX_CMD_ADDR);

/* enable aux transfer */
hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_REQ, 0x1);
ret = readl_poll_timeout(dp->base + HIBMC_DP_AUX_REQ, val,
!(val & HIBMC_DP_CFG_AUX_REQ), 50, 5000);
if (ret) {
hibmc_dp_aux_reset(dp);
return ret;
}

return hibmc_dp_aux_parse_xfer(dp, msg);
}

void hibmc_dp_aux_init(struct hibmc_dp *dp)
{
hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0);
hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1);
hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM,
HIBMC_DP_MIN_PULSE_NUM);

dp->aux.transfer = hibmc_dp_aux_xfer;
dp->aux.name = "HIBMC DRM dp aux";
dp->aux.drm_dev = dp->drm_dev;
drm_dp_aux_init(&dp->aux);
dp->dp_dev->aux = &dp->aux;
}
70 changes: 70 additions & 0 deletions drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (c) 2024 Hisilicon Limited. */

#ifndef DP_COMM_H
#define DP_COMM_H

#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/kernel.h>
#include <linux/bitfield.h>
#include <linux/io.h>
#include <drm/drm_dp_helper.h>

#include "dp_hw.h"

#define HIBMC_DP_LANE_NUM_MAX 2

struct hibmc_link_status {
bool clock_recovered;
bool channel_equalized;
};

struct hibmc_link_cap {
u8 link_rate;
u8 lanes;
};

struct hibmc_dp_link {
struct hibmc_link_status status;
u8 train_set[HIBMC_DP_LANE_NUM_MAX];
struct hibmc_link_cap cap;
};

struct hibmc_dp_dev {
struct drm_dp_aux *aux;
struct drm_device *dev;
void __iomem *base;
struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */
struct hibmc_dp_link link;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
void __iomem *serdes_base;
};

#define dp_field_modify(reg_value, mask, val) \
do { \
(reg_value) &= ~(mask); \
(reg_value) |= FIELD_PREP(mask, val); \
} while (0) \

#define hibmc_dp_reg_write_field(dp, offset, mask, val) \
do { \
u32 reg_value; \
typeof(dp) _dp = dp; \
typeof(_dp->base) addr = (_dp->base + (offset)); \
mutex_lock(&_dp->lock); \
reg_value = readl(addr); \
dp_field_modify(reg_value, mask, val); \
writel(reg_value, addr); \
mutex_unlock(&_dp->lock); \
} while (0)

void hibmc_dp_aux_init(struct hibmc_dp *dp);
int hibmc_dp_link_training(struct hibmc_dp_dev *dp);
int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp);
int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp);
int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]);

#endif
21 changes: 21 additions & 0 deletions drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (c) 2024 Hisilicon Limited. */

#ifndef DP_CONFIG_H
#define DP_CONFIG_H

#define HIBMC_DP_BPP 24
#define HIBMC_DP_SYMBOL_PER_FCLK 4
#define HIBMC_DP_MSA1 0x20
#define HIBMC_DP_MSA2 0x845c00
#define HIBMC_DP_OFFSET 0x1e0000
#define HIBMC_DP_HDCP 0x2
#define HIBMC_DP_INT_RST 0xffff
#define HIBMC_DP_DPTX_RST 0x3ff
#define HIBMC_DP_CLK_EN 0x7
#define HIBMC_DP_SYNC_EN_MASK 0x3
#define HIBMC_DP_LINK_RATE_CAL 27
#define HIBMC_DP_SYNC_DELAY(lanes) ((lanes) == 0x2 ? 86 : 46)
#define HIBMC_DP_INT_ENABLE 0xc

#endif
Loading