2018-05-18 08:25:37

by Liviu Dudau

[permalink] [raw]
Subject: [PATCH v7 0/5] drm/mali-dp: Add support for memory writeback engine

Hi,

Updating the Mali DP memory writeback engine support series to match
the latest generic writeback connector support posted here [1]. As the
generic patches look ready to be merged into drm-misc-next, I'm sending
the updated Mali DP driver as well.

Changelog:
- v7: Added support for DP500 writeback. It behaves differently from
the rest of the IP portfolio, but because customers are asking for
it to be supported in mainline, we're adding it here.
- v6: skipped, in order to align with latest generic series numbers.
- v5: https://lists.freedesktop.org/archives/dri-devel/2017-May/141799.html

Best regards,
Liviu

[1] https://lists.freedesktop.org/archives/dri-devel/2018-May/177125.html



Brian Starkey (2):
drm: mali-dp: Add RGB writeback formats for DP550/DP650
drm: mali-dp: Add writeback connector

Liviu Dudau (3):
drm: mali-dp: Add support for writeback on DP550/DP650
drm/mali-dp: Add writeback support for DP500.
drm/mali-dp: Add RGB writeback formats for DP500.

drivers/gpu/drm/arm/Makefile | 1 +
drivers/gpu/drm/arm/malidp_crtc.c | 9 ++
drivers/gpu/drm/arm/malidp_drv.c | 19 ++-
drivers/gpu/drm/arm/malidp_drv.h | 3 +
drivers/gpu/drm/arm/malidp_hw.c | 165 ++++++++++++++++---
drivers/gpu/drm/arm/malidp_hw.h | 21 +++
drivers/gpu/drm/arm/malidp_mw.c | 253 ++++++++++++++++++++++++++++++
drivers/gpu/drm/arm/malidp_mw.h | 18 +++
drivers/gpu/drm/arm/malidp_regs.h | 18 ++-
drivers/gpu/drm/drm_atomic.c | 4 +
10 files changed, 484 insertions(+), 27 deletions(-)
create mode 100644 drivers/gpu/drm/arm/malidp_mw.c
create mode 100644 drivers/gpu/drm/arm/malidp_mw.h

--
2.17.0



2018-05-18 08:27:51

by Liviu Dudau

[permalink] [raw]
Subject: [PATCH v7 1/5] drm: mali-dp: Add support for writeback on DP550/DP650

Mali-DP display processors are able to write the composition result to a
memory buffer via the SE.

Add entry points in the HAL for enabling/disabling this feature, and
implement support for it on DP650 and DP550. DP500 acts differently and
so is omitted from this change.

Changes since v3:
- Fix missing vsync interrupt for DP550

Signed-off-by: Liviu Dudau <[email protected]>
Signed-off-by: Brian Starkey <[email protected]>
[rebased and fixed conflicts]
Signed-off-by: Mihail Atanassov <[email protected]>
---
drivers/gpu/drm/arm/malidp_hw.c | 54 +++++++++++++++++++++++++++++--
drivers/gpu/drm/arm/malidp_hw.h | 17 ++++++++++
drivers/gpu/drm/arm/malidp_regs.h | 15 +++++++++
3 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
index d789b46dc8173..7a0f1ee161feb 100644
--- a/drivers/gpu/drm/arm/malidp_hw.c
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -588,6 +588,48 @@ static long malidp550_se_calc_mclk(struct malidp_hw_device *hwdev,
return ret;
}

+static int malidp550_enable_memwrite(struct malidp_hw_device *hwdev,
+ dma_addr_t *addrs, s32 *pitches,
+ int num_planes, u16 w, u16 h, u32 fmt_id)
+{
+ u32 base = MALIDP550_SE_MEMWRITE_BASE;
+ u32 de_base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
+
+ /* enable the scaling engine block */
+ malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
+
+ malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
+ switch (num_planes) {
+ case 2:
+ malidp_hw_write(hwdev, lower_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_LOW);
+ malidp_hw_write(hwdev, upper_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_HIGH);
+ malidp_hw_write(hwdev, pitches[1], base + MALIDP_MW_P2_STRIDE);
+ /* fall through */
+ case 1:
+ malidp_hw_write(hwdev, lower_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_LOW);
+ malidp_hw_write(hwdev, upper_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_HIGH);
+ malidp_hw_write(hwdev, pitches[0], base + MALIDP_MW_P1_STRIDE);
+ break;
+ default:
+ WARN(1, "Invalid number of planes");
+ }
+
+ malidp_hw_write(hwdev, MALIDP_DE_H_ACTIVE(w) | MALIDP_DE_V_ACTIVE(h),
+ MALIDP550_SE_MEMWRITE_OUT_SIZE);
+ malidp_hw_setbits(hwdev, MALIDP550_SE_MEMWRITE_ONESHOT | MALIDP_SE_MEMWRITE_EN,
+ MALIDP550_SE_CONTROL);
+
+ return 0;
+}
+
+static void malidp550_disable_memwrite(struct malidp_hw_device *hwdev)
+{
+ u32 base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
+ malidp_hw_clearbits(hwdev, MALIDP550_SE_MEMWRITE_ONESHOT | MALIDP_SE_MEMWRITE_EN,
+ MALIDP550_SE_CONTROL);
+ malidp_hw_clearbits(hwdev, MALIDP_SCALE_ENGINE_EN, base + MALIDP_DE_DISPLAY_FUNC);
+}
+
static int malidp650_query_hw(struct malidp_hw_device *hwdev)
{
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -673,9 +715,11 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
.se_irq_map = {
.irq_mask = MALIDP550_SE_IRQ_EOW |
MALIDP550_SE_IRQ_AXI_ERR,
+ .vsync_irq = MALIDP550_SE_IRQ_EOW,
},
.dc_irq_map = {
- .irq_mask = MALIDP550_DC_IRQ_CONF_VALID,
+ .irq_mask = MALIDP550_DC_IRQ_CONF_VALID |
+ MALIDP550_DC_IRQ_SE,
.vsync_irq = MALIDP550_DC_IRQ_CONF_VALID,
},
.pixel_formats = malidp550_de_formats,
@@ -691,6 +735,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
.rotmem_required = malidp550_rotmem_required,
.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
.se_calc_mclk = malidp550_se_calc_mclk,
+ .enable_memwrite = malidp550_enable_memwrite,
+ .disable_memwrite = malidp550_disable_memwrite,
.features = 0,
},
[MALIDP_650] = {
@@ -711,9 +757,11 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
.se_irq_map = {
.irq_mask = MALIDP550_SE_IRQ_EOW |
MALIDP550_SE_IRQ_AXI_ERR,
+ .vsync_irq = MALIDP550_SE_IRQ_EOW,
},
.dc_irq_map = {
- .irq_mask = MALIDP550_DC_IRQ_CONF_VALID,
+ .irq_mask = MALIDP550_DC_IRQ_CONF_VALID |
+ MALIDP550_DC_IRQ_SE,
.vsync_irq = MALIDP550_DC_IRQ_CONF_VALID,
},
.pixel_formats = malidp550_de_formats,
@@ -729,6 +777,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
.rotmem_required = malidp550_rotmem_required,
.se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs,
.se_calc_mclk = malidp550_se_calc_mclk,
+ .enable_memwrite = malidp550_enable_memwrite,
+ .disable_memwrite = malidp550_disable_memwrite,
.features = 0,
},
};
diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
index b5dd6c73ec9f2..72cc22f828360 100644
--- a/drivers/gpu/drm/arm/malidp_hw.h
+++ b/drivers/gpu/drm/arm/malidp_hw.h
@@ -177,6 +177,23 @@ struct malidp_hw {
long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
struct malidp_se_config *se_config,
struct videomode *vm);
+ /**
+ * Enable writing to memory the content of the next frame
+ * @param hwdev - malidp_hw_device structure containing the HW description
+ * @param addrs - array of addresses for each plane
+ * @param pitches - array of pitches for each plane
+ * @param num_planes - number of planes to be written
+ * @param w - width of the output frame
+ * @param h - height of the output frame
+ * @param fmt_id - internal format ID of output buffer
+ */
+ int (*enable_memwrite)(struct malidp_hw_device *hwdev, dma_addr_t *addrs,
+ s32 *pitches, int num_planes, u16 w, u16 h, u32 fmt_id);
+
+ /*
+ * Disable the writing to memory of the next frame's content.
+ */
+ void (*disable_memwrite)(struct malidp_hw_device *hwdev);

u8 features;
};
diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h
index 149024fb44327..e2b2c496225e3 100644
--- a/drivers/gpu/drm/arm/malidp_regs.h
+++ b/drivers/gpu/drm/arm/malidp_regs.h
@@ -66,6 +66,8 @@
#define MALIDP_DISP_FUNC_GAMMA (1 << 0)
#define MALIDP_DISP_FUNC_CADJ (1 << 4)
#define MALIDP_DISP_FUNC_ILACED (1 << 8)
+#define MALIDP_SCALE_ENGINE_EN (1 << 16)
+#define MALIDP_SE_MEMWRITE_EN (2 << 5)

/* register offsets for IRQ management */
#define MALIDP_REG_STATUS 0x00000
@@ -153,6 +155,16 @@
(((x) & MALIDP_SE_ENH_LIMIT_MASK) << 16)
#define MALIDP_SE_ENH_COEFF0 0x04

+
+/* register offsets relative to MALIDP5x0_SE_MEMWRITE_BASE */
+#define MALIDP_MW_FORMAT 0x00000
+#define MALIDP_MW_P1_STRIDE 0x00004
+#define MALIDP_MW_P2_STRIDE 0x00008
+#define MALIDP_MW_P1_PTR_LOW 0x0000c
+#define MALIDP_MW_P1_PTR_HIGH 0x00010
+#define MALIDP_MW_P2_PTR_LOW 0x0002c
+#define MALIDP_MW_P2_PTR_HIGH 0x00030
+
/* register offsets and bits specific to DP500 */
#define MALIDP500_ADDR_SPACE_SIZE 0x01000
#define MALIDP500_DC_BASE 0x00000
@@ -217,6 +229,9 @@
#define MALIDP550_DE_PERF_BASE 0x00500
#define MALIDP550_SE_BASE 0x08000
#define MALIDP550_SE_CONTROL 0x08010
+#define MALIDP550_SE_MEMWRITE_ONESHOT (1 << 7)
+#define MALIDP550_SE_MEMWRITE_OUT_SIZE 0x08030
+#define MALIDP550_SE_MEMWRITE_BASE 0x08100
#define MALIDP550_DC_BASE 0x0c000
#define MALIDP550_DC_CONTROL 0x0c010
#define MALIDP550_DC_CONFIG_REQ (1 << 16)
--
2.17.0


2018-05-18 08:28:04

by Liviu Dudau

[permalink] [raw]
Subject: [PATCH v7 2/5] drm: mali-dp: Add RGB writeback formats for DP550/DP650

From: Brian Starkey <[email protected]>

Add a layer bit for the SE memory-write, and add it to the pixel format
matrix for DP550/DP650.

Signed-off-by: Brian Starkey <[email protected]>
[rebased and fixed conflicts]
Signed-off-by: Mihail Atanassov <[email protected]>
Signed-off-by: Liviu Dudau <[email protected]>
---
drivers/gpu/drm/arm/malidp_hw.c | 22 +++++++++++-----------
drivers/gpu/drm/arm/malidp_hw.h | 1 +
2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
index 7a0f1ee161feb..455a83689d039 100644
--- a/drivers/gpu/drm/arm/malidp_hw.c
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -47,27 +47,27 @@ static const struct malidp_format_id malidp500_de_formats[] = {

#define MALIDP_COMMON_FORMATS \
/* fourcc, layers supporting the format, internal id */ \
- { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 0) }, \
- { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 1) }, \
- { DRM_FORMAT_RGBA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 2) }, \
- { DRM_FORMAT_BGRA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 3) }, \
+ { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 0) }, \
+ { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 1) }, \
+ { DRM_FORMAT_RGBA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 2) }, \
+ { DRM_FORMAT_BGRA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 3) }, \
{ DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 0) }, \
{ DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 1) }, \
{ DRM_FORMAT_RGBA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 2) }, \
{ DRM_FORMAT_BGRA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 3) }, \
- { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 0) }, \
- { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 1) }, \
- { DRM_FORMAT_RGBX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 2) }, \
- { DRM_FORMAT_BGRX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 3) }, \
- { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 0) }, \
- { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 1) }, \
+ { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 0) }, \
+ { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 1) }, \
+ { DRM_FORMAT_RGBX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 2) }, \
+ { DRM_FORMAT_BGRX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 3) }, \
+ { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(3, 0) }, \
+ { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(3, 1) }, \
{ DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 0) }, \
{ DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1) }, \
{ DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2) }, \
{ DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3) }, \
{ DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \
{ DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \
- { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \
+ { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(5, 6) }, \
{ DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) }

static const struct malidp_format_id malidp550_de_formats[] = {
diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
index 72cc22f828360..a242e97cf6428 100644
--- a/drivers/gpu/drm/arm/malidp_hw.h
+++ b/drivers/gpu/drm/arm/malidp_hw.h
@@ -33,6 +33,7 @@ enum {
DE_GRAPHICS2 = BIT(2), /* used only in DP500 */
DE_VIDEO2 = BIT(3),
DE_SMART = BIT(4),
+ SE_MEMWRITE = BIT(5),
};

struct malidp_format_id {
--
2.17.0


2018-05-18 08:28:34

by Liviu Dudau

[permalink] [raw]
Subject: [PATCH v7 4/5] drm/mali-dp: Add RGB writeback formats for DP500.

Annotate the pixel format matrix for DP500 with the memory-write flag
for formats that are supported by the SE memwrite engine.

Signed-off-by: Liviu Dudau <[email protected]>
---
drivers/gpu/drm/arm/malidp_hw.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
index d9a7f19c9f219..ea8b23aefe961 100644
--- a/drivers/gpu/drm/arm/malidp_hw.c
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -31,12 +31,12 @@ enum {

static const struct malidp_format_id malidp500_de_formats[] = {
/* fourcc, layers supporting the format, internal id */
- { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 },
- { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 1 },
+ { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 0 },
+ { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 1 },
{ DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 2 },
{ DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 3 },
- { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 4 },
- { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 5 },
+ { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 4 },
+ { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 5 },
{ DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 6 },
{ DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 7 },
{ DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 8 },
@@ -45,7 +45,7 @@ static const struct malidp_format_id malidp500_de_formats[] = {
{ DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 11 },
{ DRM_FORMAT_UYVY, DE_VIDEO1, 12 },
{ DRM_FORMAT_YUYV, DE_VIDEO1, 13 },
- { DRM_FORMAT_NV12, DE_VIDEO1, 14 },
+ { DRM_FORMAT_NV12, DE_VIDEO1 | SE_MEMWRITE, 14 },
{ DRM_FORMAT_YUV420, DE_VIDEO1, 15 },
};

--
2.17.0


2018-05-18 08:28:47

by Liviu Dudau

[permalink] [raw]
Subject: [PATCH v7 3/5] drm/mali-dp: Add writeback support for DP500.

Mali DP500 behaves differently from the rest of the Mali DP IP,
in that it does not have a one-shot mode and keeps writing the
content of the current frame to the provided memory area until
stopped. As a way of emulating the one-shot behaviour, we are
going to use the CVAL interrupt that is being raised at the
start of each frame, during prefetch phase, to act as End-of-Write
signal, but with a twist: we are going to disable the memory
write engine right after we're notified that it has been enabled,
using the knowledge that the bit controlling the enabling will
only be acted upon on the next vblank/prefetch.

CVAL interrupt will fire durint the next prefetch phase every time
the global CVAL bit gets set, so we need a state byte to track
the memory write enabling.

Signed-off-by: Liviu Dudau <[email protected]>
---
drivers/gpu/drm/arm/malidp_hw.c | 77 +++++++++++++++++++++++++++++--
drivers/gpu/drm/arm/malidp_hw.h | 5 +-
drivers/gpu/drm/arm/malidp_regs.h | 3 +-
3 files changed, 80 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
index 455a83689d039..d9a7f19c9f219 100644
--- a/drivers/gpu/drm/arm/malidp_hw.c
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -22,6 +22,13 @@
#include "malidp_drv.h"
#include "malidp_hw.h"

+enum {
+ MW_NOT_ENABLED = 0, /* SE writeback not enabled */
+ MW_ONESHOT, /* SE in one-shot mode for writeback */
+ MW_START, /* SE started writeback */
+ MW_STOP, /* SE finished writeback */
+};
+
static const struct malidp_format_id malidp500_de_formats[] = {
/* fourcc, layers supporting the format, internal id */
{ DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 },
@@ -368,6 +375,50 @@ static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev,
return ret;
}

+static int malidp500_enable_memwrite(struct malidp_hw_device *hwdev,
+ dma_addr_t *addrs, s32 *pitches,
+ int num_planes, u16 w, u16 h, u32 fmt_id)
+{
+ u32 base = MALIDP500_SE_MEMWRITE_BASE;
+ u32 de_base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
+
+ /* enable the scaling engine block */
+ malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
+
+ hwdev->mw_state = MW_START;
+
+ malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
+ switch (num_planes) {
+ case 2:
+ malidp_hw_write(hwdev, lower_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_LOW);
+ malidp_hw_write(hwdev, upper_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_HIGH);
+ malidp_hw_write(hwdev, pitches[1], base + MALIDP_MW_P2_STRIDE);
+ /* fall through */
+ case 1:
+ malidp_hw_write(hwdev, lower_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_LOW);
+ malidp_hw_write(hwdev, upper_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_HIGH);
+ malidp_hw_write(hwdev, pitches[0], base + MALIDP_MW_P1_STRIDE);
+ break;
+ default:
+ WARN(1, "Invalid number of planes");
+ }
+
+ malidp_hw_write(hwdev, MALIDP_DE_H_ACTIVE(w) | MALIDP_DE_V_ACTIVE(h),
+ MALIDP500_SE_MEMWRITE_OUT_SIZE);
+ malidp_hw_setbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
+
+ return 0;
+}
+
+static void malidp500_disable_memwrite(struct malidp_hw_device *hwdev)
+{
+ u32 base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
+ if (hwdev->mw_state == MW_START)
+ hwdev->mw_state = MW_STOP;
+ malidp_hw_clearbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
+ malidp_hw_clearbits(hwdev, MALIDP_SCALE_ENGINE_EN, base + MALIDP_DE_DISPLAY_FUNC);
+}
+
static int malidp550_query_hw(struct malidp_hw_device *hwdev)
{
u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
@@ -598,6 +649,8 @@ static int malidp550_enable_memwrite(struct malidp_hw_device *hwdev,
/* enable the scaling engine block */
malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);

+ hwdev->mw_state = MW_ONESHOT;
+
malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
switch (num_planes) {
case 2:
@@ -676,8 +729,9 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
.vsync_irq = MALIDP500_DE_IRQ_VSYNC,
},
.se_irq_map = {
- .irq_mask = MALIDP500_SE_IRQ_CONF_MODE,
- .vsync_irq = 0,
+ .irq_mask = MALIDP500_SE_IRQ_CONF_MODE |
+ MALIDP500_SE_IRQ_CONF_VALID,
+ .vsync_irq = MALIDP500_SE_IRQ_CONF_VALID,
},
.dc_irq_map = {
.irq_mask = MALIDP500_DE_IRQ_CONF_VALID,
@@ -696,6 +750,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
.rotmem_required = malidp500_rotmem_required,
.se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
.se_calc_mclk = malidp500_se_calc_mclk,
+ .enable_memwrite = malidp500_enable_memwrite,
+ .disable_memwrite = malidp500_disable_memwrite,
.features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
},
[MALIDP_550] = {
@@ -934,7 +990,21 @@ static irqreturn_t malidp_se_irq(int irq, void *arg)
mask = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_MASKIRQ);
status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS);
status &= mask;
- /* ToDo: status decoding and firing up of VSYNC and page flip events */
+
+ if (status & se->vsync_irq) {
+ switch (hwdev->mw_state) {
+ case MW_STOP:
+ case MW_ONESHOT:
+ /* disable writeback after stop or oneshot */
+ hwdev->mw_state = MW_NOT_ENABLED;
+ break;
+ case MW_START:
+ /* writeback started, need to emulate one-shot mode */
+ hw->disable_memwrite(hwdev);
+ hw->set_config_valid(hwdev);
+ break;
+ }
+ }

malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, status);

@@ -964,6 +1034,7 @@ int malidp_se_irq_init(struct drm_device *drm, int irq)
return ret;
}

+ hwdev->mw_state = MW_NOT_ENABLED;
malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK,
hwdev->hw->map.se_irq_map.irq_mask);

diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
index a242e97cf6428..c479738b81af5 100644
--- a/drivers/gpu/drm/arm/malidp_hw.h
+++ b/drivers/gpu/drm/arm/malidp_hw.h
@@ -178,7 +178,7 @@ struct malidp_hw {
long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
struct malidp_se_config *se_config,
struct videomode *vm);
- /**
+ /*
* Enable writing to memory the content of the next frame
* @param hwdev - malidp_hw_device structure containing the HW description
* @param addrs - array of addresses for each plane
@@ -232,6 +232,9 @@ struct malidp_hw_device {
/* track the device PM state */
bool pm_suspended;

+ /* track the SE memory writeback state */
+ u8 mw_state;
+
/* size of memory used for rotating layers, up to two banks available */
u32 rotation_memory[2];
};
diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h
index e2b2c496225e3..93b198f3af864 100644
--- a/drivers/gpu/drm/arm/malidp_regs.h
+++ b/drivers/gpu/drm/arm/malidp_regs.h
@@ -198,7 +198,8 @@
#define MALIDP500_DE_LG2_PTR_BASE 0x0031c
#define MALIDP500_SE_BASE 0x00c00
#define MALIDP500_SE_CONTROL 0x00c0c
-#define MALIDP500_SE_PTR_BASE 0x00e0c
+#define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c
+#define MALIDP500_SE_MEMWRITE_BASE 0x00e00
#define MALIDP500_DC_IRQ_BASE 0x00f00
#define MALIDP500_CONFIG_VALID 0x00f00
#define MALIDP500_CONFIG_ID 0x00fd4
--
2.17.0


2018-05-18 08:29:17

by Liviu Dudau

[permalink] [raw]
Subject: [PATCH v7 5/5] drm: mali-dp: Add writeback connector

From: Brian Starkey <[email protected]>

Mali-DP has a memory writeback engine which can be used to write the
composition result to a memory buffer. Expose this functionality as a
DRM writeback connector on supported hardware.

Changes since v1:
Daniel Vetter:
- Don't require a modeset when writeback routing changes
- Make writeback connector always disconnected

Changes since v2:
- Rebase onto new drm_writeback_connector
- Add reset callback, allocating subclassed state
Daniel Vetter:
- Squash out-fence support into this commit
Gustavo Padovan:
- Don't signal fence directly from driver (and drop malidp_mw_job)

Changes since v3:
- Modifications to fit with Mali-DP commit tail changes

Signed-off-by: Brian Starkey <[email protected]>
[rebased and fixed conflicts]
Signed-off-by: Mihail Atanassov <[email protected]>
Signed-off-by: Liviu Dudau <[email protected]>
---
drivers/gpu/drm/arm/Makefile | 1 +
drivers/gpu/drm/arm/malidp_crtc.c | 9 ++
drivers/gpu/drm/arm/malidp_drv.c | 19 ++-
drivers/gpu/drm/arm/malidp_drv.h | 3 +
drivers/gpu/drm/arm/malidp_hw.c | 2 +
drivers/gpu/drm/arm/malidp_mw.c | 253 ++++++++++++++++++++++++++++++
drivers/gpu/drm/arm/malidp_mw.h | 18 +++
drivers/gpu/drm/drm_atomic.c | 4 +
8 files changed, 304 insertions(+), 5 deletions(-)
create mode 100644 drivers/gpu/drm/arm/malidp_mw.c
create mode 100644 drivers/gpu/drm/arm/malidp_mw.h

diff --git a/drivers/gpu/drm/arm/Makefile b/drivers/gpu/drm/arm/Makefile
index bb8b158ff90d0..3bf31d1a4722c 100644
--- a/drivers/gpu/drm/arm/Makefile
+++ b/drivers/gpu/drm/arm/Makefile
@@ -1,4 +1,5 @@
hdlcd-y := hdlcd_drv.o hdlcd_crtc.o
obj-$(CONFIG_DRM_HDLCD) += hdlcd.o
mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o
+mali-dp-y += malidp_mw.o
obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o
diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
index fcc62bc60f6a7..e04e48b5269bd 100644
--- a/drivers/gpu/drm/arm/malidp_crtc.c
+++ b/drivers/gpu/drm/arm/malidp_crtc.c
@@ -411,6 +411,15 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc,
}
}

+ /* If only the writeback routing has changed, we don't need a modeset */
+ if (state->connectors_changed) {
+ u32 old_mask = crtc->state->connector_mask;
+ u32 new_mask = state->connector_mask;
+ if ((old_mask ^ new_mask) ==
+ (1 << drm_connector_index(&malidp->mw_connector.base)))
+ state->connectors_changed = false;
+ }
+
ret = malidp_crtc_atomic_check_gamma(crtc, state);
ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state);
ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state);
diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
index 8d20faa198cf1..4b0c4b4e76224 100644
--- a/drivers/gpu/drm/arm/malidp_drv.c
+++ b/drivers/gpu/drm/arm/malidp_drv.c
@@ -31,6 +31,7 @@
#include <drm/drm_of.h>

#include "malidp_drv.h"
+#include "malidp_mw.h"
#include "malidp_regs.h"
#include "malidp_hw.h"

@@ -230,7 +231,9 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
malidp_atomic_commit_se_config(crtc, old_crtc_state);
}

- drm_atomic_helper_commit_planes(drm, state, 0);
+ drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY);
+
+ malidp_mw_atomic_commit(drm, state);

drm_atomic_helper_commit_modeset_enables(drm, state);

@@ -268,12 +271,18 @@ static int malidp_init(struct drm_device *drm)
drm->mode_config.helper_private = &malidp_mode_config_helpers;

ret = malidp_crtc_init(drm);
- if (ret) {
- drm_mode_config_cleanup(drm);
- return ret;
- }
+ if (ret)
+ goto crtc_fail;
+
+ ret = malidp_mw_connector_init(drm);
+ if (ret)
+ goto crtc_fail;

return 0;
+
+crtc_fail:
+ drm_mode_config_cleanup(drm);
+ return ret;
}

static void malidp_fini(struct drm_device *drm)
diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h
index c70989b933874..7f4eccd32d2f1 100644
--- a/drivers/gpu/drm/arm/malidp_drv.h
+++ b/drivers/gpu/drm/arm/malidp_drv.h
@@ -13,6 +13,8 @@
#ifndef __MALIDP_DRV_H__
#define __MALIDP_DRV_H__

+#include <drm/drm_writeback.h>
+#include <drm/drm_encoder.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <drm/drmP.h>
@@ -21,6 +23,7 @@
struct malidp_drm {
struct malidp_hw_device *dev;
struct drm_crtc crtc;
+ struct drm_writeback_connector mw_connector;
wait_queue_head_t wq;
struct drm_pending_vblank_event *event;
atomic_t config_valid;
diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
index ea8b23aefe961..1eed20e10561f 100644
--- a/drivers/gpu/drm/arm/malidp_hw.c
+++ b/drivers/gpu/drm/arm/malidp_hw.c
@@ -21,6 +21,7 @@

#include "malidp_drv.h"
#include "malidp_hw.h"
+#include "malidp_mw.h"

enum {
MW_NOT_ENABLED = 0, /* SE writeback not enabled */
@@ -995,6 +996,7 @@ static irqreturn_t malidp_se_irq(int irq, void *arg)
switch (hwdev->mw_state) {
case MW_STOP:
case MW_ONESHOT:
+ drm_writeback_signal_completion(&malidp->mw_connector, 0);
/* disable writeback after stop or oneshot */
hwdev->mw_state = MW_NOT_ENABLED;
break;
diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
new file mode 100644
index 0000000000000..e18e62c814f65
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_mw.c
@@ -0,0 +1,253 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Brian Starkey <[email protected]>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * ARM Mali DP Writeback connector implementation
+ */
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drmP.h>
+#include <drm/drm_writeback.h>
+
+#include "malidp_drv.h"
+#include "malidp_hw.h"
+#include "malidp_mw.h"
+
+#define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state)
+
+struct malidp_mw_connector_state {
+ struct drm_connector_state base;
+ dma_addr_t addrs[2];
+ s32 pitches[2];
+ u8 format;
+ u8 n_planes;
+};
+
+static int malidp_mw_connector_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+
+ return drm_add_modes_noedid(connector, dev->mode_config.max_width,
+ dev->mode_config.max_height);
+}
+
+static enum drm_mode_status
+malidp_mw_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ int w = mode->hdisplay, h = mode->vdisplay;
+
+ if ((w < mode_config->min_width) || (w > mode_config->max_width))
+ return MODE_BAD_HVALUE;
+
+ if ((h < mode_config->min_height) || (h > mode_config->max_height))
+ return MODE_BAD_VVALUE;
+
+ return MODE_OK;
+}
+
+const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = {
+ .get_modes = malidp_mw_connector_get_modes,
+ .mode_valid = malidp_mw_connector_mode_valid,
+};
+
+static void malidp_mw_connector_reset(struct drm_connector *connector)
+{
+ struct malidp_mw_connector_state *mw_state =
+ kzalloc(sizeof(*mw_state), GFP_KERNEL);
+
+ if (connector->state)
+ __drm_atomic_helper_connector_destroy_state(connector->state);
+
+ kfree(connector->state);
+ __drm_atomic_helper_connector_reset(connector, &mw_state->base);
+}
+
+static enum drm_connector_status
+malidp_mw_connector_detect(struct drm_connector *connector, bool force)
+{
+ return connector_status_disconnected;
+}
+
+static void malidp_mw_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_state *
+malidp_mw_connector_duplicate_state(struct drm_connector *connector)
+{
+ struct malidp_mw_connector_state *mw_state;
+
+ if (WARN_ON(!connector->state))
+ return NULL;
+
+ mw_state = kzalloc(sizeof(*mw_state), GFP_KERNEL);
+ if (!mw_state)
+ return NULL;
+
+ /* No need to preserve any of our driver-local data */
+ __drm_atomic_helper_connector_duplicate_state(connector, &mw_state->base);
+
+ return &mw_state->base;
+}
+
+static const struct drm_connector_funcs malidp_mw_connector_funcs = {
+ .reset = malidp_mw_connector_reset,
+ .detect = malidp_mw_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = malidp_mw_connector_destroy,
+ .atomic_duplicate_state = malidp_mw_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int
+malidp_mw_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct malidp_mw_connector_state *mw_state = to_mw_state(conn_state);
+ struct malidp_drm *malidp = encoder->dev->dev_private;
+ struct drm_framebuffer *fb;
+ int i, n_planes;
+
+ if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
+ return 0;
+
+ fb = conn_state->writeback_job->fb;
+ if ((fb->width != crtc_state->mode.hdisplay) ||
+ (fb->height != crtc_state->mode.vdisplay)) {
+ DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n",
+ fb->width, fb->height);
+ return -EINVAL;
+ }
+
+ mw_state->format =
+ malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE,
+ fb->format->format);
+ if (mw_state->format == MALIDP_INVALID_FORMAT_ID) {
+ struct drm_format_name_buf format_name;
+
+ DRM_DEBUG_KMS("Invalid pixel format %s\n",
+ drm_get_format_name(fb->format->format,
+ &format_name));
+ return -EINVAL;
+ }
+
+ n_planes = drm_format_num_planes(fb->format->format);
+ for (i = 0; i < n_planes; i++) {
+ struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i);
+ /* memory write buffers are never rotated */
+ u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 0);
+ if (fb->pitches[i] & (alignment - 1)) {
+ DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n",
+ fb->pitches[i], i);
+ return -EINVAL;
+ }
+ mw_state->pitches[i] = fb->pitches[i];
+ mw_state->addrs[i] = obj->paddr + fb->offsets[i];
+ }
+ mw_state->n_planes = n_planes;
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = {
+ .atomic_check = malidp_mw_encoder_atomic_check,
+};
+
+static u32 *get_writeback_formats(struct malidp_drm *malidp, int *n_formats)
+{
+ const struct malidp_hw_regmap *map = &malidp->dev->hw->map;
+ u32 *formats;
+ int n, i;
+
+ formats = kcalloc(map->n_pixel_formats, sizeof(*formats),
+ GFP_KERNEL);
+ if (!formats)
+ return NULL;
+
+ for (n = 0, i = 0; i < map->n_pixel_formats; i++) {
+ if (map->pixel_formats[i].layer & SE_MEMWRITE)
+ formats[n++] = map->pixel_formats[i].format;
+ }
+
+ *n_formats = n;
+
+ return formats;
+}
+
+int malidp_mw_connector_init(struct drm_device *drm)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ u32 *formats;
+ int ret, n_formats;
+
+ if (!malidp->dev->hw->enable_memwrite)
+ return 0;
+
+ malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc);
+ drm_connector_helper_add(&malidp->mw_connector.base,
+ &malidp_mw_connector_helper_funcs);
+
+ formats = get_writeback_formats(malidp, &n_formats);
+ if (!formats)
+ return -ENOMEM;
+
+ ret = drm_writeback_connector_init(drm, &malidp->mw_connector,
+ &malidp_mw_connector_funcs,
+ &malidp_mw_encoder_helper_funcs,
+ formats, n_formats);
+ kfree(formats);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void malidp_mw_atomic_commit(struct drm_device *drm,
+ struct drm_atomic_state *old_state)
+{
+ struct malidp_drm *malidp = drm->dev_private;
+ struct drm_writeback_connector *mw_conn = &malidp->mw_connector;
+ struct drm_connector_state *conn_state = mw_conn->base.state;
+ struct malidp_hw_device *hwdev = malidp->dev;
+ struct malidp_mw_connector_state *mw_state;
+
+ if (!conn_state)
+ return;
+
+ mw_state = to_mw_state(conn_state);
+
+ if (conn_state->writeback_job && conn_state->writeback_job->fb) {
+ struct drm_framebuffer *fb = conn_state->writeback_job->fb;
+
+ DRM_DEV_DEBUG_DRIVER(drm->dev,
+ "Enable memwrite %ux%u:%d %pad fmt: %u\n",
+ fb->width, fb->height,
+ mw_state->pitches[0],
+ &mw_state->addrs[0],
+ mw_state->format);
+
+ drm_writeback_queue_job(mw_conn, conn_state->writeback_job);
+ conn_state->writeback_job = NULL;
+
+ hwdev->hw->enable_memwrite(hwdev, mw_state->addrs,
+ mw_state->pitches, mw_state->n_planes,
+ fb->width, fb->height, mw_state->format);
+ } else {
+ DRM_DEV_DEBUG_DRIVER(drm->dev, "Disable memwrite\n");
+ hwdev->hw->disable_memwrite(hwdev);
+ }
+}
diff --git a/drivers/gpu/drm/arm/malidp_mw.h b/drivers/gpu/drm/arm/malidp_mw.h
new file mode 100644
index 0000000000000..93477eac5ed27
--- /dev/null
+++ b/drivers/gpu/drm/arm/malidp_mw.h
@@ -0,0 +1,18 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ * Author: Brian Starkey <[email protected]>
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ */
+
+#ifndef __MALIDP_MW_H__
+#define __MALIDP_MW_H__
+
+int malidp_mw_connector_init(struct drm_device *drm);
+void malidp_mw_atomic_commit(struct drm_device *drm,
+ struct drm_atomic_state *old_state);
+#endif
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index cc2f86c68b293..c61fe85e80d16 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1378,6 +1378,10 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name);
drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)");

+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ if (state->writeback_job && state->writeback_job->fb)
+ drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id);
+
if (connector->funcs->atomic_print_state)
connector->funcs->atomic_print_state(p, state);
}
--
2.17.0


2018-05-18 08:58:18

by Brian Starkey

[permalink] [raw]
Subject: Re: [PATCH v7 3/5] drm/mali-dp: Add writeback support for DP500.

Hi Liviu,

On Fri, May 18, 2018 at 09:24:21AM +0100, Liviu Dudau wrote:
>Mali DP500 behaves differently from the rest of the Mali DP IP,
>in that it does not have a one-shot mode and keeps writing the
>content of the current frame to the provided memory area until
>stopped. As a way of emulating the one-shot behaviour, we are
>going to use the CVAL interrupt that is being raised at the
>start of each frame, during prefetch phase, to act as End-of-Write
>signal, but with a twist: we are going to disable the memory
>write engine right after we're notified that it has been enabled,
>using the knowledge that the bit controlling the enabling will
>only be acted upon on the next vblank/prefetch.
>
>CVAL interrupt will fire durint the next prefetch phase every time
>the global CVAL bit gets set, so we need a state byte to track
>the memory write enabling.
>
>Signed-off-by: Liviu Dudau <[email protected]>
>---
> drivers/gpu/drm/arm/malidp_hw.c | 77 +++++++++++++++++++++++++++++--
> drivers/gpu/drm/arm/malidp_hw.h | 5 +-
> drivers/gpu/drm/arm/malidp_regs.h | 3 +-
> 3 files changed, 80 insertions(+), 5 deletions(-)
>
>diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
>index 455a83689d039..d9a7f19c9f219 100644
>--- a/drivers/gpu/drm/arm/malidp_hw.c
>+++ b/drivers/gpu/drm/arm/malidp_hw.c
>@@ -22,6 +22,13 @@
> #include "malidp_drv.h"
> #include "malidp_hw.h"
>
>+enum {
>+ MW_NOT_ENABLED = 0, /* SE writeback not enabled */
>+ MW_ONESHOT, /* SE in one-shot mode for writeback */
>+ MW_START, /* SE started writeback */
>+ MW_STOP, /* SE finished writeback */
>+};
>+
> static const struct malidp_format_id malidp500_de_formats[] = {
> /* fourcc, layers supporting the format, internal id */
> { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 },
>@@ -368,6 +375,50 @@ static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev,
> return ret;
> }
>
>+static int malidp500_enable_memwrite(struct malidp_hw_device *hwdev,
>+ dma_addr_t *addrs, s32 *pitches,
>+ int num_planes, u16 w, u16 h, u32 fmt_id)
>+{
>+ u32 base = MALIDP500_SE_MEMWRITE_BASE;
>+ u32 de_base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
>+
>+ /* enable the scaling engine block */
>+ malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
>+
>+ hwdev->mw_state = MW_START;
>+
>+ malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
>+ switch (num_planes) {
>+ case 2:
>+ malidp_hw_write(hwdev, lower_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_LOW);
>+ malidp_hw_write(hwdev, upper_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_HIGH);
>+ malidp_hw_write(hwdev, pitches[1], base + MALIDP_MW_P2_STRIDE);
>+ /* fall through */
>+ case 1:
>+ malidp_hw_write(hwdev, lower_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_LOW);
>+ malidp_hw_write(hwdev, upper_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_HIGH);
>+ malidp_hw_write(hwdev, pitches[0], base + MALIDP_MW_P1_STRIDE);
>+ break;
>+ default:
>+ WARN(1, "Invalid number of planes");
>+ }
>+
>+ malidp_hw_write(hwdev, MALIDP_DE_H_ACTIVE(w) | MALIDP_DE_V_ACTIVE(h),
>+ MALIDP500_SE_MEMWRITE_OUT_SIZE);
>+ malidp_hw_setbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
>+
>+ return 0;
>+}
>+
>+static void malidp500_disable_memwrite(struct malidp_hw_device *hwdev)
>+{
>+ u32 base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
>+ if (hwdev->mw_state == MW_START)
>+ hwdev->mw_state = MW_STOP;
>+ malidp_hw_clearbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
>+ malidp_hw_clearbits(hwdev, MALIDP_SCALE_ENGINE_EN, base + MALIDP_DE_DISPLAY_FUNC);
>+}
>+
> static int malidp550_query_hw(struct malidp_hw_device *hwdev)
> {
> u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
>@@ -598,6 +649,8 @@ static int malidp550_enable_memwrite(struct malidp_hw_device *hwdev,
> /* enable the scaling engine block */
> malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
>
>+ hwdev->mw_state = MW_ONESHOT;
>+
> malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
> switch (num_planes) {
> case 2:
>@@ -676,8 +729,9 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
> .vsync_irq = MALIDP500_DE_IRQ_VSYNC,
> },
> .se_irq_map = {
>- .irq_mask = MALIDP500_SE_IRQ_CONF_MODE,
>- .vsync_irq = 0,
>+ .irq_mask = MALIDP500_SE_IRQ_CONF_MODE |
>+ MALIDP500_SE_IRQ_CONF_VALID,
>+ .vsync_irq = MALIDP500_SE_IRQ_CONF_VALID,
> },
> .dc_irq_map = {
> .irq_mask = MALIDP500_DE_IRQ_CONF_VALID,
>@@ -696,6 +750,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
> .rotmem_required = malidp500_rotmem_required,
> .se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
> .se_calc_mclk = malidp500_se_calc_mclk,
>+ .enable_memwrite = malidp500_enable_memwrite,
>+ .disable_memwrite = malidp500_disable_memwrite,
> .features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
> },
> [MALIDP_550] = {
>@@ -934,7 +990,21 @@ static irqreturn_t malidp_se_irq(int irq, void *arg)
> mask = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_MASKIRQ);
> status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS);
> status &= mask;
>- /* ToDo: status decoding and firing up of VSYNC and page flip events */
>+
>+ if (status & se->vsync_irq) {
>+ switch (hwdev->mw_state) {
>+ case MW_STOP:
>+ case MW_ONESHOT:
>+ /* disable writeback after stop or oneshot */
>+ hwdev->mw_state = MW_NOT_ENABLED;
>+ break;
>+ case MW_START:
>+ /* writeback started, need to emulate one-shot mode */
>+ hw->disable_memwrite(hwdev);
>+ hw->set_config_valid(hwdev);

Is this serialised with incoming atomic commits? We have to make sure
we don't set CVAL while a new scene configuration is in-progress.

I also think the current state tracking won't work properly for two
subsequent frames using writeback. In enable_memwrite() you change the
mw_state, but the currently ongoing job is relying on it to correctly
signal. We probably need to either block incoming commits until the
writeback is finished, or we need to enhance the state tracking to
manage multiple commits.

Thanks,
-Brian

>+ break;
>+ }
>+ }
>
> malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, status);
>
>@@ -964,6 +1034,7 @@ int malidp_se_irq_init(struct drm_device *drm, int irq)
> return ret;
> }
>
>+ hwdev->mw_state = MW_NOT_ENABLED;
> malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK,
> hwdev->hw->map.se_irq_map.irq_mask);
>
>diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
>index a242e97cf6428..c479738b81af5 100644
>--- a/drivers/gpu/drm/arm/malidp_hw.h
>+++ b/drivers/gpu/drm/arm/malidp_hw.h
>@@ -178,7 +178,7 @@ struct malidp_hw {
> long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
> struct malidp_se_config *se_config,
> struct videomode *vm);
>- /**
>+ /*
> * Enable writing to memory the content of the next frame
> * @param hwdev - malidp_hw_device structure containing the HW description
> * @param addrs - array of addresses for each plane
>@@ -232,6 +232,9 @@ struct malidp_hw_device {
> /* track the device PM state */
> bool pm_suspended;
>
>+ /* track the SE memory writeback state */
>+ u8 mw_state;
>+
> /* size of memory used for rotating layers, up to two banks available */
> u32 rotation_memory[2];
> };
>diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h
>index e2b2c496225e3..93b198f3af864 100644
>--- a/drivers/gpu/drm/arm/malidp_regs.h
>+++ b/drivers/gpu/drm/arm/malidp_regs.h
>@@ -198,7 +198,8 @@
> #define MALIDP500_DE_LG2_PTR_BASE 0x0031c
> #define MALIDP500_SE_BASE 0x00c00
> #define MALIDP500_SE_CONTROL 0x00c0c
>-#define MALIDP500_SE_PTR_BASE 0x00e0c
>+#define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c
>+#define MALIDP500_SE_MEMWRITE_BASE 0x00e00
> #define MALIDP500_DC_IRQ_BASE 0x00f00
> #define MALIDP500_CONFIG_VALID 0x00f00
> #define MALIDP500_CONFIG_ID 0x00fd4
>--
>2.17.0
>

2018-05-18 11:24:27

by Liviu Dudau

[permalink] [raw]
Subject: Re: [PATCH v7 3/5] drm/mali-dp: Add writeback support for DP500.

On Fri, May 18, 2018 at 09:56:20AM +0100, Brian Starkey wrote:
> Hi Liviu,
>
> On Fri, May 18, 2018 at 09:24:21AM +0100, Liviu Dudau wrote:
> > Mali DP500 behaves differently from the rest of the Mali DP IP,
> > in that it does not have a one-shot mode and keeps writing the
> > content of the current frame to the provided memory area until
> > stopped. As a way of emulating the one-shot behaviour, we are
> > going to use the CVAL interrupt that is being raised at the
> > start of each frame, during prefetch phase, to act as End-of-Write
> > signal, but with a twist: we are going to disable the memory
> > write engine right after we're notified that it has been enabled,
> > using the knowledge that the bit controlling the enabling will
> > only be acted upon on the next vblank/prefetch.
> >
> > CVAL interrupt will fire durint the next prefetch phase every time
> > the global CVAL bit gets set, so we need a state byte to track
> > the memory write enabling.
> >
> > Signed-off-by: Liviu Dudau <[email protected]>
> > ---
> > drivers/gpu/drm/arm/malidp_hw.c | 77 +++++++++++++++++++++++++++++--
> > drivers/gpu/drm/arm/malidp_hw.h | 5 +-
> > drivers/gpu/drm/arm/malidp_regs.h | 3 +-
> > 3 files changed, 80 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c
> > index 455a83689d039..d9a7f19c9f219 100644
> > --- a/drivers/gpu/drm/arm/malidp_hw.c
> > +++ b/drivers/gpu/drm/arm/malidp_hw.c
> > @@ -22,6 +22,13 @@
> > #include "malidp_drv.h"
> > #include "malidp_hw.h"
> >
> > +enum {
> > + MW_NOT_ENABLED = 0, /* SE writeback not enabled */
> > + MW_ONESHOT, /* SE in one-shot mode for writeback */
> > + MW_START, /* SE started writeback */
> > + MW_STOP, /* SE finished writeback */
> > +};
> > +
> > static const struct malidp_format_id malidp500_de_formats[] = {
> > /* fourcc, layers supporting the format, internal id */
> > { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 },
> > @@ -368,6 +375,50 @@ static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev,
> > return ret;
> > }
> >
> > +static int malidp500_enable_memwrite(struct malidp_hw_device *hwdev,
> > + dma_addr_t *addrs, s32 *pitches,
> > + int num_planes, u16 w, u16 h, u32 fmt_id)
> > +{
> > + u32 base = MALIDP500_SE_MEMWRITE_BASE;
> > + u32 de_base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
> > +
> > + /* enable the scaling engine block */
> > + malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
> > +
> > + hwdev->mw_state = MW_START;
> > +
> > + malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
> > + switch (num_planes) {
> > + case 2:
> > + malidp_hw_write(hwdev, lower_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_LOW);
> > + malidp_hw_write(hwdev, upper_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_HIGH);
> > + malidp_hw_write(hwdev, pitches[1], base + MALIDP_MW_P2_STRIDE);
> > + /* fall through */
> > + case 1:
> > + malidp_hw_write(hwdev, lower_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_LOW);
> > + malidp_hw_write(hwdev, upper_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_HIGH);
> > + malidp_hw_write(hwdev, pitches[0], base + MALIDP_MW_P1_STRIDE);
> > + break;
> > + default:
> > + WARN(1, "Invalid number of planes");
> > + }
> > +
> > + malidp_hw_write(hwdev, MALIDP_DE_H_ACTIVE(w) | MALIDP_DE_V_ACTIVE(h),
> > + MALIDP500_SE_MEMWRITE_OUT_SIZE);
> > + malidp_hw_setbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
> > +
> > + return 0;
> > +}
> > +
> > +static void malidp500_disable_memwrite(struct malidp_hw_device *hwdev)
> > +{
> > + u32 base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK);
> > + if (hwdev->mw_state == MW_START)
> > + hwdev->mw_state = MW_STOP;
> > + malidp_hw_clearbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL);
> > + malidp_hw_clearbits(hwdev, MALIDP_SCALE_ENGINE_EN, base + MALIDP_DE_DISPLAY_FUNC);
> > +}
> > +
> > static int malidp550_query_hw(struct malidp_hw_device *hwdev)
> > {
> > u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID);
> > @@ -598,6 +649,8 @@ static int malidp550_enable_memwrite(struct malidp_hw_device *hwdev,
> > /* enable the scaling engine block */
> > malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC);
> >
> > + hwdev->mw_state = MW_ONESHOT;
> > +
> > malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT);
> > switch (num_planes) {
> > case 2:
> > @@ -676,8 +729,9 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
> > .vsync_irq = MALIDP500_DE_IRQ_VSYNC,
> > },
> > .se_irq_map = {
> > - .irq_mask = MALIDP500_SE_IRQ_CONF_MODE,
> > - .vsync_irq = 0,
> > + .irq_mask = MALIDP500_SE_IRQ_CONF_MODE |
> > + MALIDP500_SE_IRQ_CONF_VALID,
> > + .vsync_irq = MALIDP500_SE_IRQ_CONF_VALID,
> > },
> > .dc_irq_map = {
> > .irq_mask = MALIDP500_DE_IRQ_CONF_VALID,
> > @@ -696,6 +750,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = {
> > .rotmem_required = malidp500_rotmem_required,
> > .se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs,
> > .se_calc_mclk = malidp500_se_calc_mclk,
> > + .enable_memwrite = malidp500_enable_memwrite,
> > + .disable_memwrite = malidp500_disable_memwrite,
> > .features = MALIDP_DEVICE_LV_HAS_3_STRIDES,
> > },
> > [MALIDP_550] = {
> > @@ -934,7 +990,21 @@ static irqreturn_t malidp_se_irq(int irq, void *arg)
> > mask = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_MASKIRQ);
> > status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS);
> > status &= mask;
> > - /* ToDo: status decoding and firing up of VSYNC and page flip events */
> > +
> > + if (status & se->vsync_irq) {
> > + switch (hwdev->mw_state) {
> > + case MW_STOP:
> > + case MW_ONESHOT:
> > + /* disable writeback after stop or oneshot */
> > + hwdev->mw_state = MW_NOT_ENABLED;
> > + break;
> > + case MW_START:
> > + /* writeback started, need to emulate one-shot mode */
> > + hw->disable_memwrite(hwdev);
> > + hw->set_config_valid(hwdev);
>
> Is this serialised with incoming atomic commits? We have to make sure
> we don't set CVAL while a new scene configuration is in-progress.

We are not serialised with the incoming atomic commit, and we probably
should. I'll have a think on how to sort this one out.

>
> I also think the current state tracking won't work properly for two
> subsequent frames using writeback. In enable_memwrite() you change the
> mw_state, but the currently ongoing job is relying on it to correctly
> signal. We probably need to either block incoming commits until the
> writeback is finished, or we need to enhance the state tracking to
> manage multiple commits.

I will go back to the drawing boards and come up with something more
solid.

Best regards,
Liviu

>
> Thanks,
> -Brian
>
> > + break;
> > + }
> > + }
> >
> > malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, status);
> >
> > @@ -964,6 +1034,7 @@ int malidp_se_irq_init(struct drm_device *drm, int irq)
> > return ret;
> > }
> >
> > + hwdev->mw_state = MW_NOT_ENABLED;
> > malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK,
> > hwdev->hw->map.se_irq_map.irq_mask);
> >
> > diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h
> > index a242e97cf6428..c479738b81af5 100644
> > --- a/drivers/gpu/drm/arm/malidp_hw.h
> > +++ b/drivers/gpu/drm/arm/malidp_hw.h
> > @@ -178,7 +178,7 @@ struct malidp_hw {
> > long (*se_calc_mclk)(struct malidp_hw_device *hwdev,
> > struct malidp_se_config *se_config,
> > struct videomode *vm);
> > - /**
> > + /*
> > * Enable writing to memory the content of the next frame
> > * @param hwdev - malidp_hw_device structure containing the HW description
> > * @param addrs - array of addresses for each plane
> > @@ -232,6 +232,9 @@ struct malidp_hw_device {
> > /* track the device PM state */
> > bool pm_suspended;
> >
> > + /* track the SE memory writeback state */
> > + u8 mw_state;
> > +
> > /* size of memory used for rotating layers, up to two banks available */
> > u32 rotation_memory[2];
> > };
> > diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h
> > index e2b2c496225e3..93b198f3af864 100644
> > --- a/drivers/gpu/drm/arm/malidp_regs.h
> > +++ b/drivers/gpu/drm/arm/malidp_regs.h
> > @@ -198,7 +198,8 @@
> > #define MALIDP500_DE_LG2_PTR_BASE 0x0031c
> > #define MALIDP500_SE_BASE 0x00c00
> > #define MALIDP500_SE_CONTROL 0x00c0c
> > -#define MALIDP500_SE_PTR_BASE 0x00e0c
> > +#define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c
> > +#define MALIDP500_SE_MEMWRITE_BASE 0x00e00
> > #define MALIDP500_DC_IRQ_BASE 0x00f00
> > #define MALIDP500_CONFIG_VALID 0x00f00
> > #define MALIDP500_CONFIG_ID 0x00fd4
> > --
> > 2.17.0
> >

--
====================
| I would like to |
| fix the world, |
| but they're not |
| giving me the |
\ source code! /
---------------
¯\_(ツ)_/¯