Unverified Commit 61191a56 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!15492 Add dp module in hibmc driver

Merge Pull Request from: @ci-robot 
 
PR sync from: Yongbang Shi <shiyongbang@huawei.com>
https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/NMCYEKENEBEAUBNV72ZI6EEPT4TW6NK7/ 
From: Baihan Li <libaihan@huawei.com>

Realizing the basic display function of DP cable for DP connector
displaying. Add DP module in hibmc drm driver, which is for Hisilicon
Hibmc SoC which used for Out-of-band management. Blow is the general
hardware connection, both the Hibmc and the host CPU are on the same
mother board.

+----------+       +----------+      +----- ----+      +----------------+
+----------+       +----------+      +----------+      +----------------+

Baihan Li (5):
  drm/hisilicon/hibmc: add dp aux in hibmc drivers
  drm/hisilicon/hibmc: add dp link moduel in hibmc drivers
  drm/hisilicon/hibmc: add dp hw moduel in hibmc driver
  drm/hisilicon/hibmc: refactored struct hibmc_drm_private
  drm/hisilicon/hibmc: add dp module in hibmc

Jani Nikula (1):
  drm/hisilicon/hibmc: convert to struct drm_edid

 
https://gitee.com/openeuler/kernel/issues/IBSNIA 
 
Link:https://gitee.com/openeuler/kernel/pulls/15492

 

Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents d819d720 8d6349a4
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
# 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 hibmc_drm_dp.o

obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
+164 −0
Original line number Diff line number Diff line
// 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"

#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_dev *dp = container_of(aux, struct hibmc_dp_dev, aux);
	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_dev *dp)
{
	hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1);
	hibmc_dp_reg_write_field(dp, 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.is_remote = 0;
	drm_dp_aux_init(&dp->aux);
}
+63 −0
Original line number Diff line number Diff line
/* 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/display/drm_dp_helper.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];
};

#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 {								\
		typeof(dp) _dp = dp;					\
		typeof(_dp->base) addr = (_dp->base + (offset));	\
		mutex_lock(&_dp->lock);					\
		u32 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_dev *dp);
int hibmc_dp_link_training(struct hibmc_dp_dev *dp);

#endif
+19 −0
Original line number Diff line number Diff line
/* 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

#endif
+220 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2024 Hisilicon Limited.

#include <linux/io.h>
#include <linux/delay.h>
#include "dp_config.h"
#include "dp_comm.h"
#include "dp_reg.h"
#include "dp_hw.h"

static void hibmc_dp_set_tu(struct hibmc_dp_dev *dp, struct drm_display_mode *mode)
{
	u32 tu_symbol_frac_size;
	u32 tu_symbol_size;
	u32 rate_ks;
	u8 lane_num;
	u32 value;
	u32 bpp;

	lane_num = dp->link.cap.lanes;
	if (lane_num == 0) {
		drm_err(dp->dev, "set tu failed, lane num cannot be 0!\n");
		return;
	}

	bpp = HIBMC_DP_BPP;
	rate_ks = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL;
	value = (mode->clock * bpp * 5) / (61 * lane_num * rate_ks);

	if (value % 10 == 9) { /* 9 carry */
		tu_symbol_size = value / 10 + 1;
		tu_symbol_frac_size = 0;
	} else {
		tu_symbol_size = value / 10;
		tu_symbol_frac_size = value % 10 + 1;
	}

	drm_dbg_dp(dp->dev, "tu value: %u.%u value: %u\n",
		   tu_symbol_size, tu_symbol_frac_size, value);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET,
				 HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE, tu_symbol_size);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET,
				 HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE, tu_symbol_frac_size);
}

static void hibmc_dp_set_sst(struct hibmc_dp_dev *dp, struct drm_display_mode *mode)
{
	u32 hblank_size;
	u32 htotal_size;
	u32 htotal_int;
	u32 hblank_int;
	u32 fclk; /* flink_clock */

	fclk = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL;

	/* Considering the effect of spread spectrum, the value may be deviated.
	 * The coefficient (0.9947) is used to offset the deviation.
	 */
	htotal_int = mode->htotal * 9947 / 10000;
	htotal_size = htotal_int * fclk / (HIBMC_DP_SYMBOL_PER_FCLK * (mode->clock / 1000));

	hblank_int = mode->htotal - mode->hdisplay - mode->hdisplay * 53 / 10000;
	hblank_size = hblank_int * fclk * 9947 /
		      (mode->clock * 10 * HIBMC_DP_SYMBOL_PER_FCLK);

	drm_dbg_dp(dp->dev, "h_active %u v_active %u htotal_size %u hblank_size %u",
		   mode->hdisplay, mode->vdisplay, htotal_size, hblank_size);
	drm_dbg_dp(dp->dev, "flink_clock %u pixel_clock %d", fclk, mode->clock / 1000);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE,
				 HIBMC_DP_CFG_STREAM_HTOTAL_SIZE, htotal_size);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE,
				 HIBMC_DP_CFG_STREAM_HBLANK_SIZE, hblank_size);
}

static void hibmc_dp_link_cfg(struct hibmc_dp_dev *dp, struct drm_display_mode *mode)
{
	u32 timing_delay;
	u32 vblank;
	u32 hstart;
	u32 vstart;

	vblank = mode->vtotal - mode->vdisplay;
	timing_delay = mode->htotal - mode->hsync_start;
	hstart = mode->htotal - mode->hsync_start;
	vstart = mode->vtotal - mode->vsync_start;

	hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0,
				 HIBMC_DP_CFG_TIMING_GEN0_HBLANK, mode->htotal - mode->hdisplay);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0,
				 HIBMC_DP_CFG_TIMING_GEN0_HACTIVE, mode->hdisplay);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2,
				 HIBMC_DP_CFG_TIMING_GEN0_VBLANK, vblank);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2,
				 HIBMC_DP_CFG_TIMING_GEN0_VACTIVE, mode->vdisplay);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG3,
				 HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH,
				 mode->vsync_start - mode->vdisplay);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0,
				 HIBMC_DP_CFG_STREAM_HACTIVE, mode->hdisplay);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0,
				 HIBMC_DP_CFG_STREAM_HBLANK, mode->htotal - mode->hdisplay);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG2,
				 HIBMC_DP_CFG_STREAM_HSYNC_WIDTH,
				 mode->hsync_end - mode->hsync_start);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1,
				 HIBMC_DP_CFG_STREAM_VACTIVE, mode->vdisplay);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1,
				 HIBMC_DP_CFG_STREAM_VBLANK, vblank);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3,
				 HIBMC_DP_CFG_STREAM_VFRONT_PORCH,
				 mode->vsync_start - mode->vdisplay);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3,
				 HIBMC_DP_CFG_STREAM_VSYNC_WIDTH,
				 mode->vsync_end - mode->vsync_start);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0,
				 HIBMC_DP_CFG_STREAM_VSTART, vstart);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0,
				 HIBMC_DP_CFG_STREAM_HSTART, hstart);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VSYNC_POLARITY,
				 mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_HSYNC_POLARITY,
				 mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0);

	/* MSA mic 0 and 1 */
	writel(HIBMC_DP_MSA1, dp->base + HIBMC_DP_VIDEO_MSA1);
	writel(HIBMC_DP_MSA2, dp->base + HIBMC_DP_VIDEO_MSA2);

	hibmc_dp_set_tu(dp, mode);

	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_RGB_ENABLE, 0x1);
	hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VIDEO_MAPPING, 0);

	/* divide 2: up even */
	if (timing_delay % 2)
		timing_delay++;

	hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_MODEL_CTRL,
				 HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1, timing_delay);

	hibmc_dp_set_sst(dp, mode);
}

int hibmc_dp_hw_init(struct hibmc_dp *dp)
{
	struct drm_device *drm_dev = dp->drm_dev;
	struct hibmc_dp_dev *dp_dev;

	dp_dev = devm_kzalloc(drm_dev->dev, sizeof(struct hibmc_dp_dev), GFP_KERNEL);
	if (!dp_dev)
		return -ENOMEM;

	mutex_init(&dp_dev->lock);

	dp->dp_dev = dp_dev;

	dp_dev->dev = drm_dev;
	dp_dev->base = dp->mmio + HIBMC_DP_OFFSET;

	hibmc_dp_aux_init(dp_dev);

	dp_dev->link.cap.lanes = 0x2;
	dp_dev->link.cap.link_rate = DP_LINK_BW_2_7;

	/* hdcp data */
	writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG);
	/* int init */
	writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE);
	writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS);
	/* rst */
	writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL);
	/* clock enable */
	writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL);

	return 0;
}

void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable)
{
	struct hibmc_dp_dev *dp_dev = dp->dp_dev;

	if (enable) {
		hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0x1);
		writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
		hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0x1);
		writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
	} else {
		hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0);
		writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
		hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0);
		writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL);
	}

	msleep(50);
}

int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode)
{
	struct hibmc_dp_dev *dp_dev = dp->dp_dev;
	int ret;

	if (!dp_dev->link.status.channel_equalized) {
		ret = hibmc_dp_link_training(dp_dev);
		if (ret) {
			drm_err(dp->drm_dev, "dp link training failed, ret: %d\n", ret);
			return ret;
		}
	}

	hibmc_dp_display_en(dp, false);
	hibmc_dp_link_cfg(dp_dev, mode);

	return 0;
}
Loading