diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/gpu/drm/meson/meson_osd_afbcd.c | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/meson/meson_osd_afbcd.c')
-rw-r--r-- | drivers/gpu/drm/meson/meson_osd_afbcd.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/drivers/gpu/drm/meson/meson_osd_afbcd.c b/drivers/gpu/drm/meson/meson_osd_afbcd.c new file mode 100644 index 000000000..0cdbe8994 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_osd_afbcd.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#include <linux/bitfield.h> + +#include <drm/drm_print.h> +#include <drm/drm_fourcc.h> + +#include "meson_drv.h" +#include "meson_registers.h" +#include "meson_viu.h" +#include "meson_rdma.h" +#include "meson_osd_afbcd.h" + +/* + * DOC: Driver for the ARM FrameBuffer Compression Decoders + * + * The Amlogic GXM and G12A SoC families embeds an AFBC Decoder, + * to decode compressed buffers generated by the ARM Mali GPU. + * + * For the GXM Family, Amlogic designed their own Decoder, named in + * the vendor source as "MESON_AFBC", and a single decoder is available + * for the 2 OSD planes. + * This decoder is compatible with the AFBC 1.0 specifications and the + * Mali T820 GPU capabilities. + * It supports : + * - basic AFBC buffer for RGB32 only, thus YTR feature is mandatory + * - SPARSE layout and SPLIT layout + * - only 16x16 superblock + * + * The decoder reads the data from the SDRAM, decodes and sends the + * decoded pixel stream to the OSD1 Plane pixel composer. + * + * For the G12A Family, Amlogic integrated an ARM AFBC Decoder, named + * in the vendor source as "MALI_AFBC", and the decoder can decode up + * to 4 surfaces, one for each of the 4 available OSDs. + * This decoder is compatible with the AFBC 1.2 specifications for the + * Mali G31 and G52 GPUs. + * Is supports : + * - basic AFBC buffer for multiple RGB and YUV pixel formats + * - SPARSE layout and SPLIT layout + * - 16x16 and 32x8 "wideblk" superblocks + * - Tiled header + * + * The ARM AFBC Decoder independent from the VPU Pixel Pipeline, so + * the ARM AFBC Decoder reads the data from the SDRAM then decodes + * into a private internal physical address where the OSD1 Plane pixel + * composer unpacks the decoded data. + */ + +/* Amlogic AFBC Decoder for GXM Family */ + +#define OSD1_AFBCD_RGB32 0x15 + +static int meson_gxm_afbcd_pixel_fmt(u64 modifier, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return OSD1_AFBCD_RGB32; + /* TOFIX support mode formats */ + default: + DRM_DEBUG("unsupported afbc format[%08x]\n", format); + return -EINVAL; + } +} + +static bool meson_gxm_afbcd_supported_fmt(u64 modifier, uint32_t format) +{ + if (modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) + return false; + + if (!(modifier & AFBC_FORMAT_MOD_YTR)) + return false; + + return meson_gxm_afbcd_pixel_fmt(modifier, format) >= 0; +} + +static int meson_gxm_afbcd_reset(struct meson_drm *priv) +{ + writel_relaxed(VIU_SW_RESET_OSD1_AFBCD, + priv->io_base + _REG(VIU_SW_RESET)); + writel_relaxed(0, priv->io_base + _REG(VIU_SW_RESET)); + + return 0; +} + +static int meson_gxm_afbcd_init(struct meson_drm *priv) +{ + return 0; +} + +static void meson_gxm_afbcd_exit(struct meson_drm *priv) +{ + meson_gxm_afbcd_reset(priv); +} + +static int meson_gxm_afbcd_enable(struct meson_drm *priv) +{ + writel_relaxed(FIELD_PREP(OSD1_AFBCD_ID_FIFO_THRD, 0x40) | + OSD1_AFBCD_DEC_ENABLE, + priv->io_base + _REG(OSD1_AFBCD_ENABLE)); + + return 0; +} + +static int meson_gxm_afbcd_disable(struct meson_drm *priv) +{ + writel_bits_relaxed(OSD1_AFBCD_DEC_ENABLE, 0, + priv->io_base + _REG(OSD1_AFBCD_ENABLE)); + + return 0; +} + +static int meson_gxm_afbcd_setup(struct meson_drm *priv) +{ + u32 conv_lbuf_len; + u32 mode = FIELD_PREP(OSD1_AFBCD_MIF_URGENT, 3) | + FIELD_PREP(OSD1_AFBCD_HOLD_LINE_NUM, 4) | + FIELD_PREP(OSD1_AFBCD_RGBA_EXCHAN_CTRL, 0x34) | + meson_gxm_afbcd_pixel_fmt(priv->afbcd.modifier, + priv->afbcd.format); + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_SPARSE) + mode |= OSD1_AFBCD_HREG_HALF_BLOCK; + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_SPLIT) + mode |= OSD1_AFBCD_HREG_BLOCK_SPLIT; + + writel_relaxed(mode, priv->io_base + _REG(OSD1_AFBCD_MODE)); + + writel_relaxed(FIELD_PREP(OSD1_AFBCD_HREG_VSIZE_IN, + priv->viu.osd1_width) | + FIELD_PREP(OSD1_AFBCD_HREG_HSIZE_IN, + priv->viu.osd1_height), + priv->io_base + _REG(OSD1_AFBCD_SIZE_IN)); + + writel_relaxed(priv->viu.osd1_addr >> 4, + priv->io_base + _REG(OSD1_AFBCD_HDR_PTR)); + writel_relaxed(priv->viu.osd1_addr >> 4, + priv->io_base + _REG(OSD1_AFBCD_FRAME_PTR)); + /* TOFIX: bits 31:24 are not documented, nor the meaning of 0xe4 */ + writel_relaxed((0xe4 << 24) | (priv->viu.osd1_addr & 0xffffff), + priv->io_base + _REG(OSD1_AFBCD_CHROMA_PTR)); + + if (priv->viu.osd1_width <= 128) + conv_lbuf_len = 32; + else if (priv->viu.osd1_width <= 256) + conv_lbuf_len = 64; + else if (priv->viu.osd1_width <= 512) + conv_lbuf_len = 128; + else if (priv->viu.osd1_width <= 1024) + conv_lbuf_len = 256; + else if (priv->viu.osd1_width <= 2048) + conv_lbuf_len = 512; + else + conv_lbuf_len = 1024; + + writel_relaxed(conv_lbuf_len, + priv->io_base + _REG(OSD1_AFBCD_CONV_CTRL)); + + writel_relaxed(FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_BGN_H, 0) | + FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_END_H, + priv->viu.osd1_width - 1), + priv->io_base + _REG(OSD1_AFBCD_PIXEL_HSCOPE)); + + writel_relaxed(FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_BGN_V, 0) | + FIELD_PREP(OSD1_AFBCD_DEC_PIXEL_END_V, + priv->viu.osd1_height - 1), + priv->io_base + _REG(OSD1_AFBCD_PIXEL_VSCOPE)); + + return 0; +} + +struct meson_afbcd_ops meson_afbcd_gxm_ops = { + .init = meson_gxm_afbcd_init, + .exit = meson_gxm_afbcd_exit, + .reset = meson_gxm_afbcd_reset, + .enable = meson_gxm_afbcd_enable, + .disable = meson_gxm_afbcd_disable, + .setup = meson_gxm_afbcd_setup, + .supported_fmt = meson_gxm_afbcd_supported_fmt, +}; + +/* ARM AFBC Decoder for G12A Family */ + +/* Amlogic G12A Mali AFBC Decoder supported formats */ +enum { + MAFBC_FMT_RGB565 = 0, + MAFBC_FMT_RGBA5551, + MAFBC_FMT_RGBA1010102, + MAFBC_FMT_YUV420_10B, + MAFBC_FMT_RGB888, + MAFBC_FMT_RGBA8888, + MAFBC_FMT_RGBA4444, + MAFBC_FMT_R8, + MAFBC_FMT_RG88, + MAFBC_FMT_YUV420_8B, + MAFBC_FMT_YUV422_8B = 11, + MAFBC_FMT_YUV422_10B = 14, +}; + +static int meson_g12a_afbcd_pixel_fmt(u64 modifier, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + /* YTR is forbidden for non XBGR formats */ + if (modifier & AFBC_FORMAT_MOD_YTR) + return -EINVAL; + fallthrough; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return MAFBC_FMT_RGBA8888; + case DRM_FORMAT_RGB888: + /* YTR is forbidden for non XBGR formats */ + if (modifier & AFBC_FORMAT_MOD_YTR) + return -EINVAL; + return MAFBC_FMT_RGB888; + case DRM_FORMAT_RGB565: + /* YTR is forbidden for non XBGR formats */ + if (modifier & AFBC_FORMAT_MOD_YTR) + return -EINVAL; + return MAFBC_FMT_RGB565; + /* TOFIX support mode formats */ + default: + DRM_DEBUG("unsupported afbc format[%08x]\n", format); + return -EINVAL; + } +} + +static int meson_g12a_afbcd_bpp(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return 32; + case DRM_FORMAT_RGB888: + return 24; + case DRM_FORMAT_RGB565: + return 16; + /* TOFIX support mode formats */ + default: + DRM_ERROR("unsupported afbc format[%08x]\n", format); + return 0; + } +} + +static int meson_g12a_afbcd_fmt_to_blk_mode(u64 modifier, uint32_t format) +{ + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return OSD_MALI_COLOR_MODE_RGBA8888; + case DRM_FORMAT_RGB888: + return OSD_MALI_COLOR_MODE_RGB888; + case DRM_FORMAT_RGB565: + return OSD_MALI_COLOR_MODE_RGB565; + /* TOFIX support mode formats */ + default: + DRM_DEBUG("unsupported afbc format[%08x]\n", format); + return -EINVAL; + } +} + +static bool meson_g12a_afbcd_supported_fmt(u64 modifier, uint32_t format) +{ + return meson_g12a_afbcd_pixel_fmt(modifier, format) >= 0; +} + +static int meson_g12a_afbcd_reset(struct meson_drm *priv) +{ + meson_rdma_reset(priv); + + meson_rdma_writel_sync(priv, VIU_SW_RESET_G12A_AFBC_ARB | + VIU_SW_RESET_G12A_OSD1_AFBCD, + VIU_SW_RESET); + meson_rdma_writel_sync(priv, 0, VIU_SW_RESET); + + return 0; +} + +static int meson_g12a_afbcd_init(struct meson_drm *priv) +{ + int ret; + + ret = meson_rdma_init(priv); + if (ret) + return ret; + + meson_rdma_setup(priv); + + /* Handle AFBC Decoder reset manually */ + writel_bits_relaxed(MALI_AFBCD_MANUAL_RESET, MALI_AFBCD_MANUAL_RESET, + priv->io_base + _REG(MALI_AFBCD_TOP_CTRL)); + + return 0; +} + +static void meson_g12a_afbcd_exit(struct meson_drm *priv) +{ + meson_g12a_afbcd_reset(priv); + meson_rdma_free(priv); +} + +static int meson_g12a_afbcd_enable(struct meson_drm *priv) +{ + meson_rdma_writel_sync(priv, VPU_MAFBC_IRQ_SURFACES_COMPLETED | + VPU_MAFBC_IRQ_CONFIGURATION_SWAPPED | + VPU_MAFBC_IRQ_DECODE_ERROR | + VPU_MAFBC_IRQ_DETILING_ERROR, + VPU_MAFBC_IRQ_MASK); + + meson_rdma_writel_sync(priv, VPU_MAFBC_S0_ENABLE, + VPU_MAFBC_SURFACE_CFG); + + meson_rdma_writel_sync(priv, VPU_MAFBC_DIRECT_SWAP, + VPU_MAFBC_COMMAND); + + /* This will enable the RDMA replaying the register writes on vsync */ + meson_rdma_flush(priv); + + return 0; +} + +static int meson_g12a_afbcd_disable(struct meson_drm *priv) +{ + writel_bits_relaxed(VPU_MAFBC_S0_ENABLE, 0, + priv->io_base + _REG(VPU_MAFBC_SURFACE_CFG)); + + return 0; +} + +static int meson_g12a_afbcd_setup(struct meson_drm *priv) +{ + u32 format = meson_g12a_afbcd_pixel_fmt(priv->afbcd.modifier, + priv->afbcd.format); + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_YTR) + format |= VPU_MAFBC_YUV_TRANSFORM; + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_SPLIT) + format |= VPU_MAFBC_BLOCK_SPLIT; + + if (priv->afbcd.modifier & AFBC_FORMAT_MOD_TILED) + format |= VPU_MAFBC_TILED_HEADER_EN; + + if ((priv->afbcd.modifier & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) == + AFBC_FORMAT_MOD_BLOCK_SIZE_32x8) + format |= FIELD_PREP(VPU_MAFBC_SUPER_BLOCK_ASPECT, 1); + + meson_rdma_writel_sync(priv, format, + VPU_MAFBC_FORMAT_SPECIFIER_S0); + + meson_rdma_writel_sync(priv, priv->viu.osd1_addr, + VPU_MAFBC_HEADER_BUF_ADDR_LOW_S0); + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S0); + + meson_rdma_writel_sync(priv, priv->viu.osd1_width, + VPU_MAFBC_BUFFER_WIDTH_S0); + meson_rdma_writel_sync(priv, ALIGN(priv->viu.osd1_height, 32), + VPU_MAFBC_BUFFER_HEIGHT_S0); + + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_BOUNDING_BOX_X_START_S0); + meson_rdma_writel_sync(priv, priv->viu.osd1_width - 1, + VPU_MAFBC_BOUNDING_BOX_X_END_S0); + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_BOUNDING_BOX_Y_START_S0); + meson_rdma_writel_sync(priv, priv->viu.osd1_height - 1, + VPU_MAFBC_BOUNDING_BOX_Y_END_S0); + + meson_rdma_writel_sync(priv, MESON_G12A_AFBCD_OUT_ADDR, + VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S0); + meson_rdma_writel_sync(priv, 0, + VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S0); + + meson_rdma_writel_sync(priv, priv->viu.osd1_width * + (meson_g12a_afbcd_bpp(priv->afbcd.format) / 8), + VPU_MAFBC_OUTPUT_BUF_STRIDE_S0); + + return 0; +} + +struct meson_afbcd_ops meson_afbcd_g12a_ops = { + .init = meson_g12a_afbcd_init, + .exit = meson_g12a_afbcd_exit, + .reset = meson_g12a_afbcd_reset, + .enable = meson_g12a_afbcd_enable, + .disable = meson_g12a_afbcd_disable, + .setup = meson_g12a_afbcd_setup, + .fmt_to_blk_mode = meson_g12a_afbcd_fmt_to_blk_mode, + .supported_fmt = meson_g12a_afbcd_supported_fmt, +}; |