drm/mediatek: Add AFBC support to Mediatek DRM driver
authorJustin Green <greenjustin@chromium.org>
Wed, 16 Nov 2022 19:33:35 +0000 (14:33 -0500)
committerChun-Kuang Hu <chunkuang.hu@kernel.org>
Wed, 23 Nov 2022 16:18:30 +0000 (16:18 +0000)
Tested on MT8195 and confirmed both correct video output and improved DRAM
bandwidth performance.

Signed-off-by: Justin Green <greenjustin@chromium.org>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20221116193335.36320-1-greenjustin@google.com/
Signed-off-by: Chun-Kuang Hu <chunkuang.hu@kernel.org>
drivers/gpu/drm/mediatek/mtk_disp_ovl.c
drivers/gpu/drm/mediatek/mtk_drm_plane.c
drivers/gpu/drm/mediatek/mtk_drm_plane.h

index 002b0f6cae1a063322dbb0e8ae33337c55325314..5a60c3b504ff90e18f30d7483b04c6a64e074540 100644 (file)
 #define DISP_REG_OVL_DATAPATH_CON              0x0024
 #define OVL_LAYER_SMI_ID_EN                            BIT(0)
 #define OVL_BGCLR_SEL_IN                               BIT(2)
+#define OVL_LAYER_AFBC_EN(n)                           BIT(4+n)
 #define DISP_REG_OVL_ROI_BGCLR                 0x0028
 #define DISP_REG_OVL_SRC_CON                   0x002c
 #define DISP_REG_OVL_CON(n)                    (0x0030 + 0x20 * (n))
 #define DISP_REG_OVL_SRC_SIZE(n)               (0x0038 + 0x20 * (n))
 #define DISP_REG_OVL_OFFSET(n)                 (0x003c + 0x20 * (n))
+#define DISP_REG_OVL_PITCH_MSB(n)              (0x0040 + 0x20 * (n))
+#define OVL_PITCH_MSB_2ND_SUBBUF                       BIT(16)
 #define DISP_REG_OVL_PITCH(n)                  (0x0044 + 0x20 * (n))
 #define DISP_REG_OVL_RDMA_CTRL(n)              (0x00c0 + 0x20 * (n))
 #define DISP_REG_OVL_RDMA_GMC(n)               (0x00c8 + 0x20 * (n))
 #define DISP_REG_OVL_ADDR_MT2701               0x0040
 #define DISP_REG_OVL_ADDR_MT8173               0x0f40
 #define DISP_REG_OVL_ADDR(ovl, n)              ((ovl)->data->addr + 0x20 * (n))
+#define DISP_REG_OVL_HDR_ADDR(ovl, n)          ((ovl)->data->addr + 0x20 * (n) + 0x04)
+#define DISP_REG_OVL_HDR_PITCH(ovl, n)         ((ovl)->data->addr + 0x20 * (n) + 0x08)
 
 #define GMC_THRESHOLD_BITS     16
 #define GMC_THRESHOLD_HIGH     ((1 << GMC_THRESHOLD_BITS) / 4)
@@ -67,6 +72,7 @@ struct mtk_disp_ovl_data {
        unsigned int layer_nr;
        bool fmt_rgb565_is_0;
        bool smi_id_en;
+       bool supports_afbc;
 };
 
 /*
@@ -172,7 +178,14 @@ void mtk_ovl_stop(struct device *dev)
                reg = reg & ~OVL_LAYER_SMI_ID_EN;
                writel_relaxed(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON);
        }
+}
 
+static void mtk_ovl_set_afbc(struct mtk_disp_ovl *ovl, struct cmdq_pkt *cmdq_pkt,
+                            int idx, bool enabled)
+{
+       mtk_ddp_write_mask(cmdq_pkt, enabled ? OVL_LAYER_AFBC_EN(idx) : 0,
+                          &ovl->cmdq_reg, ovl->regs,
+                          DISP_REG_OVL_DATAPATH_CON, OVL_LAYER_AFBC_EN(idx));
 }
 
 void mtk_ovl_config(struct device *dev, unsigned int w,
@@ -310,11 +323,23 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
        struct mtk_disp_ovl *ovl = dev_get_drvdata(dev);
        struct mtk_plane_pending_state *pending = &state->pending;
        unsigned int addr = pending->addr;
-       unsigned int pitch = pending->pitch & 0xffff;
+       unsigned int hdr_addr = pending->hdr_addr;
+       unsigned int pitch = pending->pitch;
+       unsigned int hdr_pitch = pending->hdr_pitch;
        unsigned int fmt = pending->format;
        unsigned int offset = (pending->y << 16) | pending->x;
        unsigned int src_size = (pending->height << 16) | pending->width;
        unsigned int con;
+       bool is_afbc = pending->modifier != DRM_FORMAT_MOD_LINEAR;
+       union overlay_pitch {
+               struct split_pitch {
+                       u16 lsb;
+                       u16 msb;
+               } split_pitch;
+               u32 pitch;
+       } overlay_pitch;
+
+       overlay_pitch.pitch = pitch;
 
        if (!pending->enable) {
                mtk_ovl_layer_off(dev, idx, cmdq_pkt);
@@ -335,9 +360,12 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
                addr += pending->pitch - 1;
        }
 
+       if (ovl->data->supports_afbc)
+               mtk_ovl_set_afbc(ovl, cmdq_pkt, idx, is_afbc);
+
        mtk_ddp_write_relaxed(cmdq_pkt, con, &ovl->cmdq_reg, ovl->regs,
                              DISP_REG_OVL_CON(idx));
-       mtk_ddp_write_relaxed(cmdq_pkt, pitch, &ovl->cmdq_reg, ovl->regs,
+       mtk_ddp_write_relaxed(cmdq_pkt, overlay_pitch.split_pitch.lsb, &ovl->cmdq_reg, ovl->regs,
                              DISP_REG_OVL_PITCH(idx));
        mtk_ddp_write_relaxed(cmdq_pkt, src_size, &ovl->cmdq_reg, ovl->regs,
                              DISP_REG_OVL_SRC_SIZE(idx));
@@ -346,6 +374,20 @@ void mtk_ovl_layer_config(struct device *dev, unsigned int idx,
        mtk_ddp_write_relaxed(cmdq_pkt, addr, &ovl->cmdq_reg, ovl->regs,
                              DISP_REG_OVL_ADDR(ovl, idx));
 
+       if (is_afbc) {
+               mtk_ddp_write_relaxed(cmdq_pkt, hdr_addr, &ovl->cmdq_reg, ovl->regs,
+                                     DISP_REG_OVL_HDR_ADDR(ovl, idx));
+               mtk_ddp_write_relaxed(cmdq_pkt,
+                                     OVL_PITCH_MSB_2ND_SUBBUF | overlay_pitch.split_pitch.msb,
+                                     &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH_MSB(idx));
+               mtk_ddp_write_relaxed(cmdq_pkt, hdr_pitch, &ovl->cmdq_reg, ovl->regs,
+                                     DISP_REG_OVL_HDR_PITCH(ovl, idx));
+       } else {
+               mtk_ddp_write_relaxed(cmdq_pkt,
+                                     overlay_pitch.split_pitch.msb,
+                                     &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH_MSB(idx));
+       }
+
        mtk_ovl_layer_on(dev, idx, cmdq_pkt);
 }
 
index 2f5e007dd38001eb8634ed724a52979a299cdde7..d54fbf34b000525db2f7cb1d17e27da97dca0c76 100644 (file)
@@ -11,6 +11,7 @@
 #include <drm/drm_fourcc.h>
 #include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
+#include <linux/align.h>
 
 #include "mtk_drm_crtc.h"
 #include "mtk_drm_ddp_comp.h"
@@ -32,6 +33,14 @@ static const u32 formats[] = {
        DRM_FORMAT_YUYV,
 };
 
+static const u64 modifiers[] = {
+       DRM_FORMAT_MOD_LINEAR,
+       DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+                               AFBC_FORMAT_MOD_SPLIT |
+                               AFBC_FORMAT_MOD_SPARSE),
+       DRM_FORMAT_MOD_INVALID,
+};
+
 static void mtk_plane_reset(struct drm_plane *plane)
 {
        struct mtk_plane_state *state;
@@ -51,6 +60,7 @@ static void mtk_plane_reset(struct drm_plane *plane)
 
        state->base.plane = plane;
        state->pending.format = DRM_FORMAT_RGB565;
+       state->pending.modifier = DRM_FORMAT_MOD_LINEAR;
 }
 
 static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane)
@@ -71,6 +81,32 @@ static struct drm_plane_state *mtk_plane_duplicate_state(struct drm_plane *plane
        return &state->base;
 }
 
+static bool mtk_plane_format_mod_supported(struct drm_plane *plane,
+                                          uint32_t format,
+                                          uint64_t modifier)
+{
+       if (modifier == DRM_FORMAT_MOD_LINEAR)
+               return true;
+
+       if (modifier != DRM_FORMAT_MOD_ARM_AFBC(
+                               AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
+                               AFBC_FORMAT_MOD_SPLIT |
+                               AFBC_FORMAT_MOD_SPARSE))
+               return false;
+
+       if (format != DRM_FORMAT_XRGB8888 &&
+           format != DRM_FORMAT_ARGB8888 &&
+           format != DRM_FORMAT_BGRX8888 &&
+           format != DRM_FORMAT_BGRA8888 &&
+           format != DRM_FORMAT_ABGR8888 &&
+           format != DRM_FORMAT_XBGR8888 &&
+           format != DRM_FORMAT_RGB888 &&
+           format != DRM_FORMAT_BGR888)
+               return false;
+
+       return true;
+}
+
 static void mtk_drm_plane_destroy_state(struct drm_plane *plane,
                                        struct drm_plane_state *state)
 {
@@ -119,21 +155,52 @@ static void mtk_plane_update_new_state(struct drm_plane_state *new_state,
        struct drm_gem_object *gem;
        struct mtk_drm_gem_obj *mtk_gem;
        unsigned int pitch, format;
+       u64 modifier;
        dma_addr_t addr;
+       dma_addr_t hdr_addr = 0;
+       unsigned int hdr_pitch = 0;
 
        gem = fb->obj[0];
        mtk_gem = to_mtk_gem_obj(gem);
        addr = mtk_gem->dma_addr;
        pitch = fb->pitches[0];
        format = fb->format->format;
+       modifier = fb->modifier;
 
-       addr += (new_state->src.x1 >> 16) * fb->format->cpp[0];
-       addr += (new_state->src.y1 >> 16) * pitch;
+       if (modifier == DRM_FORMAT_MOD_LINEAR) {
+               addr += (new_state->src.x1 >> 16) * fb->format->cpp[0];
+               addr += (new_state->src.y1 >> 16) * pitch;
+       } else {
+               int width_in_blocks = ALIGN(fb->width, AFBC_DATA_BLOCK_WIDTH)
+                                     / AFBC_DATA_BLOCK_WIDTH;
+               int height_in_blocks = ALIGN(fb->height, AFBC_DATA_BLOCK_HEIGHT)
+                                      / AFBC_DATA_BLOCK_HEIGHT;
+               int x_offset_in_blocks = (new_state->src.x1 >> 16) / AFBC_DATA_BLOCK_WIDTH;
+               int y_offset_in_blocks = (new_state->src.y1 >> 16) / AFBC_DATA_BLOCK_HEIGHT;
+               int hdr_size;
+
+               hdr_pitch = width_in_blocks * AFBC_HEADER_BLOCK_SIZE;
+               pitch = width_in_blocks * AFBC_DATA_BLOCK_WIDTH *
+                       AFBC_DATA_BLOCK_HEIGHT * fb->format->cpp[0];
+
+               hdr_size = ALIGN(hdr_pitch * height_in_blocks, AFBC_HEADER_ALIGNMENT);
+
+               hdr_addr = addr + hdr_pitch * y_offset_in_blocks +
+                          AFBC_HEADER_BLOCK_SIZE * x_offset_in_blocks;
+               /* The data plane is offset by 1 additional block. */
+               addr = addr + hdr_size +
+                      pitch * y_offset_in_blocks +
+                      AFBC_DATA_BLOCK_WIDTH * AFBC_DATA_BLOCK_HEIGHT *
+                      fb->format->cpp[0] * (x_offset_in_blocks + 1);
+       }
 
        mtk_plane_state->pending.enable = true;
        mtk_plane_state->pending.pitch = pitch;
+       mtk_plane_state->pending.hdr_pitch = hdr_pitch;
        mtk_plane_state->pending.format = format;
+       mtk_plane_state->pending.modifier = modifier;
        mtk_plane_state->pending.addr = addr;
+       mtk_plane_state->pending.hdr_addr = hdr_addr;
        mtk_plane_state->pending.x = new_state->dst.x1;
        mtk_plane_state->pending.y = new_state->dst.y1;
        mtk_plane_state->pending.width = drm_rect_width(&new_state->dst);
@@ -172,6 +239,7 @@ static const struct drm_plane_funcs mtk_plane_funcs = {
        .reset = mtk_plane_reset,
        .atomic_duplicate_state = mtk_plane_duplicate_state,
        .atomic_destroy_state = mtk_drm_plane_destroy_state,
+       .format_mod_supported = mtk_plane_format_mod_supported,
 };
 
 static int mtk_plane_atomic_check(struct drm_plane *plane,
@@ -253,7 +321,7 @@ int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane,
 
        err = drm_universal_plane_init(dev, plane, possible_crtcs,
                                       &mtk_plane_funcs, formats,
-                                      ARRAY_SIZE(formats), NULL, type, NULL);
+                                      ARRAY_SIZE(formats), modifiers, type, NULL);
        if (err) {
                DRM_ERROR("failed to initialize plane\n");
                return err;
index 2d5ec66e3df1bcbc90e5bb3d1cdb711cade54523..8f39011cdbfc92c6632110e47aaab1c479ee11b2 100644 (file)
 #include <drm/drm_crtc.h>
 #include <linux/types.h>
 
+#define AFBC_DATA_BLOCK_WIDTH 32
+#define AFBC_DATA_BLOCK_HEIGHT 8
+#define AFBC_HEADER_BLOCK_SIZE 16
+#define AFBC_HEADER_ALIGNMENT 1024
+
 struct mtk_plane_pending_state {
        bool                            config;
        bool                            enable;
        dma_addr_t                      addr;
+       dma_addr_t                      hdr_addr;
        unsigned int                    pitch;
+       unsigned int                    hdr_pitch;
        unsigned int                    format;
+       unsigned long long              modifier;
        unsigned int                    x;
        unsigned int                    y;
        unsigned int                    width;