Skip to content

Commit

Permalink
drm/hisilicon/hibmc: add dp aux in hibmc drivers
Browse files Browse the repository at this point in the history
Add dp aux read/write functions. They are basic functions
and will be used later.

Signed-off-by: Baihan Li <libaihan@huawei.com>
Signed-off-by: Yongbang Shi <shiyongbang@huawei.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Tian Tao <tiantao6@hisilicon.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250103093824.1963816-2-shiyongbang@huawei.com
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
  • Loading branch information
Baihan Li authored and Dmitry Baryshkov committed Jan 3, 2025
1 parent f8a2397 commit 057e779
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 1 deletion.
3 changes: 2 additions & 1 deletion drivers/gpu/drm/hisilicon/hibmc/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# 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

obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o
164 changes: 164 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,164 @@
// 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);
}
42 changes: 42 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,42 @@
/* 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>

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() */
};

#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);

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

#ifndef DP_REG_H
#define DP_REG_H

#define HIBMC_DP_AUX_CMD_ADDR 0x50
#define HIBMC_DP_AUX_WR_DATA0 0x54
#define HIBMC_DP_AUX_WR_DATA1 0x58
#define HIBMC_DP_AUX_WR_DATA2 0x5c
#define HIBMC_DP_AUX_WR_DATA3 0x60
#define HIBMC_DP_AUX_RD_DATA0 0x64
#define HIBMC_DP_AUX_REQ 0x74
#define HIBMC_DP_AUX_STATUS 0x78
#define HIBMC_DP_DPTX_RST_CTRL 0x700

#define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1)
#define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2)
#define HIBMC_DP_CFG_AUX_MIN_PULSE_NUM GENMASK(13, 9)
#define HIBMC_DP_CFG_AUX_REQ BIT(0)
#define HIBMC_DP_CFG_AUX_RST_N BIT(4)
#define HIBMC_DP_CFG_AUX_TIMEOUT BIT(0)
#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12)
#define HIBMC_DP_CFG_AUX GENMASK(24, 17)
#define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4)

#endif

0 comments on commit 057e779

Please sign in to comment.