diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt
index 13b56372ac28bfaeed2007836c40f7923614b552..e269a3d352a131a4d79c911d6e53565bf8414bf6 100644
--- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt
+++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt
@@ -226,6 +226,29 @@ dcss_drm: dcss@0x32e00000 {
 	};
 };
 
+Freescale i.MX8 PRG (Prefetch Resolve Gasket)
+=============================================
+Required properties:
+- compatible: should be "fsl,<chip>-prg"
+- reg: should be register base and length as documented in the
+  datasheet
+- clocks: phandles to the PRG apb and rtram clocks, as described in
+  Documentation/devicetree/bindings/clock/clock-bindings.txt,
+  Documentation/devicetree/bindings/clock/imx8qm-clock.txt and
+  Documentation/devicetree/bindings/clock/imx8qxp-clock.txt
+- clock-names: should be "apb" and "rtram"
+- power-domains: phandle pointing to power domain
+
+example:
+
+prg@56040000 {
+	compatible = "fsl,imx8qm-prg";
+	reg = <0x0 0x56040000 0x0 0x10000>;
+	clocks = <&clk IMX8QM_DC0_PRG0_APB_CLK>,
+		 <&clk IMX8QM_DC0_PRG0_RTRAM_CLK>;
+	clock-names = "apb", "rtram";
+	power-domains = <&pd_dc0>;
+};
 
 Parallel display support
 ========================
diff --git a/drivers/gpu/imx/Kconfig b/drivers/gpu/imx/Kconfig
index 8dccc191858f4981344454ecc14a4cf742dcbf36..7a2c7e06f0526871c8959f71489b533f45976b0e 100644
--- a/drivers/gpu/imx/Kconfig
+++ b/drivers/gpu/imx/Kconfig
@@ -1,3 +1,8 @@
+config IMX8_PRG
+	tristate
+	default y if IMX_DPU_CORE=y
+	default m if IMX_DPU_CORE=m
+
 source drivers/gpu/imx/ipu-v3/Kconfig
 source drivers/gpu/imx/lcdif/Kconfig
 source drivers/gpu/imx/dcss/Kconfig
diff --git a/drivers/gpu/imx/Makefile b/drivers/gpu/imx/Makefile
index 9d772bb4064d8e4078102bb85b72728e23250f67..a8feed067a1f81e41de6ef1c3eea14c526542d65 100644
--- a/drivers/gpu/imx/Makefile
+++ b/drivers/gpu/imx/Makefile
@@ -1,3 +1,5 @@
+obj-$(CONFIG_IMX8_PRG)	+= imx8_prg.o
+
 obj-$(CONFIG_IMX_IPUV3_CORE)	+= ipu-v3/
 obj-$(CONFIG_IMX_LCDIF_CORE)	+= lcdif/
 obj-$(CONFIG_IMX_DCSS_CORE)	+= dcss/
diff --git a/drivers/gpu/imx/imx8_prg.c b/drivers/gpu/imx/imx8_prg.c
new file mode 100644
index 0000000000000000000000000000000000000000..4dbcb1cb909a91f0b45cc440ef271e04c536ecbf
--- /dev/null
+++ b/drivers/gpu/imx/imx8_prg.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2017-2019 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <drm/drm_fourcc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <video/imx8-prefetch.h>
+
+#define SET			0x4
+#define CLR			0x8
+#define TOG			0xc
+
+#define PRG_CTRL		0x00
+#define BYPASS			BIT(0)
+#define SC_DATA_TYPE		BIT(2)
+#define SC_DATA_TYPE_8BIT	0
+#define SC_DATA_TYPE_10BIT	BIT(2)
+#define UV_EN			BIT(3)
+#define HANDSHAKE_MODE		BIT(4)
+#define HANDSHAKE_MODE_4LINES	0
+#define HANDSHAKE_MODE_8LINES	BIT(4)
+#define SHADOW_LOAD_MODE	BIT(5)
+#define DES_DATA_TYPE		0x30000
+enum {
+	DES_DATA_TYPE_32BPP = (0 << 16),
+	DES_DATA_TYPE_24BPP = (1 << 16),
+	DES_DATA_TYPE_16BPP = (2 << 16),
+	DES_DATA_TYPE_8BPP = (3 << 16),
+};
+#define SOFTRST			BIT(30)
+#define SHADOW_EN		BIT(31)
+
+#define PRG_STATUS		0x10
+#define BUFFER_VALID_B		BIT(1)
+#define BUFFER_VALID_A		BIT(0)
+
+#define PRG_REG_UPDATE		0x20
+#define REG_UPDATE		BIT(0)
+
+#define PRG_STRIDE		0x30
+#define STRIDE(n)		(((n) - 1) & 0xffff)
+
+#define PRG_HEIGHT		0x40
+#define HEIGHT(n)		(((n) - 1) & 0xffff)
+
+#define PRG_BADDR		0x50
+
+#define PRG_OFFSET		0x60
+#define Y(n)			(((n) & 0x7) << 16)
+#define X(n)			((n) & 0xffff)
+
+#define PRG_WIDTH		0x70
+#define WIDTH(n)		(((n) - 1) & 0xffff)
+
+struct prg {
+	struct device *dev;
+	void __iomem *base;
+	struct list_head list;
+	struct clk *clk_apb;
+	struct clk *clk_rtram;
+	bool is_auxiliary;
+	bool is_blit;
+};
+
+static DEFINE_MUTEX(prg_list_mutex);
+static LIST_HEAD(prg_list);
+
+static inline u32 prg_read(struct prg *prg, unsigned int offset)
+{
+	return readl(prg->base + offset);
+}
+
+static inline void prg_write(struct prg *prg, u32 value, unsigned int offset)
+{
+	writel(value, prg->base + offset);
+}
+
+static void prg_reset(struct prg *prg)
+{
+	if (prg->is_blit)
+		usleep_range(10, 20);
+
+	prg_write(prg, SOFTRST, PRG_CTRL + SET);
+
+	if (prg->is_blit)
+		usleep_range(10, 20);
+	else
+		usleep_range(1000, 2000);
+
+	prg_write(prg, SOFTRST, PRG_CTRL + CLR);
+}
+
+void prg_enable(struct prg *prg)
+{
+	if (WARN_ON(!prg))
+		return;
+
+	prg_write(prg, BYPASS, PRG_CTRL + CLR);
+}
+EXPORT_SYMBOL_GPL(prg_enable);
+
+void prg_disable(struct prg *prg)
+{
+	if (WARN_ON(!prg))
+		return;
+
+	prg_write(prg, BYPASS, PRG_CTRL);
+}
+EXPORT_SYMBOL_GPL(prg_disable);
+
+void prg_configure(struct prg *prg, unsigned int width, unsigned int height,
+		   unsigned int x_offset, unsigned int y_offset,
+		   unsigned int stride, unsigned int bits_per_pixel,
+		   unsigned long baddr, u32 format, u64 modifier,
+		   bool start)
+{
+	unsigned int burst_size;
+	unsigned int mt_w = 0, mt_h = 0;	/* w/h in a micro-tile */
+	unsigned long _baddr;
+	u32 val;
+
+	if (WARN_ON(!prg))
+		return;
+
+	if (start)
+		prg_reset(prg);
+
+	/* prg finer cropping into micro-tile block - top/left start point */
+	switch (modifier) {
+	case DRM_FORMAT_MOD_NONE:
+		break;
+	case DRM_FORMAT_MOD_AMPHION_TILED:
+		mt_w = 8;
+		mt_h = 8;
+		break;
+	case DRM_FORMAT_MOD_VIVANTE_TILED:
+	case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+		mt_w = (bits_per_pixel == 16) ? 8 : 4;
+		mt_h = 4;
+		break;
+	default:
+		dev_err(prg->dev, "unsupported modifier 0x%016llx\n", modifier);
+		return;
+	}
+
+	if (modifier) {
+		x_offset %= mt_w;
+		y_offset %= mt_h;
+
+		/* consider x offset to calculate stride */
+		_baddr = baddr + (x_offset * (bits_per_pixel / 8));
+	} else {
+		x_offset = 0;
+		y_offset = 0;
+		_baddr = baddr;
+	}
+
+	/*
+	 * address TKT343664:
+	 * fetch unit base address has to align to burst_size
+	 */
+	burst_size = 1 << (ffs(_baddr) - 1);
+	burst_size = round_up(burst_size, 8);
+	burst_size = min(burst_size, 128U);
+
+	/*
+	 * address TKT339017:
+	 * fixup for burst size vs stride mismatch
+	 */
+	if (modifier)
+		stride = round_up(stride + round_up(_baddr % 8, 8), burst_size);
+	else
+		stride = round_up(stride, burst_size);
+
+	/*
+	 * address TKT342628(part 1):
+	 * when prg stride is less or equals to burst size,
+	 * the auxiliary prg height needs to be a half
+	 */
+	if (prg->is_auxiliary && stride <= burst_size) {
+		height /= 2;
+		if (modifier)
+			y_offset /= 2;
+	}
+
+	prg_write(prg, STRIDE(stride), PRG_STRIDE);
+	prg_write(prg, WIDTH(width), PRG_WIDTH);
+	prg_write(prg, HEIGHT(height), PRG_HEIGHT);
+	prg_write(prg, X(x_offset) | Y(y_offset), PRG_OFFSET);
+	prg_write(prg, baddr, PRG_BADDR);
+
+	val = prg_read(prg, PRG_CTRL);
+	val &= ~SC_DATA_TYPE;
+	val |= SC_DATA_TYPE_8BIT;
+	val &= ~HANDSHAKE_MODE;
+	if (format == DRM_FORMAT_NV21 || format == DRM_FORMAT_NV12) {
+		val |= HANDSHAKE_MODE_8LINES;
+		/*
+		 * address TKT342628(part 2):
+		 * when prg stride is less or equals to burst size,
+		 * we disable UV_EN bit for the auxiliary prg
+		 */
+		if (prg->is_auxiliary && stride > burst_size)
+			val |= UV_EN;
+		else
+			val &= ~UV_EN;
+	} else {
+		val |= HANDSHAKE_MODE_4LINES;
+		val &= ~UV_EN;
+	}
+	val |= SHADOW_LOAD_MODE;
+	val &= ~DES_DATA_TYPE;
+	switch (bits_per_pixel) {
+	case 32:
+		val |= DES_DATA_TYPE_32BPP;
+		break;
+	case 24:
+		val |= DES_DATA_TYPE_24BPP;
+		break;
+	case 16:
+		val |= DES_DATA_TYPE_16BPP;
+		break;
+	case 8:
+		val |= DES_DATA_TYPE_8BPP;
+		break;
+	}
+	if (start)
+		/* no shadow for the first frame */
+		val &= ~SHADOW_EN;
+	else
+		val |= SHADOW_EN;
+	prg_write(prg, val, PRG_CTRL);
+
+	dev_dbg(prg->dev, "bits per pixel %u\n", bits_per_pixel);
+}
+EXPORT_SYMBOL_GPL(prg_configure);
+
+void prg_reg_update(struct prg *prg)
+{
+	if (WARN_ON(!prg))
+		return;
+
+	prg_write(prg, REG_UPDATE, PRG_REG_UPDATE);
+}
+EXPORT_SYMBOL_GPL(prg_reg_update);
+
+void prg_shadow_enable(struct prg *prg)
+{
+	if (WARN_ON(!prg))
+		return;
+
+	prg_write(prg, SHADOW_EN, PRG_CTRL + SET);
+}
+EXPORT_SYMBOL_GPL(prg_shadow_enable);
+
+bool prg_stride_supported(struct prg *prg, unsigned int stride)
+{
+	return stride < 0x10000;
+}
+EXPORT_SYMBOL_GPL(prg_stride_supported);
+
+bool prg_stride_double_check(struct prg *prg,
+			     unsigned int width, unsigned int x_offset,
+			     unsigned int bits_per_pixel, u64 modifier,
+			     unsigned int stride, dma_addr_t baddr)
+{
+	unsigned int burst_size;
+	unsigned int mt_w = 0;	/* w in a micro-tile */
+	dma_addr_t _baddr;
+
+	if (WARN_ON(!prg))
+		return false;
+
+	/* prg finer cropping into micro-tile block - top/left start point */
+	switch (modifier) {
+	case DRM_FORMAT_MOD_NONE:
+		break;
+	case DRM_FORMAT_MOD_AMPHION_TILED:
+		mt_w = 8;
+		break;
+	case DRM_FORMAT_MOD_VIVANTE_TILED:
+	case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
+		mt_w = (bits_per_pixel == 16) ? 8 : 4;
+		break;
+	default:
+		dev_err(prg->dev, "unsupported modifier 0x%016llx\n", modifier);
+		return false;
+	}
+
+	if (modifier) {
+		x_offset %= mt_w;
+
+		/* consider x offset to calculate stride */
+		_baddr = baddr + (x_offset * (bits_per_pixel / 8));
+	} else {
+		_baddr = baddr;
+	}
+
+	/*
+	 * address TKT343664:
+	 * fetch unit base address has to align to burst size
+	 */
+	burst_size = 1 << (ffs(_baddr) - 1);
+	burst_size = round_up(burst_size, 8);
+	burst_size = min(burst_size, 128U);
+
+	/*
+	 * address TKT339017:
+	 * fixup for burst size vs stride mismatch
+	 */
+	if (modifier)
+		stride = round_up(stride + round_up(_baddr % 8, 8), burst_size);
+	else
+		stride = round_up(stride, burst_size);
+
+	return stride < 0x10000;
+}
+EXPORT_SYMBOL_GPL(prg_stride_double_check);
+
+void prg_set_auxiliary(struct prg *prg)
+{
+	if (WARN_ON(!prg))
+		return;
+
+	prg->is_auxiliary = true;
+}
+EXPORT_SYMBOL_GPL(prg_set_auxiliary);
+
+void prg_set_primary(struct prg *prg)
+{
+	if (WARN_ON(!prg))
+		return;
+
+	prg->is_auxiliary = false;
+}
+EXPORT_SYMBOL_GPL(prg_set_primary);
+
+void prg_set_blit(struct prg *prg)
+{
+	if (WARN_ON(!prg))
+		return;
+
+	prg->is_blit = true;
+}
+EXPORT_SYMBOL_GPL(prg_set_blit);
+
+struct prg *
+prg_lookup_by_phandle(struct device *dev, const char *name, int index)
+{
+	struct device_node *prg_node = of_parse_phandle(dev->of_node,
+							name, index);
+	struct prg *prg;
+
+	mutex_lock(&prg_list_mutex);
+	list_for_each_entry(prg, &prg_list, list) {
+		if (prg_node == prg->dev->of_node) {
+			mutex_unlock(&prg_list_mutex);
+			device_link_add(dev, prg->dev,
+					DL_FLAG_AUTOREMOVE_CONSUMER);
+			return prg;
+		}
+	}
+	mutex_unlock(&prg_list_mutex);
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(prg_lookup_by_phandle);
+
+static const struct of_device_id prg_dt_ids[] = {
+	{ .compatible = "fsl,imx8qm-prg", },
+	{ .compatible = "fsl,imx8qxp-prg", },
+	{ /* sentinel */ },
+};
+
+static int prg_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct prg *prg;
+
+	prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL);
+	if (!prg)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	prg->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(prg->base))
+		return PTR_ERR(prg->base);
+
+	prg->clk_apb = devm_clk_get(dev, "apb");
+	if (IS_ERR(prg->clk_apb))
+		return PTR_ERR(prg->clk_apb);
+	clk_prepare_enable(prg->clk_apb);
+
+	prg->clk_rtram = devm_clk_get(dev, "rtram");
+	if (IS_ERR(prg->clk_rtram))
+		return PTR_ERR(prg->clk_rtram);
+	clk_prepare_enable(prg->clk_rtram);
+
+	prg->dev = dev;
+	platform_set_drvdata(pdev, prg);
+	mutex_lock(&prg_list_mutex);
+	list_add(&prg->list, &prg_list);
+	mutex_unlock(&prg_list_mutex);
+
+	prg_reset(prg);
+
+	return 0;
+}
+
+static int prg_remove(struct platform_device *pdev)
+{
+	struct prg *prg = platform_get_drvdata(pdev);
+
+	mutex_lock(&prg_list_mutex);
+	list_del(&prg->list);
+	mutex_unlock(&prg_list_mutex);
+
+	clk_disable_unprepare(prg->clk_rtram);
+	clk_disable_unprepare(prg->clk_apb);
+
+	return 0;
+}
+
+struct platform_driver prg_drv = {
+	.probe = prg_probe,
+	.remove = prg_remove,
+	.driver = {
+		.name = "imx8-prg",
+		.of_match_table = prg_dt_ids,
+	},
+};
+module_platform_driver(prg_drv);
+
+MODULE_DESCRIPTION("i.MX8 PRG driver");
+MODULE_AUTHOR("NXP Semiconductor");
+MODULE_LICENSE("GPL");
diff --git a/include/video/imx8-prefetch.h b/include/video/imx8-prefetch.h
new file mode 100644
index 0000000000000000000000000000000000000000..7bbb57499df4cc4d09c7efd00c20189acca967ce
--- /dev/null
+++ b/include/video/imx8-prefetch.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017-2019 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef _IMX8_PREFETCH_H_
+#define _IMX8_PREFETCH_H_
+
+#define PRG_HANDSHAKE_8LINES		8
+#define PRG_HANDSHAKE_4LINES		4
+#define AMPHION_STRIPE_WIDTH		8
+#define AMPHION_STRIPE_HEIGHT		128
+#define AMPHION_UV_STRIPE_HEIGHT	AMPHION_STRIPE_HEIGHT
+#define AMPHION_Y_STRIPE_HEIGHT		(2 * AMPHION_STRIPE_HEIGHT)
+#define VIVANTE_TILE_WIDTH		4
+#define VIVANTE_TILE_HEIGHT		4
+#define VIVANTE_SUPER_TILE_WIDTH	64
+#define VIVANTE_SUPER_TILE_HEIGHT	64
+
+struct prg;
+struct prg *
+prg_lookup_by_phandle(struct device *dev, const char *name, int index);
+void prg_enable(struct prg *prg);
+void prg_disable(struct prg *prg);
+void prg_configure(struct prg *prg, unsigned int width, unsigned int height,
+		   unsigned int x_offset, unsigned int y_offset,
+		   unsigned int stride, unsigned int bits_per_pixel,
+		   unsigned long baddr, u32 format, u64 modifier,
+		   bool start);
+void prg_reg_update(struct prg *prg);
+void prg_shadow_enable(struct prg *prg);
+bool prg_stride_supported(struct prg *prg, unsigned int stride);
+bool prg_stride_double_check(struct prg *prg,
+			     unsigned int width, unsigned int x_offset,
+			     unsigned int bits_per_pixel, u64 modifier,
+			     unsigned int stride, dma_addr_t baddr);
+void prg_set_auxiliary(struct prg *prg);
+void prg_set_primary(struct prg *prg);
+void prg_set_blit(struct prg *prg);
+
+#endif