diff options
author | Liu Ying <victor.liu@nxp.com> | 2017-07-03 11:35:52 +0800 |
---|---|---|
committer | Leonard Crestez <leonard.crestez@nxp.com> | 2018-08-24 12:41:33 +0300 |
commit | c242e43f8874501d861bfa34810ddd9a6fd4beb6 (patch) | |
tree | 04e8090c82e3d18a945ba1fc6d1baaec01fea3ca | |
parent | fd9ae9865ca1f4bfaceded890131fb5b602fe3f8 (diff) |
MLK-15110-2 gpu: imx: Add i.MX8 PRG(Prefetch Resolve Gasket) support
The Pretch Resolve Gasket(PRG) is a digital core function as a gasket
interface between RTRAM controller and DPU. The main function of PRG
is to convert the AXI interface to RTRAM interface and remapping the
ARADDR to a RTRAM address. This patch adds the base driver support
for i.MX8qm/qxp PRG.
Signed-off-by: Liu Ying <victor.liu@nxp.com>
-rw-r--r-- | Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt | 23 | ||||
-rw-r--r-- | drivers/gpu/imx/Kconfig | 5 | ||||
-rw-r--r-- | drivers/gpu/imx/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/imx/imx8_prg.c | 380 | ||||
-rw-r--r-- | include/video/imx8-prefetch.h | 46 |
5 files changed, 456 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 6a5c927ecbb6..da728cc81af4 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -190,6 +190,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 9b1e9502ecdd..a94160290d68 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/dpu/Kconfig source drivers/gpu/imx/dpu-blit/Kconfig diff --git a/drivers/gpu/imx/Makefile b/drivers/gpu/imx/Makefile index d3062986cbdc..fd3cb4757cb8 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_DPU_CORE) += dpu/ obj-$(CONFIG_IMX_DPU_BLIT) += dpu-blit/ diff --git a/drivers/gpu/imx/imx8_prg.c b/drivers/gpu/imx/imx8_prg.c new file mode 100644 index 000000000000..8f2c98cee030 --- /dev/null +++ b/drivers/gpu/imx/imx8_prg.c @@ -0,0 +1,380 @@ +/* + * Copyright 2017 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/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; +}; + +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) +{ + prg_write(prg, SOFTRST, PRG_CTRL + SET); + 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; + u32 val; + + if (WARN_ON(!prg)) + return; + + if (start) + prg_reset(prg); + + /* + * address TKT343664: + * fetch unit base address has to align to burst_size + */ + burst_size = 1 << (ffs(baddr) - 1); + burst_size = min(burst_size, 128U); + + /* + * address TKT339017: + * fixup for burst size vs stride mismatch + */ + 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; + + /* prg finer cropping into tile block - top/left start point */ + switch (modifier) { + case DRM_FORMAT_MOD_NONE: + break; + case DRM_FORMAT_MOD_AMPHION_TILED: + x_offset %= AMPHION_STRIPE_WIDTH; + y_offset %= (prg->is_auxiliary ? + AMPHION_UV_STRIPE_HEIGHT : AMPHION_Y_STRIPE_HEIGHT); + break; + case DRM_FORMAT_MOD_VIVANTE_TILED: + x_offset %= VIVANTE_TILE_WIDTH; + y_offset %= VIVANTE_TILE_HEIGHT; + break; + case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED: + x_offset %= VIVANTE_SUPER_TILE_WIDTH; + y_offset %= VIVANTE_SUPER_TILE_HEIGHT; + break; + default: + dev_err(prg->dev, "unsupported modifier 0x%016llx\n", + modifier); + return; + } + + if (y_offset > + ((format == DRM_FORMAT_NV21 || format == DRM_FORMAT_NV12) ? + (PRG_HANDSHAKE_8LINES - 1) : (PRG_HANDSHAKE_4LINES - 1))) { + dev_err(prg->dev, + "unsupported crop line %d for modifier 0x%016llx\n", + y_offset, modifier); + return; + } + + 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 stride, dma_addr_t baddr) +{ + unsigned int burst_size; + + /* + * address TKT343664: + * fetch unit base address has to align to burst size + */ + burst_size = 1 << (ffs(baddr) - 1); + burst_size = min(burst_size, 128U); + + /* + * address TKT339017: + * fixup for burst size vs stride mismatch + */ + 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); + +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); + return prg; + } + } + mutex_unlock(&prg_list_mutex); + + return NULL; +} +EXPORT_SYMBOL_GPL(prg_lookup_by_phandle); + +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; +} + +static const struct of_device_id prg_dt_ids[] = { + { .compatible = "fsl,imx8qm-prg", }, + { .compatible = "fsl,imx8qxp-prg", }, + { /* sentinel */ }, +}; + +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 000000000000..52ab319b04ea --- /dev/null +++ b/include/video/imx8-prefetch.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 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 stride, dma_addr_t baddr); +void prg_set_auxiliary(struct prg *prg); + +#endif |