2024-05-02 10:42:28

by Shawn Sung (宋孝謙)

[permalink] [raw]
Subject: [PATCH v7 16/18] drm/mediatek: Support CRC in display driver

From: Hsiao Chien Sung <[email protected]>

Register CRC related function pointers to support
CRC retrieval.

Signed-off-by: Hsiao Chien Sung <[email protected]>
---
drivers/gpu/drm/mediatek/mtk_crtc.c | 260 ++++++++++++++++++++++++
drivers/gpu/drm/mediatek/mtk_crtc.h | 38 ++++
drivers/gpu/drm/mediatek/mtk_ddp_comp.h | 3 +
3 files changed, 301 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index 6f34f573e127e..29d00d11224b0 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -18,6 +18,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_work.h>

#include "mtk_crtc.h"
#include "mtk_ddp_comp.h"
@@ -69,6 +70,9 @@ struct mtk_crtc {
/* lock for display hardware access */
struct mutex hw_lock;
bool config_updating;
+
+ struct mtk_ddp_comp *crc_provider;
+ struct drm_vblank_work crc_work;
};

struct mtk_crtc_state {
@@ -703,6 +707,71 @@ static void mtk_crtc_update_output(struct drm_crtc *crtc,
}
}

+static void mtk_crtc_crc_work(struct kthread_work *base)
+{
+ struct drm_vblank_work *work = to_drm_vblank_work(base);
+ struct mtk_crtc *mtk_crtc =
+ container_of(work, typeof(*mtk_crtc), crc_work);
+
+ if (mtk_crtc->base.crc.opened) {
+ struct mtk_ddp_comp *comp = mtk_crtc->crc_provider;
+ u64 vblank = drm_crtc_vblank_count(&mtk_crtc->base);
+
+ comp->funcs->crc_read(comp->dev);
+
+ /* could take more than 50ms to finish */
+ drm_crtc_add_crc_entry(&mtk_crtc->base, true, vblank,
+ comp->funcs->crc_entry(comp->dev));
+
+ drm_vblank_work_schedule(&mtk_crtc->crc_work, vblank + 1, true);
+ }
+}
+
+static int mtk_crtc_set_crc_source(struct drm_crtc *crtc, const char *src)
+{
+ struct mtk_crtc *mtk_crtc = to_mtk_crtc(crtc);
+
+ if (!src)
+ return -EINVAL;
+
+ if (strcmp(src, "auto") != 0) {
+ DRM_ERROR("%s(crtc-%d): unknown source '%s'\n",
+ __func__, drm_crtc_index(crtc), src);
+ return -EINVAL;
+ }
+
+ /*
+ * skip the first crc because the first frame (vblank + 1) is configured
+ * by mtk_crtc_ddp_hw_init() when atomic enable
+ */
+ drm_vblank_work_schedule(&mtk_crtc->crc_work,
+ drm_crtc_vblank_count(crtc) + 2, false);
+ return 0;
+}
+
+static int mtk_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src,
+ size_t *cnt)
+{
+ struct mtk_crtc *mtk_crtc = to_mtk_crtc(crtc);
+ struct mtk_ddp_comp *comp = mtk_crtc->crc_provider;
+
+ if (!comp) {
+ DRM_ERROR("%s(crtc-%d): no crc provider\n",
+ __func__, drm_crtc_index(crtc));
+ return -ENOENT;
+ }
+
+ if (src && strcmp(src, "auto") != 0) {
+ DRM_ERROR("%s(crtc-%d): unknown source '%s'\n",
+ __func__, drm_crtc_index(crtc), src);
+ return -EINVAL;
+ }
+
+ *cnt = comp->funcs->crc_cnt(comp->dev);
+
+ return 0;
+}
+
int mtk_crtc_plane_check(struct drm_crtc *crtc, struct drm_plane *plane,
struct mtk_plane_state *state)
{
@@ -751,6 +820,8 @@ static void mtk_crtc_atomic_enable(struct drm_crtc *crtc,

drm_crtc_vblank_on(crtc);
mtk_crtc->enabled = true;
+
+ drm_vblank_work_init(&mtk_crtc->crc_work, crtc, mtk_crtc_crc_work);
}

static void mtk_crtc_atomic_disable(struct drm_crtc *crtc,
@@ -840,6 +911,8 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
.atomic_destroy_state = mtk_crtc_destroy_state,
.enable_vblank = mtk_crtc_enable_vblank,
.disable_vblank = mtk_crtc_disable_vblank,
+ .set_crc_source = mtk_crtc_set_crc_source,
+ .verify_crc_source = mtk_crtc_verify_crc_source,
};

static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
@@ -1033,6 +1106,11 @@ int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,

if (comp->funcs->ctm_set)
has_ctm = true;
+
+ if (comp->funcs->crc_cnt &&
+ comp->funcs->crc_entry &&
+ comp->funcs->crc_read)
+ mtk_crtc->crc_provider = comp;
}

mtk_ddp_comp_register_vblank_cb(comp, mtk_crtc_ddp_irq,
@@ -1136,3 +1214,185 @@ int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,

return 0;
}
+
+void mtk_crtc_init_crc(struct mtk_crtc_crc *crc, const u32 *crc_offset_table,
+ size_t crc_count, u32 reset_offset, u32 reset_mask)
+{
+ crc->ofs = crc_offset_table;
+ crc->cnt = crc_count;
+ crc->rst_ofs = reset_offset;
+ crc->rst_msk = reset_mask;
+ crc->va = kcalloc(crc->cnt, sizeof(*crc->va), GFP_KERNEL);
+ if (!crc->va) {
+ DRM_ERROR("failed to allocate memory for crc\n");
+ crc->cnt = 0;
+ }
+}
+
+void mtk_crtc_read_crc(struct mtk_crtc_crc *crc, void __iomem *reg)
+{
+ if (!crc->cnt || !crc->ofs || !crc->va)
+ return;
+
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+ /* sync to see the most up-to-date copy of the DMA buffer */
+ dma_sync_single_for_cpu(crc->cmdq_client.chan->mbox->dev,
+ crc->pa, crc->cnt * sizeof(*crc->va),
+ DMA_FROM_DEVICE);
+#endif
+}
+
+void mtk_crtc_destroy_crc(struct mtk_crtc_crc *crc)
+{
+ if (!crc->cnt)
+ return;
+
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+ if (crc->pa) {
+ dma_unmap_single(crc->cmdq_client.chan->mbox->dev,
+ crc->pa, crc->cnt * sizeof(*crc->va),
+ DMA_TO_DEVICE);
+ crc->pa = 0;
+ }
+ if (crc->cmdq_client.chan) {
+ mtk_drm_cmdq_pkt_destroy(&crc->cmdq_handle);
+ mbox_free_channel(crc->cmdq_client.chan);
+ crc->cmdq_client.chan = NULL;
+ }
+#endif
+ kfree(crc->va);
+ crc->va = NULL;
+ crc->cnt = 0;
+}
+
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+/**
+ * mtk_crtc_create_crc_cmdq - Create a CMDQ thread for syncing the CRCs
+ * @dev: Kernel device node of the CRC provider
+ * @crc: Pointer of the CRC to init
+ *
+ * This function will create a looping thread on GCE (Global Command Engine) to
+ * keep the CRC up to date by monitoring the assigned event (usually the frame
+ * done event) of the CRC provider, and read the CRCs from the registers to a
+ * shared memory for the workqueue to read. To start/stop the looping thread,
+ * please call `mtk_crtc_start_crc_cmdq()` and `mtk_crtc_stop_crc_cmdq()`
+ * defined blow.
+ *
+ * The reason why we don't update the CRCs with CPU is that the front porch of
+ * 4K60 timing in CEA-861 is less than 60us, and register read/write speed is
+ * relatively unreliable comparing to GCE due to the bus design.
+ *
+ * We must create a new thread instead of using the original one for plane
+ * update is because:
+ * 1. We cannot add another wait-for-event command at the end of cmdq packet, or
+ * the cmdq callback will delay for too long
+ * 2. Will get the CRC of the previous frame if using the existed wait-for-event
+ * command which is at the beginning of the packet
+ */
+void mtk_crtc_create_crc_cmdq(struct device *dev, struct mtk_crtc_crc *crc)
+{
+ int i;
+
+ if (!crc->cnt) {
+ dev_warn(dev, "%s: not support\n", __func__);
+ goto cleanup;
+ }
+
+ if (!crc->ofs) {
+ dev_warn(dev, "%s: not defined\n", __func__);
+ goto cleanup;
+ }
+
+ crc->cmdq_client.client.dev = dev;
+ crc->cmdq_client.client.tx_block = false;
+ crc->cmdq_client.client.knows_txdone = true;
+ crc->cmdq_client.client.rx_callback = NULL;
+ crc->cmdq_client.chan = mbox_request_channel(&crc->cmdq_client.client, 0);
+ if (IS_ERR(crc->cmdq_client.chan)) {
+ dev_warn(dev, "%s: failed to create mailbox client\n", __func__);
+ crc->cmdq_client.chan = NULL;
+ goto cleanup;
+ }
+
+ if (mtk_drm_cmdq_pkt_create(&crc->cmdq_client, &crc->cmdq_handle, PAGE_SIZE)) {
+ dev_warn(dev, "%s: failed to create cmdq packet\n", __func__);
+ goto cleanup;
+ }
+
+ if (!crc->va) {
+ dev_warn(dev, "%s: no memory\n", __func__);
+ goto cleanup;
+ }
+
+ /* map the entry to get a dma address for cmdq to store the crc */
+ crc->pa = dma_map_single(crc->cmdq_client.chan->mbox->dev,
+ crc->va, crc->cnt * sizeof(*crc->va),
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(crc->cmdq_client.chan->mbox->dev, crc->pa)) {
+ dev_err(dev, "%s: failed to map dma\n", __func__);
+ goto cleanup;
+ }
+
+ if (crc->cmdq_event)
+ cmdq_pkt_wfe(&crc->cmdq_handle, crc->cmdq_event, true);
+
+ for (i = 0; i < crc->cnt; i++) {
+ /* put crc to spr1 register */
+ cmdq_pkt_read_s(&crc->cmdq_handle, crc->cmdq_reg->subsys,
+ crc->cmdq_reg->offset + crc->ofs[i],
+ CMDQ_THR_SPR_IDX1);
+
+ /* copy spr1 register to physical address of the crc */
+ cmdq_pkt_assign(&crc->cmdq_handle, CMDQ_THR_SPR_IDX0,
+ CMDQ_ADDR_HIGH(crc->pa + i * sizeof(*crc->va)));
+ cmdq_pkt_write_s(&crc->cmdq_handle, CMDQ_THR_SPR_IDX0,
+ CMDQ_ADDR_LOW(crc->pa + i * sizeof(*crc->va)),
+ CMDQ_THR_SPR_IDX1);
+ }
+ /* reset crc */
+ mtk_ddp_write_mask(&crc->cmdq_handle, ~0, crc->cmdq_reg, 0,
+ crc->rst_ofs, crc->rst_msk);
+
+ /* clear reset bit */
+ mtk_ddp_write_mask(&crc->cmdq_handle, 0, crc->cmdq_reg, 0,
+ crc->rst_ofs, crc->rst_msk);
+
+ /* jump to head of the cmdq packet */
+ cmdq_pkt_jump(&crc->cmdq_handle, crc->cmdq_handle.pa_base);
+
+ return;
+cleanup:
+ mtk_crtc_destroy_crc(crc);
+}
+
+/**
+ * mtk_crtc_start_crc_cmdq - Start the GCE looping thread for CRC update
+ * @crc: Pointer of the CRC information
+ */
+void mtk_crtc_start_crc_cmdq(struct mtk_crtc_crc *crc)
+{
+ if (!crc->cmdq_client.chan)
+ return;
+
+ dma_sync_single_for_device(crc->cmdq_client.chan->mbox->dev,
+ crc->cmdq_handle.pa_base,
+ crc->cmdq_handle.cmd_buf_size,
+ DMA_TO_DEVICE);
+ mbox_send_message(crc->cmdq_client.chan, &crc->cmdq_handle);
+ mbox_client_txdone(crc->cmdq_client.chan, 0);
+}
+
+/**
+ * mtk_crtc_stop_crc_cmdq - Stop the GCE looping thread for CRC update
+ * @crc: Pointer of the CRC information
+ */
+void mtk_crtc_stop_crc_cmdq(struct mtk_crtc_crc *crc)
+{
+ if (!crc->cmdq_client.chan)
+ return;
+
+ /* remove all the commands from the cmdq packet */
+ mbox_flush(crc->cmdq_client.chan, 2000);
+}
+#endif
diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.h b/drivers/gpu/drm/mediatek/mtk_crtc.h
index 388e900b6f4de..a79c4611754e4 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.h
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.h
@@ -14,6 +14,34 @@
#define MTK_MAX_BPC 10
#define MTK_MIN_BPC 3

+/**
+ * struct mtk_crtc_crc - crc related information
+ * @ofs: register offset of crc
+ * @rst_ofs: register offset of crc reset
+ * @rst_msk: register mask of crc reset
+ * @cnt: count of crc
+ * @va: pointer to the start of crc array
+ * @pa: physical address of the crc for gce to access
+ * @cmdq_event: the event to trigger the cmdq
+ * @cmdq_reg: address of the register that cmdq is going to access
+ * @cmdq_client: handler to control cmdq (mbox channel, thread ...etc.)
+ * @cmdq_handle: cmdq packet to store the commands
+ */
+struct mtk_crtc_crc {
+ const u32 *ofs;
+ u32 rst_ofs;
+ u32 rst_msk;
+ size_t cnt;
+ u32 *va;
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+ dma_addr_t pa;
+ u32 cmdq_event;
+ struct cmdq_client_reg *cmdq_reg;
+ struct cmdq_client cmdq_client;
+ struct cmdq_pkt cmdq_handle;
+#endif
+};
+
void mtk_crtc_commit(struct drm_crtc *crtc);
int mtk_crtc_create(struct drm_device *drm_dev, const unsigned int *path,
unsigned int path_len, int priv_data_index,
@@ -25,4 +53,14 @@ void mtk_crtc_async_update(struct drm_crtc *crtc, struct drm_plane *plane,
struct drm_atomic_state *plane_state);
struct device *mtk_crtc_dma_dev_get(struct drm_crtc *crtc);

+void mtk_crtc_init_crc(struct mtk_crtc_crc *crc, const u32 *crc_offset_table,
+ size_t crc_count, u32 reset_offset, u32 reset_mask);
+void mtk_crtc_read_crc(struct mtk_crtc_crc *crc, void __iomem *reg);
+void mtk_crtc_destroy_crc(struct mtk_crtc_crc *crc);
+#if IS_REACHABLE(CONFIG_MTK_CMDQ)
+void mtk_crtc_create_crc_cmdq(struct device *dev, struct mtk_crtc_crc *crc);
+void mtk_crtc_start_crc_cmdq(struct mtk_crtc_crc *crc);
+void mtk_crtc_stop_crc_cmdq(struct mtk_crtc_crc *crc);
+#endif
+
#endif /* MTK_CRTC_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index f7fe2e08dc8e2..b9c79e740abe0 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -88,6 +88,9 @@ struct mtk_ddp_comp_funcs {
void (*remove)(struct device *dev, struct mtk_mutex *mutex);
unsigned int (*encoder_index)(struct device *dev);
enum drm_mode_status (*mode_valid)(struct device *dev, const struct drm_display_mode *mode);
+ size_t (*crc_cnt)(struct device *dev);
+ u32 *(*crc_entry)(struct device *dev);
+ void (*crc_read)(struct device *dev);
};

struct mtk_ddp_comp {
--
2.18.0



2024-05-04 14:22:48

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v7 16/18] drm/mediatek: Support CRC in display driver

Hi Shawn,

kernel test robot noticed the following build errors:

[auto build test ERROR on next-20240501]
[cannot apply to v6.9-rc6 v6.9-rc5 v6.9-rc4 linus/master v6.9-rc6]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Shawn-Sung/soc-mediatek-Disable-9-bit-alpha-in-ETHDR/20240502-184103
base: next-20240501
patch link: https://lore.kernel.org/r/20240502103848.5845-17-shawn.sung%40mediatek.com
patch subject: [PATCH v7 16/18] drm/mediatek: Support CRC in display driver
config: arm64-defconfig (https://download.01.org/0day-ci/archive/20240504/[email protected]/config)
compiler: aarch64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240504/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

drivers/gpu/drm/mediatek/mtk_crtc.c: In function 'mtk_crtc_create_crc_cmdq':
>> drivers/gpu/drm/mediatek/mtk_crtc.c:1362:9: error: too few arguments to function 'cmdq_pkt_jump'
1362 | cmdq_pkt_jump(&crc->cmdq_handle, crc->cmdq_handle.pa_base);
| ^~~~~~~~~~~~~
In file included from drivers/gpu/drm/mediatek/mtk_crtc.c:11:
include/linux/soc/mediatek/mtk-cmdq.h:319:19: note: declared here
319 | static inline int cmdq_pkt_jump(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa)
| ^~~~~~~~~~~~~


vim +/cmdq_pkt_jump +1362 drivers/gpu/drm/mediatek/mtk_crtc.c

1267
1268 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
1269 /**
1270 * mtk_crtc_create_crc_cmdq - Create a CMDQ thread for syncing the CRCs
1271 * @dev: Kernel device node of the CRC provider
1272 * @crc: Pointer of the CRC to init
1273 *
1274 * This function will create a looping thread on GCE (Global Command Engine) to
1275 * keep the CRC up to date by monitoring the assigned event (usually the frame
1276 * done event) of the CRC provider, and read the CRCs from the registers to a
1277 * shared memory for the workqueue to read. To start/stop the looping thread,
1278 * please call `mtk_crtc_start_crc_cmdq()` and `mtk_crtc_stop_crc_cmdq()`
1279 * defined blow.
1280 *
1281 * The reason why we don't update the CRCs with CPU is that the front porch of
1282 * 4K60 timing in CEA-861 is less than 60us, and register read/write speed is
1283 * relatively unreliable comparing to GCE due to the bus design.
1284 *
1285 * We must create a new thread instead of using the original one for plane
1286 * update is because:
1287 * 1. We cannot add another wait-for-event command at the end of cmdq packet, or
1288 * the cmdq callback will delay for too long
1289 * 2. Will get the CRC of the previous frame if using the existed wait-for-event
1290 * command which is at the beginning of the packet
1291 */
1292 void mtk_crtc_create_crc_cmdq(struct device *dev, struct mtk_crtc_crc *crc)
1293 {
1294 int i;
1295
1296 if (!crc->cnt) {
1297 dev_warn(dev, "%s: not support\n", __func__);
1298 goto cleanup;
1299 }
1300
1301 if (!crc->ofs) {
1302 dev_warn(dev, "%s: not defined\n", __func__);
1303 goto cleanup;
1304 }
1305
1306 crc->cmdq_client.client.dev = dev;
1307 crc->cmdq_client.client.tx_block = false;
1308 crc->cmdq_client.client.knows_txdone = true;
1309 crc->cmdq_client.client.rx_callback = NULL;
1310 crc->cmdq_client.chan = mbox_request_channel(&crc->cmdq_client.client, 0);
1311 if (IS_ERR(crc->cmdq_client.chan)) {
1312 dev_warn(dev, "%s: failed to create mailbox client\n", __func__);
1313 crc->cmdq_client.chan = NULL;
1314 goto cleanup;
1315 }
1316
1317 if (mtk_drm_cmdq_pkt_create(&crc->cmdq_client, &crc->cmdq_handle, PAGE_SIZE)) {
1318 dev_warn(dev, "%s: failed to create cmdq packet\n", __func__);
1319 goto cleanup;
1320 }
1321
1322 if (!crc->va) {
1323 dev_warn(dev, "%s: no memory\n", __func__);
1324 goto cleanup;
1325 }
1326
1327 /* map the entry to get a dma address for cmdq to store the crc */
1328 crc->pa = dma_map_single(crc->cmdq_client.chan->mbox->dev,
1329 crc->va, crc->cnt * sizeof(*crc->va),
1330 DMA_FROM_DEVICE);
1331
1332 if (dma_mapping_error(crc->cmdq_client.chan->mbox->dev, crc->pa)) {
1333 dev_err(dev, "%s: failed to map dma\n", __func__);
1334 goto cleanup;
1335 }
1336
1337 if (crc->cmdq_event)
1338 cmdq_pkt_wfe(&crc->cmdq_handle, crc->cmdq_event, true);
1339
1340 for (i = 0; i < crc->cnt; i++) {
1341 /* put crc to spr1 register */
1342 cmdq_pkt_read_s(&crc->cmdq_handle, crc->cmdq_reg->subsys,
1343 crc->cmdq_reg->offset + crc->ofs[i],
1344 CMDQ_THR_SPR_IDX1);
1345
1346 /* copy spr1 register to physical address of the crc */
1347 cmdq_pkt_assign(&crc->cmdq_handle, CMDQ_THR_SPR_IDX0,
1348 CMDQ_ADDR_HIGH(crc->pa + i * sizeof(*crc->va)));
1349 cmdq_pkt_write_s(&crc->cmdq_handle, CMDQ_THR_SPR_IDX0,
1350 CMDQ_ADDR_LOW(crc->pa + i * sizeof(*crc->va)),
1351 CMDQ_THR_SPR_IDX1);
1352 }
1353 /* reset crc */
1354 mtk_ddp_write_mask(&crc->cmdq_handle, ~0, crc->cmdq_reg, 0,
1355 crc->rst_ofs, crc->rst_msk);
1356
1357 /* clear reset bit */
1358 mtk_ddp_write_mask(&crc->cmdq_handle, 0, crc->cmdq_reg, 0,
1359 crc->rst_ofs, crc->rst_msk);
1360
1361 /* jump to head of the cmdq packet */
> 1362 cmdq_pkt_jump(&crc->cmdq_handle, crc->cmdq_handle.pa_base);
1363
1364 return;
1365 cleanup:
1366 mtk_crtc_destroy_crc(crc);
1367 }
1368

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki