Hi all,
this is the first round of patches to enable the Prefetch Resolve Gasket and the Prefetch Resolve Engine as found on the i.MX6 QuadPlus. Basically those units are external extensions to the IPUv3 that are able to prefetch display data from DRAM to an internal SRAM region, transforming the periodic realtime requests from the display FIFOs into larger bursts of normal memory requests, which do mix better with other DRAM traffic in the system.
The PRE can do a number of transformations on the fly, like changing component and plane ordering, as well resolving the Vivante GPU tiling format into linear scanlines. None of those transformations are used right now.
This initial patchset uses the PRG/PRE units as linear prefetchers only. It does however hook up most of the interactions between imx-drm, IPUv3 and PRG/PRE, so that adding those transformations should be an incremental change over that. Also the devicetree binding fully describe the devices, so that no further changes should be necessary.
Regards, Lucas
Lucas Stach (9): gpu: ipu-v3: remove AXI ID setting for IC channel gpu: ipu-v3: add DT binding for the Prefetch Resolve Engine gpu: ipu-v3: add driver for Prefetch Resolve Engine gpu: ipu-v3: add DT binding for the Prefetch Resolve Gasket gpu: ipu-v3: add driver for Prefetch Resolve Gasket gpu: ipu-v3: extend the IPUv3 DT binding for i.MX6 QuadPlus gpu: ipu-v3: hook up PRG unit drm/imx: enable/disable PRG on CRTC enable/disable drm/imx: use PRG/PRE when possible
.../bindings/display/imx/fsl-imx-drm.txt | 53 +++ drivers/gpu/drm/imx/imx-drm-core.c | 5 + drivers/gpu/drm/imx/imx-drm.h | 3 + drivers/gpu/drm/imx/ipuv3-crtc.c | 2 + drivers/gpu/drm/imx/ipuv3-plane.c | 122 +++++- drivers/gpu/ipu-v3/Makefile | 2 +- drivers/gpu/ipu-v3/ipu-common.c | 7 + drivers/gpu/ipu-v3/ipu-image-convert.c | 2 - drivers/gpu/ipu-v3/ipu-pre.c | 290 +++++++++++++++ drivers/gpu/ipu-v3/ipu-prg.c | 413 +++++++++++++++++++++ drivers/gpu/ipu-v3/ipu-prv.h | 15 + include/video/imx-ipu-v3.h | 15 + 12 files changed, 923 insertions(+), 6 deletions(-) create mode 100644 drivers/gpu/ipu-v3/ipu-pre.c create mode 100644 drivers/gpu/ipu-v3/ipu-prg.c
This is a pretty minor optimization for the IC channel to get out-of-order AXI returns, but clashes with the AXI ID assignment that needs to be done for the display channels on QuadPlus.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- drivers/gpu/ipu-v3/ipu-image-convert.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index 805b6fa7b5f4..5e3cc6bd98fc 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -671,8 +671,6 @@ static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, ipu_ic_task_idma_init(chan->ic, channel, width, height, burst_size, rot_mode);
- ipu_cpmem_set_axi_id(channel, 1); - ipu_idmac_set_double_buffer(channel, ctx->double_buffering); }
On Fri, 2017-02-17 at 19:28 +0100, Lucas Stach wrote:
This is a pretty minor optimization for the IC channel to get out-of-order AXI returns, but clashes with the AXI ID assignment that needs to be done for the display channels on QuadPlus.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
drivers/gpu/ipu-v3/ipu-image-convert.c | 2 -- 1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c index 805b6fa7b5f4..5e3cc6bd98fc 100644 --- a/drivers/gpu/ipu-v3/ipu-image-convert.c +++ b/drivers/gpu/ipu-v3/ipu-image-convert.c @@ -671,8 +671,6 @@ static void init_idmac_channel(struct ipu_image_convert_ctx *ctx, ipu_ic_task_idma_init(chan->ic, channel, width, height, burst_size, rot_mode);
- ipu_cpmem_set_axi_id(channel, 1);
The IC IDMAC channels were set to AXI ID 1 to avoid starving the scanout channels. AXI ID 0 is set to the highest QoS priority in imx6q_axi_init, AXI ID 1 is set slightly lower for that reason. This should be kept for all the non-plus models.
ipu_idmac_set_double_buffer(channel, ctx->double_buffering); }
regards Philipp
The Prefetch Resolve Engine is a prefetch and tile resolve engine which prefetches display data from DRAM to an internal SRAM region. It has a single clock for configuration register access and the functional units. A single shared interrupt is used for status and error signaling.
The only external dependency is the SRAM region to use for the prefetch double buffer.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- .../bindings/display/imx/fsl-imx-drm.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 971c3eedb1c7..1bd777d7c37d 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -53,6 +53,32 @@ ipu: ipu@18000000 { }; };
+Freescale i.MX PRE (Prefetch Resolve Engine) +============================================ + +Required properties: +- compatible: should be "fsl,imx6qp-pre" +- reg: should be register base and length as documented in the + datasheet +- clocks : phandle to the PRE axi clock input, as described + in Documentation/devicetree/bindings/clock/clock-bindings.txt and + Documentation/devicetree/bindings/clock/imx6q-clock.txt. +- clock-names: should be "axi" +- interrupts: should contain the PRE interrupt +- fsl,ocram: phandle pointing to the mmio-sram device node, that should be + used for the PRE SRAM double buffer. + +example: + +pre@021c8000 { + compatible = "fsl,imx6qp-pre"; + reg = <0x021c8000 0x1000>; + interrupts = <GIC_SPI 90 IRQ_TYPE_EDGE_RISING>; + clocks = <&clks IMX6QDL_CLK_PRE0>; + clock-names = "axi"; + fsl,ocram = <&ocram2>; +}; + Parallel display support ========================
On Fri, 2017-02-17 at 19:28 +0100, Lucas Stach wrote:
The Prefetch Resolve Engine is a prefetch and tile resolve engine which prefetches display data from DRAM to an internal SRAM region. It has a single clock for configuration register access and the functional units. A single shared interrupt is used for status and error signaling.
The only external dependency is the SRAM region to use for the prefetch double buffer.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
.../bindings/display/imx/fsl-imx-drm.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 971c3eedb1c7..1bd777d7c37d 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -53,6 +53,32 @@ ipu: ipu@18000000 { }; };
+Freescale i.MX PRE (Prefetch Resolve Engine) +============================================
+Required properties: +- compatible: should be "fsl,imx6qp-pre" +- reg: should be register base and length as documented in the
- datasheet
+- clocks : phandle to the PRE axi clock input, as described
- in Documentation/devicetree/bindings/clock/clock-bindings.txt and
- Documentation/devicetree/bindings/clock/imx6q-clock.txt.
+- clock-names: should be "axi" +- interrupts: should contain the PRE interrupt +- fsl,ocram: phandle pointing to the mmio-sram device node, that should be
- used for the PRE SRAM double buffer.
+example:
+pre@021c8000 {
- compatible = "fsl,imx6qp-pre";
- reg = <0x021c8000 0x1000>;
- interrupts = <GIC_SPI 90 IRQ_TYPE_EDGE_RISING>;
- clocks = <&clks IMX6QDL_CLK_PRE0>;
- clock-names = "axi";
- fsl,ocram = <&ocram2>;
Nitpick: the CODA property for this is called iram. Should we do the same here in case the PRE is later used on other SoCs that call their on-chip SRAM differently? There is no mention of OCRAM in the PRE chapter in the i.MX6QP reference manual, but Figures 38-1 and 38-5 mention an "IRAM Double Buffer".
+};
Parallel display support
regards Philipp
On Fri, Feb 17, 2017 at 07:28:23PM +0100, Lucas Stach wrote:
The Prefetch Resolve Engine is a prefetch and tile resolve engine which prefetches display data from DRAM to an internal SRAM region. It has a single clock for configuration register access and the functional units. A single shared interrupt is used for status and error signaling.
The only external dependency is the SRAM region to use for the prefetch double buffer.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
.../bindings/display/imx/fsl-imx-drm.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 971c3eedb1c7..1bd777d7c37d 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -53,6 +53,32 @@ ipu: ipu@18000000 { }; };
+Freescale i.MX PRE (Prefetch Resolve Engine) +============================================
+Required properties: +- compatible: should be "fsl,imx6qp-pre" +- reg: should be register base and length as documented in the
- datasheet
+- clocks : phandle to the PRE axi clock input, as described
- in Documentation/devicetree/bindings/clock/clock-bindings.txt and
- Documentation/devicetree/bindings/clock/imx6q-clock.txt.
+- clock-names: should be "axi" +- interrupts: should contain the PRE interrupt +- fsl,ocram: phandle pointing to the mmio-sram device node, that should be
- used for the PRE SRAM double buffer.
+example:
+pre@021c8000 {
Drop the leading 0.
- compatible = "fsl,imx6qp-pre";
- reg = <0x021c8000 0x1000>;
- interrupts = <GIC_SPI 90 IRQ_TYPE_EDGE_RISING>;
- clocks = <&clks IMX6QDL_CLK_PRE0>;
- clock-names = "axi";
- fsl,ocram = <&ocram2>;
+};
Parallel display support
-- 2.11.0
This adds support for the i.MX6 QuadPlus PRE units. Currently only linear prefetch into SRAM is supported, other modes of operation like the tiled-to-linear conversion will be added later.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- drivers/gpu/ipu-v3/Makefile | 2 +- drivers/gpu/ipu-v3/ipu-pre.c | 290 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/ipu-v3/ipu-prv.h | 11 ++ 3 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/ipu-v3/ipu-pre.c
diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 5f961416c4ee..8ae90de46b4d 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ - ipu-smfc.o ipu-vdi.o + ipu-pre.o ipu-smfc.o ipu-vdi.o diff --git a/drivers/gpu/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu-pre.c new file mode 100644 index 000000000000..febe0cb8b094 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-pre.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2017 Lucas Stach, Pengutronix + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/err.h> +#include <linux/genalloc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <video/imx-ipu-v3.h> + +#include "ipu-prv.h" + +#define IPU_PRE_MAX_WIDTH 2048 +#define IPU_PRE_NUM_SCANLINES 8 + +#define IPU_PRE_CTRL 0x000 +#define IPU_PRE_CTRL_SET 0x004 +#define IPU_PRE_CTRL_ENABLE (1 << 0) +#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) +#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) +#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) +#define IPU_PRE_CTRL_VFLIP (1 << 5) +#define IPU_PRE_CTRL_SO (1 << 6) +#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) +#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) +#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) +#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) +#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) +#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) +#define IPU_PRE_CTRL_CLKGATE (1 << 30) +#define IPU_PRE_CTRL_SFTRST (1 << 31) + +#define IPU_PRE_CUR_BUF 0x030 + +#define IPU_PRE_NEXT_BUF 0x040 + +#define IPU_PRE_TPR_CTRL 0x070 +#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) +#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff + +#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 +#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) +#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) +#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) +#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) +#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) +#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) +#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) +#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15) + +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16) + +#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 +#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) +#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16) + +#define IPU_PRE_STORE_ENG_CTRL 0x110 +#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) +#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) +#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) + +#define IPU_PRE_STORE_ENG_SIZE 0x130 +#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) +#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16) + +#define IPU_PRE_STORE_ENG_PITCH 0x140 +#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0) + +#define IPU_PRE_STORE_ENG_ADDR 0x150 + +struct ipu_pre { + struct list_head list; + struct device *dev; + + void __iomem *regs; + struct clk *clk_axi; + struct gen_pool *ocram; + + dma_addr_t buffer_paddr; + void *buffer_virt; + bool in_use; +}; + +static DEFINE_MUTEX(ipu_pre_list_mutex); +static LIST_HEAD(ipu_pre_list); +static int available_pres; + +int ipu_pre_get_available_count(void) +{ + return available_pres; +} + +struct ipu_pre * +ipu_pre_get_by_prg_device(struct device *dev, int index) +{ + struct device_node *pre_node = of_parse_phandle(dev->of_node, + "fsl,pres", index); + struct ipu_pre *pre; + + mutex_lock(&ipu_pre_list_mutex); + list_for_each_entry(pre, &ipu_pre_list, list) { + if (pre_node == pre->dev->of_node) { + mutex_unlock(&ipu_pre_list_mutex); + device_link_add(dev, pre->dev, DL_FLAG_AUTOREMOVE); + return pre; + } + } + mutex_unlock(&ipu_pre_list_mutex); + + return NULL; +} + +int ipu_pre_get(struct ipu_pre *pre) +{ + u32 val; + + if (pre->in_use) + return -EBUSY; + + clk_prepare_enable(pre->clk_axi); + + /* first get the engine out of reset and remove clock gating */ + writel(0, pre->regs + IPU_PRE_CTRL); + + /* init defaults that should be applied to all streams */ + val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN | + IPU_PRE_CTRL_HANDSHAKE_EN | + IPU_PRE_CTRL_TPR_REST_SEL | + IPU_PRE_CTRL_BLOCK_16 | IPU_PRE_CTRL_SDW_UPDATE; + writel(val, pre->regs + IPU_PRE_CTRL); + + pre->in_use = true; + return 0; +} + +void ipu_pre_put(struct ipu_pre *pre) +{ + u32 val; + + val = IPU_PRE_CTRL_SFTRST | IPU_PRE_CTRL_CLKGATE; + writel(val, pre->regs + IPU_PRE_CTRL); + + clk_disable_unprepare(pre->clk_axi); + + pre->in_use = false; +} + +void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, + unsigned int height, unsigned int stride, u32 format, + unsigned int bufaddr) +{ + const struct drm_format_info *info = drm_format_info(format); + u32 active_bpp = info->cpp[0] >> 1; + u32 val; + + writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF); + writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); + + val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) | + IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) | + IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) | + IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS | + IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN; + writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL); + + val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) | + IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height); + writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE); + + val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride); + writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH); + + val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) | + IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) | + IPU_PRE_STORE_ENG_CTRL_STORE_EN; + writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL); + + val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) | + IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height); + writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE); + + val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride); + writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH); + + writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR); + + val = readl(pre->regs + IPU_PRE_CTRL); + val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE | + IPU_PRE_CTRL_SDW_UPDATE; + writel(val, pre->regs + IPU_PRE_CTRL); +} + +void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) +{ + writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF); + writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET); +} + +u32 ipu_pre_get_baddr(struct ipu_pre *pre) +{ + return (u32)pre->buffer_paddr; +} + +static int ipu_pre_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct ipu_pre *pre; + + pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); + if (!pre) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pre->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pre->regs)) + return PTR_ERR(pre->regs); + + pre->clk_axi = devm_clk_get(dev, "axi"); + if (IS_ERR(pre->clk_axi)) + return PTR_ERR(pre->clk_axi); + + pre->ocram = of_gen_pool_get(dev->of_node, "fsl,ocram", 0); + if (!pre->ocram) + return -EPROBE_DEFER; + + /* + * Allocate OCRAM buffer with maximum size. This could be made dynamic, + * but as there is no other user of this OCRAM region and we can fit all + * max sized buffers into it, there is no need yet. + */ + pre->buffer_virt = gen_pool_dma_alloc(pre->ocram, IPU_PRE_MAX_WIDTH * + IPU_PRE_NUM_SCANLINES * 4, + &pre->buffer_paddr); + if (!pre->buffer_virt) + return -ENOMEM; + + pre->dev = dev; + platform_set_drvdata(pdev, pre); + mutex_lock(&ipu_pre_list_mutex); + list_add(&pre->list, &ipu_pre_list); + available_pres++; + mutex_unlock(&ipu_pre_list_mutex); + + return 0; +} + +static int ipu_pre_remove(struct platform_device *pdev) +{ + struct ipu_pre *pre = platform_get_drvdata(pdev); + + mutex_lock(&ipu_pre_list_mutex); + list_del(&pre->list); + available_pres--; + mutex_unlock(&ipu_pre_list_mutex); + + if (pre->buffer_virt) + gen_pool_free(pre->ocram, (unsigned long)pre->buffer_virt, + IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4); + return 0; +} + +static const struct of_device_id ipu_pre_dt_ids[] = { + { .compatible = "fsl,imx6qp-pre", }, + { /* sentinel */ }, +}; + +static struct platform_driver ipu_pre_drv = { + .probe = ipu_pre_probe, + .remove = ipu_pre_remove, + .driver = { + .name = "imx-ipu-pre", + .of_match_table = ipu_pre_dt_ids, + }, +}; +module_platform_driver(ipu_pre_drv); diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index 22e47b68b14a..536a156a5eb6 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -168,6 +168,7 @@ struct ipu_ic_priv; struct ipu_vdi; struct ipu_image_convert_priv; struct ipu_smfc_priv; +struct ipu_pre;
struct ipu_devtype;
@@ -259,4 +260,14 @@ void ipu_cpmem_exit(struct ipu_soc *ipu); int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); void ipu_smfc_exit(struct ipu_soc *ipu);
+struct ipu_pre *ipu_pre_get_by_prg_device(struct device *dev, int index); +int ipu_pre_get_available_count(void); +int ipu_pre_get(struct ipu_pre *pre); +void ipu_pre_put(struct ipu_pre *pre); +u32 ipu_pre_get_baddr(struct ipu_pre *pre); +void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, + unsigned int height, + unsigned int stride, u32 format, unsigned int bufaddr); +void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr); + #endif /* __IPU_PRV_H__ */
On Fri, 2017-02-17 at 19:28 +0100, Lucas Stach wrote:
This adds support for the i.MX6 QuadPlus PRE units. Currently only linear prefetch into SRAM is supported, other modes of operation like the tiled-to-linear conversion will be added later.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
drivers/gpu/ipu-v3/Makefile | 2 +- drivers/gpu/ipu-v3/ipu-pre.c | 290 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/ipu-v3/ipu-prv.h | 11 ++ 3 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/ipu-v3/ipu-pre.c
diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 5f961416c4ee..8ae90de46b4d 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \
ipu-smfc.o ipu-vdi.o
ipu-pre.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu-pre.c new file mode 100644 index 000000000000..febe0cb8b094 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-pre.c @@ -0,0 +1,290 @@ +/*
- Copyright (c) 2017 Lucas Stach, Pengutronix
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
- This program is distributed in the hope 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/err.h> +#include <linux/genalloc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+#define IPU_PRE_MAX_WIDTH 2048 +#define IPU_PRE_NUM_SCANLINES 8
+#define IPU_PRE_CTRL 0x000 +#define IPU_PRE_CTRL_SET 0x004 +#define IPU_PRE_CTRL_ENABLE (1 << 0)
Single bit fields could use BIT(0) etc.
+#define IPU_PRE_CTRL_BLOCK_EN (1 << 1) +#define IPU_PRE_CTRL_BLOCK_16 (1 << 2) +#define IPU_PRE_CTRL_SDW_UPDATE (1 << 4) +#define IPU_PRE_CTRL_VFLIP (1 << 5) +#define IPU_PRE_CTRL_SO (1 << 6) +#define IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) +#define IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) +#define IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) +#define IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) +#define IPU_PRE_CTRL_EN_REPEAT (1 << 28) +#define IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) +#define IPU_PRE_CTRL_CLKGATE (1 << 30) +#define IPU_PRE_CTRL_SFTRST (1 << 31)
+#define IPU_PRE_CUR_BUF 0x030
+#define IPU_PRE_NEXT_BUF 0x040
+#define IPU_PRE_TPR_CTRL 0x070 +#define IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) +#define IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff
+#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 +#define IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) +#define IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) +#define IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) +#define IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) +#define IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) +#define IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) +#define IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) +#define IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15)
+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0a0 +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) +#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16)
+#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 +#define IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) +#define IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16)
+#define IPU_PRE_STORE_ENG_CTRL 0x110 +#define IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) +#define IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) +#define IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)
+#define IPU_PRE_STORE_ENG_SIZE 0x130 +#define IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) +#define IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16)
+#define IPU_PRE_STORE_ENG_PITCH 0x140 +#define IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0)
+#define IPU_PRE_STORE_ENG_ADDR 0x150
+struct ipu_pre {
- struct list_head list;
- struct device *dev;
- void __iomem *regs;
- struct clk *clk_axi;
- struct gen_pool *ocram;
- dma_addr_t buffer_paddr;
- void *buffer_virt;
- bool in_use;
+};
+static DEFINE_MUTEX(ipu_pre_list_mutex); +static LIST_HEAD(ipu_pre_list); +static int available_pres;
+int ipu_pre_get_available_count(void) +{
- return available_pres;
+}
+struct ipu_pre * +ipu_pre_get_by_prg_device(struct device *dev, int index)
I'd call this ipu_pre_lookup_by_phandle and pass the device_node and property name string parameter. That way the name is better separated from ipu_pre_get ...
+{
- struct device_node *pre_node = of_parse_phandle(dev->of_node,
"fsl,pres", index);
... and the PRG specific "fsl,pres" string could move into the PRG driver.
- struct ipu_pre *pre;
- mutex_lock(&ipu_pre_list_mutex);
- list_for_each_entry(pre, &ipu_pre_list, list) {
if (pre_node == pre->dev->of_node) {
mutex_unlock(&ipu_pre_list_mutex);
device_link_add(dev, pre->dev, DL_FLAG_AUTOREMOVE);
return pre;
}
- }
- mutex_unlock(&ipu_pre_list_mutex);
- return NULL;
+}
+int ipu_pre_get(struct ipu_pre *pre) +{
- u32 val;
- if (pre->in_use)
return -EBUSY;
This could race for in_use ...
- clk_prepare_enable(pre->clk_axi);
- /* first get the engine out of reset and remove clock gating */
- writel(0, pre->regs + IPU_PRE_CTRL);
- /* init defaults that should be applied to all streams */
- val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN |
IPU_PRE_CTRL_HANDSHAKE_EN |
IPU_PRE_CTRL_TPR_REST_SEL |
IPU_PRE_CTRL_BLOCK_16 | IPU_PRE_CTRL_SDW_UPDATE;
- writel(val, pre->regs + IPU_PRE_CTRL);
- pre->in_use = true;
... until here.
- return 0;
+}
+void ipu_pre_put(struct ipu_pre *pre) +{
- u32 val;
- val = IPU_PRE_CTRL_SFTRST | IPU_PRE_CTRL_CLKGATE;
- writel(val, pre->regs + IPU_PRE_CTRL);
- clk_disable_unprepare(pre->clk_axi);
- pre->in_use = false;
+}
+void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
unsigned int height, unsigned int stride, u32 format,
unsigned int bufaddr)
+{
- const struct drm_format_info *info = drm_format_info(format);
- u32 active_bpp = info->cpp[0] >> 1;
- u32 val;
- writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
- val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |
IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |
IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) |
IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS |
IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN;
- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL);
- val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) |
IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height);
- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE);
- val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride);
- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH);
- val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp) |
IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) |
IPU_PRE_STORE_ENG_CTRL_STORE_EN;
- writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL);
- val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) |
IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height);
- writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE);
- val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride);
- writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH);
- writel(pre->buffer_paddr, pre->regs + IPU_PRE_STORE_ENG_ADDR);
- val = readl(pre->regs + IPU_PRE_CTRL);
- val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE |
IPU_PRE_CTRL_SDW_UPDATE;
- writel(val, pre->regs + IPU_PRE_CTRL);
+}
+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) +{
- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
- writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs + IPU_PRE_CTRL_SET);
+}
+u32 ipu_pre_get_baddr(struct ipu_pre *pre) +{
- return (u32)pre->buffer_paddr;
+}
+static int ipu_pre_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct ipu_pre *pre;
- pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL);
- if (!pre)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pre->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pre->regs))
return PTR_ERR(pre->regs);
- pre->clk_axi = devm_clk_get(dev, "axi");
- if (IS_ERR(pre->clk_axi))
return PTR_ERR(pre->clk_axi);
- pre->ocram = of_gen_pool_get(dev->of_node, "fsl,ocram", 0);
- if (!pre->ocram)
return -EPROBE_DEFER;
I'd call this iram, from the point of view of the PRE.
- /*
* Allocate OCRAM buffer with maximum size. This could be made dynamic,
* but as there is no other user of this OCRAM region and we can fit all
* max sized buffers into it, there is no need yet.
*/
- pre->buffer_virt = gen_pool_dma_alloc(pre->ocram, IPU_PRE_MAX_WIDTH *
IPU_PRE_NUM_SCANLINES * 4,
&pre->buffer_paddr);
- if (!pre->buffer_virt)
return -ENOMEM;
- pre->dev = dev;
- platform_set_drvdata(pdev, pre);
- mutex_lock(&ipu_pre_list_mutex);
- list_add(&pre->list, &ipu_pre_list);
- available_pres++;
- mutex_unlock(&ipu_pre_list_mutex);
- return 0;
+}
+static int ipu_pre_remove(struct platform_device *pdev) +{
- struct ipu_pre *pre = platform_get_drvdata(pdev);
- mutex_lock(&ipu_pre_list_mutex);
- list_del(&pre->list);
- available_pres--;
- mutex_unlock(&ipu_pre_list_mutex);
- if (pre->buffer_virt)
gen_pool_free(pre->ocram, (unsigned long)pre->buffer_virt,
IPU_PRE_MAX_WIDTH * IPU_PRE_NUM_SCANLINES * 4);
- return 0;
+}
+static const struct of_device_id ipu_pre_dt_ids[] = {
- { .compatible = "fsl,imx6qp-pre", },
- { /* sentinel */ },
+};
+static struct platform_driver ipu_pre_drv = {
- .probe = ipu_pre_probe,
- .remove = ipu_pre_remove,
- .driver = {
.name = "imx-ipu-pre",
.of_match_table = ipu_pre_dt_ids,
- },
+}; +module_platform_driver(ipu_pre_drv); diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index 22e47b68b14a..536a156a5eb6 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -168,6 +168,7 @@ struct ipu_ic_priv; struct ipu_vdi; struct ipu_image_convert_priv; struct ipu_smfc_priv; +struct ipu_pre;
struct ipu_devtype;
@@ -259,4 +260,14 @@ void ipu_cpmem_exit(struct ipu_soc *ipu); int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); void ipu_smfc_exit(struct ipu_soc *ipu);
+struct ipu_pre *ipu_pre_get_by_prg_device(struct device *dev, int index); +int ipu_pre_get_available_count(void); +int ipu_pre_get(struct ipu_pre *pre); +void ipu_pre_put(struct ipu_pre *pre); +u32 ipu_pre_get_baddr(struct ipu_pre *pre); +void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
unsigned int height,
unsigned int stride, u32 format, unsigned int bufaddr);
+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr);
#endif /* __IPU_PRV_H__ */
regards Philipp
Am Montag, den 20.02.2017, 16:20 +0100 schrieb Philipp Zabel:
On Fri, 2017-02-17 at 19:28 +0100, Lucas Stach wrote:
This adds support for the i.MX6 QuadPlus PRE units. Currently only linear prefetch into SRAM is supported, other modes of operation like the tiled-to-linear conversion will be added later.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
drivers/gpu/ipu-v3/Makefile  |   2 +-  drivers/gpu/ipu-v3/ipu-pre.c | 290 +++++++++++++++++++++++++++++++++++++++++++  drivers/gpu/ipu-v3/ipu-prv.h |  11 ++  3 files changed, 302 insertions(+), 1 deletion(-)  create mode 100644 drivers/gpu/ipu-v3/ipu-pre.c
diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu- v3/Makefile index 5f961416c4ee..8ae90de46b4d 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o   imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \  ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \
ipu-smfc.o ipu-vdi.o
ipu-pre.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu- pre.c new file mode 100644 index 000000000000..febe0cb8b094 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-pre.c @@ -0,0 +1,290 @@ +/*
- Copyright (c) 2017 Lucas Stach, Pengutronix
- This program is free software; you can redistribute it and/or
modify it
- under the terms and conditions of the GNU General Public
License,
- version 2, as published by the Free Software Foundation.
- This program is distributed in the hope 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/err.h> +#include <linux/genalloc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+#define IPU_PRE_MAX_WIDTH 2048 +#define IPU_PRE_NUM_SCANLINES 8
+#define IPU_PRE_CTRL 0x000 +#define IPU_PRE_CTRL_SET 0x004 +#define  IPU_PRE_CTRL_ENABLE (1 << 0)
Single bit fields could use BIT(0) etc.
+#define  IPU_PRE_CTRL_BLOCK_EN (1 << 1) +#define  IPU_PRE_CTRL_BLOCK_16 (1 << 2) +#define  IPU_PRE_CTRL_SDW_UPDATE (1 << 4) +#define  IPU_PRE_CTRL_VFLIP (1 << 5) +#define  IPU_PRE_CTRL_SO (1 << 6) +#define  IPU_PRE_CTRL_INTERLACED_FIELD (1 << 7) +#define  IPU_PRE_CTRL_HANDSHAKE_EN (1 << 8) +#define  IPU_PRE_CTRL_HANDSHAKE_LINE_NUM(v) ((v & 0x3) << 9) +#define  IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN (1 << 11) +#define  IPU_PRE_CTRL_EN_REPEAT (1 << 28) +#define  IPU_PRE_CTRL_TPR_REST_SEL (1 << 29) +#define  IPU_PRE_CTRL_CLKGATE (1 << 30) +#define  IPU_PRE_CTRL_SFTRST (1 << 31)
+#define IPU_PRE_CUR_BUF 0x0 30
+#define IPU_PRE_NEXT_BUF 0x040
+#define IPU_PRE_TPR_CTRL 0x070 +#define  IPU_PRE_TPR_CTRL_TILE_FORMAT(v) ((v & 0xff) << 0) +#define  IPU_PRE_TPR_CTRL_TILE_FORMAT_MASK 0xff
+#define IPU_PRE_PREFETCH_ENG_CTRL 0x080 +#define  IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN (1 << 0) +#define  IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(v) ((v & 0x7) << 1) +#define  IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(v) ((v & 0x3) << 4) +#define  IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(v) ((v & 0x7) << 8) +#define  IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS (1 << 11) +#define  IPU_PRE_PREF_ENG_CTRL_FIELD_INVERSE (1 << 12) +#define  IPU_PRE_PREF_ENG_CTRL_PARTIAL_UV_SWAP (1 << 14) +#define  IPU_PRE_PREF_ENG_CTRL_TPR_COOR_OFFSET_EN (1 << 15)
+#define IPU_PRE_PREFETCH_ENG_INPUT_SIZE 0x0 a0 +#define  IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(v) ((v & 0xffff) << 0) +#define  IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(v) ((v & 0xffff) << 16)
+#define IPU_PRE_PREFETCH_ENG_PITCH 0x0d0 +#define  IPU_PRE_PREFETCH_ENG_PITCH_Y(v) ((v & 0xffff) << 0) +#define  IPU_PRE_PREFETCH_ENG_PITCH_UV(v) ((v & 0xffff) << 16)
+#define IPU_PRE_STORE_ENG_CTRL 0x11 0 +#define  IPU_PRE_STORE_ENG_CTRL_STORE_EN (1 << 0) +#define  IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(v) ((v & 0x7) << 1) +#define  IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(v) ((v & 0x3) << 4)
+#define IPU_PRE_STORE_ENG_SIZE 0x13 0 +#define  IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(v) ((v & 0xffff) << 0) +#define  IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(v) ((v & 0xffff) << 16)
+#define IPU_PRE_STORE_ENG_PITCH 0x1 40 +#define  IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(v) ((v & 0xffff) << 0)
+#define IPU_PRE_STORE_ENG_ADDR 0x15 0
+struct ipu_pre {
- struct list_head list;
- struct device *dev;
- void __iomem *regs;
- struct clk *clk_axi;
- struct gen_pool *ocram;
- dma_addr_t buffer_paddr;
- void *buffer_virt;
- bool in_use;
+};
+static DEFINE_MUTEX(ipu_pre_list_mutex); +static LIST_HEAD(ipu_pre_list); +static int available_pres;
+int ipu_pre_get_available_count(void) +{
- return available_pres;
+}
+struct ipu_pre * +ipu_pre_get_by_prg_device(struct device *dev, int index)
I'd call this ipu_pre_lookup_by_phandle and pass the device_node and property name string parameter. That way the name is better separated from ipu_pre_get ...
+{
- struct device_node *pre_node = of_parse_phandle(dev-
of_node,
"fsl,pres"
, index);
... and the PRG specific "fsl,pres" string could move into the PRG driver.
Good point, will do.
- struct ipu_pre *pre;
- mutex_lock(&ipu_pre_list_mutex);
- list_for_each_entry(pre, &ipu_pre_list, list) {
if (pre_node == pre->dev->of_node) {
mutex_unlock(&ipu_pre_list_mutex);
device_link_add(dev, pre->dev,
DL_FLAG_AUTOREMOVE);
return pre;
}
- }
- mutex_unlock(&ipu_pre_list_mutex);
- return NULL;
+}
+int ipu_pre_get(struct ipu_pre *pre) +{
- u32 val;
- if (pre->in_use)
return -EBUSY;
This could race for in_use ...
All the PRE/PRG configuration functions are thread unsafe, with the premise that they are only used during an atomic commit, which is inherently single threaded. I would rather like to avoid introducing unnecessary thread safety here.
- clk_prepare_enable(pre->clk_axi);
- /* first get the engine out of reset and remove clock
gating */
- writel(0, pre->regs + IPU_PRE_CTRL);
- /* init defaults that should be applied to all streams */
- val = IPU_PRE_CTRL_HANDSHAKE_ABORT_SKIP_EN |
- IPU_PRE_CTRL_HANDSHAKE_EN |
- IPU_PRE_CTRL_TPR_REST_SEL |
- IPU_PRE_CTRL_BLOCK_16 | IPU_PRE_CTRL_SDW_UPDATE;
- writel(val, pre->regs + IPU_PRE_CTRL);
- pre->in_use = true;
... until here.
- return 0;
+}
+void ipu_pre_put(struct ipu_pre *pre) +{
- u32 val;
- val = IPU_PRE_CTRL_SFTRST | IPU_PRE_CTRL_CLKGATE;
- writel(val, pre->regs + IPU_PRE_CTRL);
- clk_disable_unprepare(pre->clk_axi);
- pre->in_use = false;
+}
+void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
       unsigned int height, unsigned int stride,
u32 format,
       unsigned int bufaddr)
+{
- const struct drm_format_info *info =
drm_format_info(format);
- u32 active_bpp = info->cpp[0] >> 1;
- u32 val;
- writel(bufaddr, pre->regs + IPU_PRE_CUR_BUF);
- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
- val = IPU_PRE_PREF_ENG_CTRL_INPUT_PIXEL_FORMAT(0) |
- IPU_PRE_PREF_ENG_CTRL_INPUT_ACTIVE_BPP(active_bpp) |
- IPU_PRE_PREF_ENG_CTRL_RD_NUM_BYTES(4) |
- IPU_PRE_PREF_ENG_CTRL_SHIFT_BYPASS |
- IPU_PRE_PREF_ENG_CTRL_PREFETCH_EN;
- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_CTRL);
- val = IPU_PRE_PREFETCH_ENG_INPUT_SIZE_WIDTH(width) |
- IPU_PRE_PREFETCH_ENG_INPUT_SIZE_HEIGHT(height);
- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_INPUT_SIZE);
- val = IPU_PRE_PREFETCH_ENG_PITCH_Y(stride);
- writel(val, pre->regs + IPU_PRE_PREFETCH_ENG_PITCH);
- val = IPU_PRE_STORE_ENG_CTRL_OUTPUT_ACTIVE_BPP(active_bpp)
|
- IPU_PRE_STORE_ENG_CTRL_WR_NUM_BYTES(4) |
- IPU_PRE_STORE_ENG_CTRL_STORE_EN;
- writel(val, pre->regs + IPU_PRE_STORE_ENG_CTRL);
- val = IPU_PRE_STORE_ENG_SIZE_INPUT_WIDTH(width) |
- IPU_PRE_STORE_ENG_SIZE_INPUT_HEIGHT(height);
- writel(val, pre->regs + IPU_PRE_STORE_ENG_SIZE);
- val = IPU_PRE_STORE_ENG_PITCH_OUT_PITCH(stride);
- writel(val, pre->regs + IPU_PRE_STORE_ENG_PITCH);
- writel(pre->buffer_paddr, pre->regs +
IPU_PRE_STORE_ENG_ADDR);
- val = readl(pre->regs + IPU_PRE_CTRL);
- val |= IPU_PRE_CTRL_EN_REPEAT | IPU_PRE_CTRL_ENABLE |
- IPU_PRE_CTRL_SDW_UPDATE;
- writel(val, pre->regs + IPU_PRE_CTRL);
+}
+void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr) +{
- writel(bufaddr, pre->regs + IPU_PRE_NEXT_BUF);
- writel(IPU_PRE_CTRL_SDW_UPDATE, pre->regs +
IPU_PRE_CTRL_SET); +}
+u32 ipu_pre_get_baddr(struct ipu_pre *pre) +{
- return (u32)pre->buffer_paddr;
+}
+static int ipu_pre_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct ipu_pre *pre;
- pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL);
- if (!pre)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pre->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pre->regs))
return PTR_ERR(pre->regs);
- pre->clk_axi = devm_clk_get(dev, "axi");
- if (IS_ERR(pre->clk_axi))
return PTR_ERR(pre->clk_axi);
- pre->ocram = of_gen_pool_get(dev->of_node, "fsl,ocram",
0);
- if (!pre->ocram)
return -EPROBE_DEFER;
I'd call this iram, from the point of view of the PRE.
- /*
- * Allocate OCRAM buffer with maximum size. This could be
made dynamic,
- * but as there is no other user of this OCRAM region and
we can fit all
- * max sized buffers into it, there is no need yet.
- */
- pre->buffer_virt = gen_pool_dma_alloc(pre->ocram,
IPU_PRE_MAX_WIDTH *
      IPU_PRE_NUM_SCANLINE
S * 4,
      &pre->buffer_paddr);
- if (!pre->buffer_virt)
return -ENOMEM;
- pre->dev = dev;
- platform_set_drvdata(pdev, pre);
- mutex_lock(&ipu_pre_list_mutex);
- list_add(&pre->list, &ipu_pre_list);
- available_pres++;
- mutex_unlock(&ipu_pre_list_mutex);
- return 0;
+}
+static int ipu_pre_remove(struct platform_device *pdev) +{
- struct ipu_pre *pre = platform_get_drvdata(pdev);
- mutex_lock(&ipu_pre_list_mutex);
- list_del(&pre->list);
- available_pres--;
- mutex_unlock(&ipu_pre_list_mutex);
- if (pre->buffer_virt)
gen_pool_free(pre->ocram, (unsigned long)pre-
buffer_virt,
      IPU_PRE_MAX_WIDTH *
IPU_PRE_NUM_SCANLINES * 4);
- return 0;
+}
+static const struct of_device_id ipu_pre_dt_ids[] = {
- { .compatible = "fsl,imx6qp-pre", },
- { /* sentinel */ },
+};
+static struct platform_driver ipu_pre_drv = {
- .probe = ipu_pre_probe,
- .remove = ipu_pre_remove,
- .driver = {
.name = "imx-ipu-pre",
.of_match_table = ipu_pre_dt_ids,
- },
+}; +module_platform_driver(ipu_pre_drv); diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu- prv.h index 22e47b68b14a..536a156a5eb6 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -168,6 +168,7 @@ struct ipu_ic_priv; Â struct ipu_vdi; Â struct ipu_image_convert_priv; Â struct ipu_smfc_priv; +struct ipu_pre; Â Â struct ipu_devtype; Â @@ -259,4 +260,14 @@ void ipu_cpmem_exit(struct ipu_soc *ipu); Â int ipu_smfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base); Â void ipu_smfc_exit(struct ipu_soc *ipu); Â +struct ipu_pre *ipu_pre_get_by_prg_device(struct device *dev, int index); +int ipu_pre_get_available_count(void); +int ipu_pre_get(struct ipu_pre *pre); +void ipu_pre_put(struct ipu_pre *pre); +u32 ipu_pre_get_baddr(struct ipu_pre *pre); +void ipu_pre_configure(struct ipu_pre *pre, unsigned int width,
       unsigned int height,
       unsigned int stride, u32 format, unsigned
int bufaddr); +void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr);
#endif /* __IPU_PRV_H__ */
regards Philipp
On Fri, 2017-03-03 at 19:25 +0100, Lucas Stach wrote: [...]
+int ipu_pre_get(struct ipu_pre *pre) +{
- u32 val;
- if (pre->in_use)
return -EBUSY;
This could race for in_use ...
All the PRE/PRG configuration functions are thread unsafe, with the premise that they are only used during an atomic commit, [...]
Oh, okay. It would be nice to point this out in a comment.
regards Philipp
This adds the the devicetree binding for the Prefetch Resolve Gasket, as found on i.MX6 QuadPlus. The PRG is fairly simple in that it only has a configuration register range and two clocks, one for the AHB slave port and one for the AXI ports and the functional units.
The PRE connections need to be described in the DT, as the PRE<->PRG assignment is a mix between fixed and muxable connections.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- .../bindings/display/imx/fsl-imx-drm.txt | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 1bd777d7c37d..5e4b8b13b9f8 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -79,6 +79,31 @@ pre@021c8000 { fsl,ocram = <&ocram2>; };
+Freescale i.MX PRG (Prefetch Resolve Gasket) +============================================ + +Required properties: +- compatible: should be "fsl,imx6qp-prg" +- reg: should be register base and length as documented in the + datasheet +- clocks : phandles to the PRG ipg and axi clock inputs, as described + in Documentation/devicetree/bindings/clock/clock-bindings.txt and + Documentation/devicetree/bindings/clock/imx6q-clock.txt. +- clock-names: should be "ipg" and "axi" +- fsl,pres: phandles to the PRE units attached to this PRG, with the fixed + PRE as the first entry and the muxable PREs following. + +example: + +prg@021cc000 { + compatible = "fsl,imx6qp-prg"; + reg = <0x021cc000 0x1000>; + clocks = <&clks IMX6QDL_CLK_PRG0_APB>, + <&clks IMX6QDL_CLK_PRG0_AXI>; + clock-names = "ipg", "axi"; + fsl,pres = <&pre1>, <&pre2>, <&pre3>; +}; + Parallel display support ========================
On Fri, Feb 17, 2017 at 07:28:25PM +0100, Lucas Stach wrote:
This adds the the devicetree binding for the Prefetch Resolve Gasket, as found on i.MX6 QuadPlus. The PRG is fairly simple in that it only has a configuration register range and two clocks, one for the AHB slave port and one for the AXI ports and the functional units.
The PRE connections need to be described in the DT, as the PRE<->PRG assignment is a mix between fixed and muxable connections.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
.../bindings/display/imx/fsl-imx-drm.txt | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 1bd777d7c37d..5e4b8b13b9f8 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -79,6 +79,31 @@ pre@021c8000 { fsl,ocram = <&ocram2>; };
+Freescale i.MX PRG (Prefetch Resolve Gasket) +============================================
+Required properties: +- compatible: should be "fsl,imx6qp-prg" +- reg: should be register base and length as documented in the
- datasheet
+- clocks : phandles to the PRG ipg and axi clock inputs, as described
- in Documentation/devicetree/bindings/clock/clock-bindings.txt and
- Documentation/devicetree/bindings/clock/imx6q-clock.txt.
+- clock-names: should be "ipg" and "axi" +- fsl,pres: phandles to the PRE units attached to this PRG, with the fixed
- PRE as the first entry and the muxable PREs following.
+example:
+prg@021cc000 {
Drop the leading 0. With that,
Acked-by: Rob Herring robh@kernel.org
- compatible = "fsl,imx6qp-prg";
- reg = <0x021cc000 0x1000>;
- clocks = <&clks IMX6QDL_CLK_PRG0_APB>,
<&clks IMX6QDL_CLK_PRG0_AXI>;
- clock-names = "ipg", "axi";
- fsl,pres = <&pre1>, <&pre2>, <&pre3>;
+};
Parallel display support
-- 2.11.0
-- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
This adds support for the i.MX6 QUadPlus PRG unit. It glues together the IPU and the PRE units.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- drivers/gpu/ipu-v3/Makefile | 2 +- drivers/gpu/ipu-v3/ipu-prg.c | 413 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/ipu-v3/ipu-prv.h | 3 + include/video/imx-ipu-v3.h | 15 ++ 4 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/ipu-v3/ipu-prg.c
diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 8ae90de46b4d..1ab9bceee755 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \ - ipu-pre.o ipu-smfc.o ipu-vdi.o + ipu-pre.o ipu-prg.o ipu-smfc.o ipu-vdi.o diff --git a/drivers/gpu/ipu-v3/ipu-prg.c b/drivers/gpu/ipu-v3/ipu-prg.c new file mode 100644 index 000000000000..c1e1ab0ec5c5 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-prg.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2016-2017 Lucas Stach, Pengutronix + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/err.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <video/imx-ipu-v3.h> + +#include "ipu-prv.h" + +#define IPU_PRG_CTL 0x00 +#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) +#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 +#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) +#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) +#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) +#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) +#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) +#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) +#define IPU_PRG_CTL_SOFTRST (1 << 30) +#define IPU_PRG_CTL_SHADOW_EN (1 << 31) + +#define IPU_PRG_STATUS 0x04 +#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) +#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2)) + +#define IPU_PRG_QOS 0x08 +#define IPU_PRG_QOS_ARID_MASK 0xf +#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4) + +#define IPU_PRG_REG_UPDATE 0x0c +#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0) + +#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) +#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff + +#define IPU_PRG_CROP_LINE 0x1c + +#define IPU_PRG_THD 0x20 + +#define IPU_PRG_BADDR(i) (0x24 + i * 0x4) + +#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4) + +#define IPU_PRG_ILO(i) (0x3c + i * 0x4) + +#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) +#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff +#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 +#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff +#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16 + +struct ipu_prg_channel { + bool enabled; + int used_pre; +}; + +struct ipu_prg { + struct list_head list; + struct device *dev; + int id; + + void __iomem *regs; + struct clk *clk_ipg, *clk_axi; + struct regmap *iomuxc_gpr; + struct ipu_pre *pres[3]; + + struct ipu_prg_channel chan[3]; +}; + +static DEFINE_MUTEX(ipu_prg_list_mutex); +static LIST_HEAD(ipu_prg_list); + +struct ipu_prg * +ipu_prg_get_by_ipu_device(struct device *dev) +{ + struct device_node *prg_node = of_parse_phandle(dev->of_node, + "fsl,prg", 0); + struct ipu_prg *prg; + + mutex_lock(&ipu_prg_list_mutex); + list_for_each_entry(prg, &ipu_prg_list, list) { + if (prg_node == prg->dev->of_node) { + mutex_unlock(&ipu_prg_list_mutex); + device_link_add(dev, prg->dev, DL_FLAG_AUTOREMOVE); + prg->id = of_alias_get_id(dev->of_node, "ipu"); + return prg; + } + } + mutex_unlock(&ipu_prg_list_mutex); + + return NULL; +} + +int ipu_prg_max_active_channels(void) +{ + return ipu_pre_get_available_count(); +} + +bool ipu_prg_present(struct ipu_soc *ipu) +{ + if (ipu->prg_priv) + return true; + + return false; +} + +bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, + uint64_t modifier) +{ + const struct drm_format_info *info = drm_format_info(format); + + if (info->num_planes != 1) + return false; + + return true; +} + +int ipu_prg_enable(struct ipu_soc *ipu) +{ + struct ipu_prg *prg = ipu->prg_priv; + int ret; + + if (!prg) + return 0; + + ret = clk_prepare_enable(prg->clk_axi); + if (ret) + goto fail_disable_ipg; + + return 0; + +fail_disable_ipg: + clk_disable_unprepare(prg->clk_ipg); + + return ret; +} + +void ipu_prg_disable(struct ipu_soc *ipu) +{ + struct ipu_prg *prg = ipu->prg_priv; + + if (!prg) + return; + + clk_disable_unprepare(prg->clk_axi); +} + +static int ipu_prg_ipu_to_prg_chan(int ipu_chan) +{ + /* + * This isn't clearly documented in the RM, but IPU to PRG channel + * assignment is fixed, as only with this mapping the control signals + * match up. + */ + switch (ipu_chan) { + case IPUV3_CHANNEL_MEM_BG_SYNC: + return 0; + case IPUV3_CHANNEL_MEM_FG_SYNC: + return 1; + case IPUV3_CHANNEL_MEM_DC_SYNC: + return 2; + default: + return -EINVAL; + } +} + +static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) +{ + int i, ret; + + /* channel 0 is special as it is hardwired to one of the PREs */ + if (prg_chan == 0) { + ret = ipu_pre_get(prg->pres[0]); + if (ret) + goto fail; + prg->chan[prg_chan].used_pre = 0; + return 0; + } + + for (i = 1; i < 3; i++) { + ret = ipu_pre_get(prg->pres[i]); + if (!ret) { + u32 val, mux; + int shift; + + prg->chan[prg_chan].used_pre = i; + + /* configure the PRE to PRG channel mux */ + shift = (i == 1) ? 12 : 14; + mux = (prg->id << 1) | (prg_chan - 1); + regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, + 0x3 << shift, mux << shift); + + /* check other mux, must not point to same channel */ + shift = (i == 1) ? 14 : 12; + regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val); + if (((val >> shift) & 0x3) == mux) { + regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5, + 0x3 << shift, + (mux ^ 0x1) << shift); + } + + return 0; + } + } + +fail: + dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan); + return ret; +} + +static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) +{ + struct ipu_prg_channel *chan = &prg->chan[prg_chan]; + + ipu_pre_put(prg->pres[chan->used_pre]); + chan->used_pre = -1; +} + +void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) +{ + int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); + struct ipu_prg *prg = ipu_chan->ipu->prg_priv; + struct ipu_prg_channel *chan = &prg->chan[prg_chan]; + u32 val; + + if (!chan->enabled || prg_chan < 0) + return; + + clk_prepare_enable(prg->clk_ipg); + + val = readl(prg->regs + IPU_PRG_CTL); + val |= IPU_PRG_CTL_BYPASS(prg_chan); + writel(val, prg->regs + IPU_PRG_CTL); + + val = IPU_PRG_REG_UPDATE_REG_UPDATE; + writel(val, prg->regs + IPU_PRG_REG_UPDATE); + + clk_disable_unprepare(prg->clk_ipg); + + ipu_prg_put_pre(prg, prg_chan); + + chan->enabled = false; +} + +int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, + unsigned int axi_id, unsigned int width, + unsigned int height, unsigned int stride, + u32 format, unsigned long *eba) +{ + int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num); + struct ipu_prg *prg = ipu_chan->ipu->prg_priv; + struct ipu_prg_channel *chan = &prg->chan[prg_chan]; + u32 val; + int ret; + + if (prg_chan < 0) + return prg_chan; + + if (chan->enabled) { + ipu_pre_update(prg->pres[chan->used_pre], *eba); + return 0; + } + + ret = ipu_prg_get_pre(prg, prg_chan); + if (ret) + return ret; + + ipu_pre_configure(prg->pres[chan->used_pre], + width, height, stride, format, *eba); + + + ret = clk_prepare_enable(prg->clk_ipg); + if (ret) { + ipu_prg_put_pre(prg, prg_chan); + return ret; + } + + val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK; + writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan)); + + val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) << + IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) | + ((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) << + IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT); + writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan)); + + val = ipu_pre_get_baddr(prg->pres[chan->used_pre]); + *eba = val; + writel(val, prg->regs + IPU_PRG_BADDR(prg_chan)); + + val = readl(prg->regs + IPU_PRG_CTL); + /* counter load enable */ + val |= IPU_PRG_CTL_CNT_LOAD_EN(prg_chan); + /* config AXI ID */ + val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK << + IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan)); + val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id); + /* enable channel */ + val &= ~IPU_PRG_CTL_BYPASS(prg_chan); + writel(val, prg->regs + IPU_PRG_CTL); + + val = IPU_PRG_REG_UPDATE_REG_UPDATE; + writel(val, prg->regs + IPU_PRG_REG_UPDATE); + + clk_disable_unprepare(prg->clk_ipg); + + chan->enabled = true; + return 0; +} + +static int ipu_prg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct ipu_prg *prg; + u32 val; + int i, ret; + + prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL); + if (!prg) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + prg->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(prg->regs)) + return PTR_ERR(prg->regs); + + + prg->clk_ipg = devm_clk_get(dev, "ipg"); + if (IS_ERR(prg->clk_ipg)) + return PTR_ERR(prg->clk_ipg); + + prg->clk_axi = devm_clk_get(dev, "axi"); + if (IS_ERR(prg->clk_axi)) + return PTR_ERR(prg->clk_axi); + + prg->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(prg->iomuxc_gpr)) + return PTR_ERR(prg->iomuxc_gpr); + + for (i = 0; i < 3; i++) { + prg->pres[i] = ipu_pre_get_by_prg_device(dev, i); + if (!prg->pres[i]) + return -EPROBE_DEFER; + } + + ret = clk_prepare_enable(prg->clk_ipg); + if (ret) + return ret; + + /* init to free running mode */ + val = readl(prg->regs + IPU_PRG_CTL); + val |= IPU_PRG_CTL_SHADOW_EN; + writel(val, prg->regs + IPU_PRG_CTL); + + /* disable address threshold */ + writel(0xffffffff, prg->regs + IPU_PRG_THD); + + clk_disable_unprepare(prg->clk_ipg); + + prg->dev = dev; + platform_set_drvdata(pdev, prg); + mutex_lock(&ipu_prg_list_mutex); + list_add(&prg->list, &ipu_prg_list); + mutex_unlock(&ipu_prg_list_mutex); + + return 0; +} + +static int ipu_prg_remove(struct platform_device *pdev) +{ + struct ipu_prg *prg = platform_get_drvdata(pdev); + + mutex_lock(&ipu_prg_list_mutex); + list_del(&prg->list); + mutex_unlock(&ipu_prg_list_mutex); + + return 0; +} + +static const struct of_device_id ipu_prg_dt_ids[] = { + { .compatible = "fsl,imx6qp-prg", }, + { /* sentinel */ }, +}; + +static struct platform_driver ipu_prg_drv = { + .probe = ipu_prg_probe, + .remove = ipu_prg_remove, + .driver = { + .name = "imx-ipu-prg", + .of_match_table = ipu_prg_dt_ids, + }, +}; +module_platform_driver(ipu_prg_drv); diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index 536a156a5eb6..0d1d2d667f3b 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -169,6 +169,7 @@ struct ipu_vdi; struct ipu_image_convert_priv; struct ipu_smfc_priv; struct ipu_pre; +struct ipu_prg;
struct ipu_devtype;
@@ -270,4 +271,6 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, unsigned int stride, u32 format, unsigned int bufaddr); void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr);
+struct ipu_prg *ipu_prg_get_by_ipu_device(struct device *dev); + #endif /* __IPU_PRV_H__ */ diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index 228e32082097..3902323f8769 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -331,6 +331,21 @@ int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, bool bg_chan);
/* + * IPU Prefetch Resolve Gasket (prg) functions + */ +int ipu_prg_max_active_channels(void); +bool ipu_prg_present(struct ipu_soc *ipu); +bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format, + uint64_t modifier); +int ipu_prg_enable(struct ipu_soc *ipu); +void ipu_prg_disable(struct ipu_soc *ipu); +void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan); +int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan, + unsigned int axi_id, unsigned int width, + unsigned int height, unsigned int stride, + u32 format, unsigned long *eba); + +/* * IPU CMOS Sensor Interface (csi) functions */ struct ipu_csi;
On Fri, 2017-02-17 at 19:28 +0100, Lucas Stach wrote:
This adds support for the i.MX6 QUadPlus PRG unit. It glues together the IPU and the PRE units.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
drivers/gpu/ipu-v3/Makefile | 2 +- drivers/gpu/ipu-v3/ipu-prg.c | 413 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/ipu-v3/ipu-prv.h | 3 + include/video/imx-ipu-v3.h | 15 ++ 4 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/ipu-v3/ipu-prg.c
diff --git a/drivers/gpu/ipu-v3/Makefile b/drivers/gpu/ipu-v3/Makefile index 8ae90de46b4d..1ab9bceee755 100644 --- a/drivers/gpu/ipu-v3/Makefile +++ b/drivers/gpu/ipu-v3/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \ ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \
ipu-pre.o ipu-smfc.o ipu-vdi.o
ipu-pre.o ipu-prg.o ipu-smfc.o ipu-vdi.o
diff --git a/drivers/gpu/ipu-v3/ipu-prg.c b/drivers/gpu/ipu-v3/ipu-prg.c new file mode 100644 index 000000000000..c1e1ab0ec5c5 --- /dev/null +++ b/drivers/gpu/ipu-v3/ipu-prg.c @@ -0,0 +1,413 @@ +/*
- Copyright (c) 2016-2017 Lucas Stach, Pengutronix
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
- This program is distributed in the hope 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/err.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <video/imx-ipu-v3.h>
+#include "ipu-prv.h"
+#define IPU_PRG_CTL 0x00 +#define IPU_PRG_CTL_BYPASS(i) (1 << (0 + i)) +#define IPU_PRG_CTL_SOFT_ARID_MASK 0x3 +#define IPU_PRG_CTL_SOFT_ARID_SHIFT(i) (8 + i * 2) +#define IPU_PRG_CTL_SOFT_ARID(i, v) ((v & 0x3) << (8 + 2 * i)) +#define IPU_PRG_CTL_SO(i) (1 << (16 + i)) +#define IPU_PRG_CTL_VFLIP(i) (1 << (19 + i)) +#define IPU_PRG_CTL_BLOCK_MODE(i) (1 << (22 + i)) +#define IPU_PRG_CTL_CNT_LOAD_EN(i) (1 << (25 + i)) +#define IPU_PRG_CTL_SOFTRST (1 << 30) +#define IPU_PRG_CTL_SHADOW_EN (1 << 31)
+#define IPU_PRG_STATUS 0x04 +#define IPU_PRG_STATUS_BUFFER0_READY(i) (1 << (0 + i * 2)) +#define IPU_PRG_STATUS_BUFFER1_READY(i) (1 << (1 + i * 2))
+#define IPU_PRG_QOS 0x08 +#define IPU_PRG_QOS_ARID_MASK 0xf +#define IPU_PRG_QOS_ARID_SHIFT(i) (0 + i * 4)
+#define IPU_PRG_REG_UPDATE 0x0c +#define IPU_PRG_REG_UPDATE_REG_UPDATE (1 << 0)
+#define IPU_PRG_STRIDE(i) (0x10 + i * 0x4) +#define IPU_PRG_STRIDE_STRIDE_MASK 0x3fff
+#define IPU_PRG_CROP_LINE 0x1c
+#define IPU_PRG_THD 0x20
+#define IPU_PRG_BADDR(i) (0x24 + i * 0x4)
+#define IPU_PRG_OFFSET(i) (0x30 + i * 0x4)
+#define IPU_PRG_ILO(i) (0x3c + i * 0x4)
+#define IPU_PRG_HEIGHT(i) (0x48 + i * 0x4) +#define IPU_PRG_HEIGHT_PRE_HEIGHT_MASK 0xfff +#define IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT 0 +#define IPU_PRG_HEIGHT_IPU_HEIGHT_MASK 0xfff +#define IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT 16
+struct ipu_prg_channel {
- bool enabled;
- int used_pre;
+};
+struct ipu_prg {
- struct list_head list;
- struct device *dev;
- int id;
- void __iomem *regs;
- struct clk *clk_ipg, *clk_axi;
- struct regmap *iomuxc_gpr;
- struct ipu_pre *pres[3];
- struct ipu_prg_channel chan[3];
+};
+static DEFINE_MUTEX(ipu_prg_list_mutex); +static LIST_HEAD(ipu_prg_list);
+struct ipu_prg * +ipu_prg_get_by_ipu_device(struct device *dev)
Same as for the PRE, I'd prefer this to be called "lookup" instead of "get", and to pass the "fsl,prg" string from the IPU driver, to whose DT binding it belongs.
+{
- struct device_node *prg_node = of_parse_phandle(dev->of_node,
"fsl,prg", 0);
- struct ipu_prg *prg;
- mutex_lock(&ipu_prg_list_mutex);
- list_for_each_entry(prg, &ipu_prg_list, list) {
if (prg_node == prg->dev->of_node) {
mutex_unlock(&ipu_prg_list_mutex);
device_link_add(dev, prg->dev, DL_FLAG_AUTOREMOVE);
prg->id = of_alias_get_id(dev->of_node, "ipu");
return prg;
}
- }
- mutex_unlock(&ipu_prg_list_mutex);
- return NULL;
+}
+int ipu_prg_max_active_channels(void) +{
- return ipu_pre_get_available_count();
+}
+bool ipu_prg_present(struct ipu_soc *ipu) +{
- if (ipu->prg_priv)
return true;
- return false;
+}
+bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format,
uint64_t modifier)
+{
- const struct drm_format_info *info = drm_format_info(format);
- if (info->num_planes != 1)
return false;
- return true;
+}
+int ipu_prg_enable(struct ipu_soc *ipu) +{
- struct ipu_prg *prg = ipu->prg_priv;
- int ret;
- if (!prg)
return 0;
- ret = clk_prepare_enable(prg->clk_axi);
- if (ret)
goto fail_disable_ipg;
- return 0;
+fail_disable_ipg:
- clk_disable_unprepare(prg->clk_ipg);
- return ret;
+}
+void ipu_prg_disable(struct ipu_soc *ipu) +{
- struct ipu_prg *prg = ipu->prg_priv;
- if (!prg)
return;
- clk_disable_unprepare(prg->clk_axi);
+}
+static int ipu_prg_ipu_to_prg_chan(int ipu_chan) +{
- /*
* This isn't clearly documented in the RM, but IPU to PRG channel
* assignment is fixed, as only with this mapping the control signals
* match up.
*/
- switch (ipu_chan) {
- case IPUV3_CHANNEL_MEM_BG_SYNC:
return 0;
- case IPUV3_CHANNEL_MEM_FG_SYNC:
return 1;
- case IPUV3_CHANNEL_MEM_DC_SYNC:
return 2;
- default:
return -EINVAL;
- }
+}
+static int ipu_prg_get_pre(struct ipu_prg *prg, int prg_chan) +{
- int i, ret;
- /* channel 0 is special as it is hardwired to one of the PREs */
- if (prg_chan == 0) {
ret = ipu_pre_get(prg->pres[0]);
if (ret)
goto fail;
prg->chan[prg_chan].used_pre = 0;
return 0;
- }
- for (i = 1; i < 3; i++) {
ret = ipu_pre_get(prg->pres[i]);
if (!ret) {
u32 val, mux;
int shift;
prg->chan[prg_chan].used_pre = i;
/* configure the PRE to PRG channel mux */
shift = (i == 1) ? 12 : 14;
mux = (prg->id << 1) | (prg_chan - 1);
regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
0x3 << shift, mux << shift);
/* check other mux, must not point to same channel */
shift = (i == 1) ? 14 : 12;
regmap_read(prg->iomuxc_gpr, IOMUXC_GPR5, &val);
if (((val >> shift) & 0x3) == mux) {
regmap_update_bits(prg->iomuxc_gpr, IOMUXC_GPR5,
0x3 << shift,
(mux ^ 0x1) << shift);
}
return 0;
}
- }
+fail:
- dev_err(prg->dev, "could not get PRE for PRG chan %d", prg_chan);
- return ret;
+}
+static void ipu_prg_put_pre(struct ipu_prg *prg, int prg_chan) +{
- struct ipu_prg_channel *chan = &prg->chan[prg_chan];
- ipu_pre_put(prg->pres[chan->used_pre]);
- chan->used_pre = -1;
+}
+void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan) +{
- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
- struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
- struct ipu_prg_channel *chan = &prg->chan[prg_chan];
- u32 val;
- if (!chan->enabled || prg_chan < 0)
return;
- clk_prepare_enable(prg->clk_ipg);
- val = readl(prg->regs + IPU_PRG_CTL);
- val |= IPU_PRG_CTL_BYPASS(prg_chan);
- writel(val, prg->regs + IPU_PRG_CTL);
- val = IPU_PRG_REG_UPDATE_REG_UPDATE;
- writel(val, prg->regs + IPU_PRG_REG_UPDATE);
- clk_disable_unprepare(prg->clk_ipg);
- ipu_prg_put_pre(prg, prg_chan);
- chan->enabled = false;
+}
+int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
unsigned int axi_id, unsigned int width,
unsigned int height, unsigned int stride,
u32 format, unsigned long *eba)
+{
- int prg_chan = ipu_prg_ipu_to_prg_chan(ipu_chan->num);
- struct ipu_prg *prg = ipu_chan->ipu->prg_priv;
- struct ipu_prg_channel *chan = &prg->chan[prg_chan];
- u32 val;
- int ret;
- if (prg_chan < 0)
return prg_chan;
- if (chan->enabled) {
ipu_pre_update(prg->pres[chan->used_pre], *eba);
return 0;
- }
- ret = ipu_prg_get_pre(prg, prg_chan);
- if (ret)
return ret;
- ipu_pre_configure(prg->pres[chan->used_pre],
width, height, stride, format, *eba);
- ret = clk_prepare_enable(prg->clk_ipg);
- if (ret) {
ipu_prg_put_pre(prg, prg_chan);
return ret;
- }
- val = (stride - 1) & IPU_PRG_STRIDE_STRIDE_MASK;
- writel(val, prg->regs + IPU_PRG_STRIDE(prg_chan));
- val = ((height & IPU_PRG_HEIGHT_PRE_HEIGHT_MASK) <<
IPU_PRG_HEIGHT_PRE_HEIGHT_SHIFT) |
((height & IPU_PRG_HEIGHT_IPU_HEIGHT_MASK) <<
IPU_PRG_HEIGHT_IPU_HEIGHT_SHIFT);
- writel(val, prg->regs + IPU_PRG_HEIGHT(prg_chan));
- val = ipu_pre_get_baddr(prg->pres[chan->used_pre]);
- *eba = val;
- writel(val, prg->regs + IPU_PRG_BADDR(prg_chan));
- val = readl(prg->regs + IPU_PRG_CTL);
- /* counter load enable */
- val |= IPU_PRG_CTL_CNT_LOAD_EN(prg_chan);
- /* config AXI ID */
- val &= ~(IPU_PRG_CTL_SOFT_ARID_MASK <<
IPU_PRG_CTL_SOFT_ARID_SHIFT(prg_chan));
- val |= IPU_PRG_CTL_SOFT_ARID(prg_chan, axi_id);
- /* enable channel */
- val &= ~IPU_PRG_CTL_BYPASS(prg_chan);
- writel(val, prg->regs + IPU_PRG_CTL);
- val = IPU_PRG_REG_UPDATE_REG_UPDATE;
- writel(val, prg->regs + IPU_PRG_REG_UPDATE);
- clk_disable_unprepare(prg->clk_ipg);
- chan->enabled = true;
- return 0;
+}
+static int ipu_prg_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct ipu_prg *prg;
- u32 val;
- int i, ret;
- prg = devm_kzalloc(dev, sizeof(*prg), GFP_KERNEL);
- if (!prg)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- prg->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(prg->regs))
return PTR_ERR(prg->regs);
So much whitespace.
- prg->clk_ipg = devm_clk_get(dev, "ipg");
- if (IS_ERR(prg->clk_ipg))
return PTR_ERR(prg->clk_ipg);
- prg->clk_axi = devm_clk_get(dev, "axi");
- if (IS_ERR(prg->clk_axi))
return PTR_ERR(prg->clk_axi);
- prg->iomuxc_gpr =
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
- if (IS_ERR(prg->iomuxc_gpr))
return PTR_ERR(prg->iomuxc_gpr);
- for (i = 0; i < 3; i++) {
prg->pres[i] = ipu_pre_get_by_prg_device(dev, i);
if (!prg->pres[i])
return -EPROBE_DEFER;
- }
- ret = clk_prepare_enable(prg->clk_ipg);
- if (ret)
return ret;
- /* init to free running mode */
- val = readl(prg->regs + IPU_PRG_CTL);
- val |= IPU_PRG_CTL_SHADOW_EN;
- writel(val, prg->regs + IPU_PRG_CTL);
- /* disable address threshold */
- writel(0xffffffff, prg->regs + IPU_PRG_THD);
- clk_disable_unprepare(prg->clk_ipg);
- prg->dev = dev;
- platform_set_drvdata(pdev, prg);
- mutex_lock(&ipu_prg_list_mutex);
- list_add(&prg->list, &ipu_prg_list);
- mutex_unlock(&ipu_prg_list_mutex);
- return 0;
+}
+static int ipu_prg_remove(struct platform_device *pdev) +{
- struct ipu_prg *prg = platform_get_drvdata(pdev);
- mutex_lock(&ipu_prg_list_mutex);
- list_del(&prg->list);
- mutex_unlock(&ipu_prg_list_mutex);
- return 0;
+}
+static const struct of_device_id ipu_prg_dt_ids[] = {
- { .compatible = "fsl,imx6qp-prg", },
- { /* sentinel */ },
+};
+static struct platform_driver ipu_prg_drv = {
- .probe = ipu_prg_probe,
- .remove = ipu_prg_remove,
- .driver = {
.name = "imx-ipu-prg",
.of_match_table = ipu_prg_dt_ids,
- },
+}; +module_platform_driver(ipu_prg_drv); diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index 536a156a5eb6..0d1d2d667f3b 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -169,6 +169,7 @@ struct ipu_vdi; struct ipu_image_convert_priv; struct ipu_smfc_priv; struct ipu_pre; +struct ipu_prg;
struct ipu_devtype;
@@ -270,4 +271,6 @@ void ipu_pre_configure(struct ipu_pre *pre, unsigned int width, unsigned int stride, u32 format, unsigned int bufaddr); void ipu_pre_update(struct ipu_pre *pre, unsigned int bufaddr);
+struct ipu_prg *ipu_prg_get_by_ipu_device(struct device *dev);
#endif /* __IPU_PRV_H__ */ diff --git a/include/video/imx-ipu-v3.h b/include/video/imx-ipu-v3.h index 228e32082097..3902323f8769 100644 --- a/include/video/imx-ipu-v3.h +++ b/include/video/imx-ipu-v3.h @@ -331,6 +331,21 @@ int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha, bool bg_chan);
/*
- IPU Prefetch Resolve Gasket (prg) functions
- */
+int ipu_prg_max_active_channels(void); +bool ipu_prg_present(struct ipu_soc *ipu); +bool ipu_prg_format_supported(struct ipu_soc *ipu, uint32_t format,
uint64_t modifier);
+int ipu_prg_enable(struct ipu_soc *ipu); +void ipu_prg_disable(struct ipu_soc *ipu); +void ipu_prg_channel_disable(struct ipuv3_channel *ipu_chan); +int ipu_prg_channel_configure(struct ipuv3_channel *ipu_chan,
unsigned int axi_id, unsigned int width,
unsigned int height, unsigned int stride,
u32 format, unsigned long *eba);
+/*
- IPU CMOS Sensor Interface (csi) functions
*/ struct ipu_csi;
regards Philipp
On i.MX6 QuadPlus the IPU needs to know which PRG has to be used for this IPU instance. Add a "fsl,prg" property containing a phandle pointing to the correct PRG device.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 5e4b8b13b9f8..c8c7a7b3951f 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -28,6 +28,8 @@ Required properties: in this order. - resets: phandle pointing to the system reset controller and reset line index, see reset/fsl,imx-src.txt for details +Additional required properties for fsl,imx6qp-ipu: +- fsl,prg: phandle to prg node associated with this IPU instance Optional properties: - port@[0-3]: Port nodes with endpoint definitions as defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
On Fri, Feb 17, 2017 at 07:28:27PM +0100, Lucas Stach wrote:
On i.MX6 QuadPlus the IPU needs to know which PRG has to be used for this IPU instance. Add a "fsl,prg" property containing a phandle pointing to the correct PRG device.
Is there already a new compatible string? If not, add one. If so, then the subject should be more specific.
Signed-off-by: Lucas Stach l.stach@pengutronix.de
Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt index 5e4b8b13b9f8..c8c7a7b3951f 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt +++ b/Documentation/devicetree/bindings/display/imx/fsl-imx-drm.txt @@ -28,6 +28,8 @@ Required properties: in this order.
- resets: phandle pointing to the system reset controller and reset line index, see reset/fsl,imx-src.txt for details
+Additional required properties for fsl,imx6qp-ipu: +- fsl,prg: phandle to prg node associated with this IPU instance Optional properties:
- port@[0-3]: Port nodes with endpoint definitions as defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
-- 2.11.0
The i.MX6 QuadPlus IPU needs to PRG unit to gain access to the data bus. Make sure it is present and available to be used.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- drivers/gpu/ipu-v3/ipu-common.c | 7 +++++++ drivers/gpu/ipu-v3/ipu-prv.h | 1 + 2 files changed, 8 insertions(+)
diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index d0694ef95f28..37426dd94408 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -937,6 +937,7 @@ static const struct of_device_id imx_ipu_dt_ids[] = { { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, + { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6q, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids); @@ -1402,6 +1403,12 @@ static int ipu_probe(struct platform_device *pdev) if (!ipu) return -ENODEV;
+ if (of_device_is_compatible(np, "fsl,imx6qp-ipu")) { + ipu->prg_priv = ipu_prg_get_by_ipu_device(&pdev->dev); + if (!ipu->prg_priv) + return -EPROBE_DEFER; + } + for (i = 0; i < 64; i++) ipu->channel[i].ipu = ipu; ipu->devtype = devtype; diff --git a/drivers/gpu/ipu-v3/ipu-prv.h b/drivers/gpu/ipu-v3/ipu-prv.h index 0d1d2d667f3b..0d32a4f3a76a 100644 --- a/drivers/gpu/ipu-v3/ipu-prv.h +++ b/drivers/gpu/ipu-v3/ipu-prv.h @@ -204,6 +204,7 @@ struct ipu_soc { struct ipu_vdi *vdi_priv; struct ipu_image_convert_priv *image_convert_priv; struct ipu_smfc_priv *smfc_priv; + struct ipu_prg *prg_priv; };
static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
On i.MX6 QuadPlus the PRG needs to be clocked in order to pass through the data access requests from the IDMAC. This call is a no-op for other all other SoCs.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- drivers/gpu/drm/imx/ipuv3-crtc.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 6be515a9fb69..4299c7d07d80 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -55,6 +55,7 @@ static void ipu_crtc_enable(struct drm_crtc *crtc) struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
+ ipu_prg_enable(ipu); ipu_dc_enable(ipu); ipu_dc_enable_channel(ipu_crtc->dc); ipu_di_enable(ipu_crtc->di); @@ -75,6 +76,7 @@ static void ipu_crtc_atomic_disable(struct drm_crtc *crtc, */ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false); ipu_dc_disable(ipu); + ipu_prg_disable(ipu);
spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) {
Allow the planes to use the PRG/PRE units as linear prefetchers when possible. This improves DRAM efficiency a bit and reduces the chance for display underflow when the memory subsystem is under load.
This does not yet support scanning out tiled buffers directly, as this needs more work, but it already wires up the basic interaction between imx-drm, the IPUv3 driver and the PRG and PRE drivers.
Signed-off-by: Lucas Stach l.stach@pengutronix.de --- drivers/gpu/drm/imx/imx-drm-core.c | 5 ++ drivers/gpu/drm/imx/imx-drm.h | 3 + drivers/gpu/drm/imx/ipuv3-plane.c | 122 ++++++++++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index bef76cb0d05d..f3fd94046e3e 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -147,6 +147,11 @@ static int imx_drm_atomic_check(struct drm_device *dev, if (ret) return ret;
+ /* Assign PRG/PRE channels and check if all constrains are satisfied. */ + ret = ipu_planes_assign_pre(dev, state); + if (ret) + return ret; + return ret; }
diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index 5a91cb16c8fa..485df472fd34 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -52,4 +52,7 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, void imx_drm_connector_destroy(struct drm_connector *connector); void imx_drm_encoder_destroy(struct drm_encoder *encoder);
+int ipu_planes_assign_pre(struct drm_device *dev, + struct drm_atomic_state *state); + #endif /* _IMX_DRM_H_ */ diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 57130352db3b..6867cd7f2da3 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -23,6 +23,17 @@ #include "video/imx-ipu-v3.h" #include "ipuv3-plane.h"
+struct ipu_plane_state { + struct drm_plane_state base; + bool use_pre; +}; + +static inline struct ipu_plane_state * +to_ipu_plane_state(struct drm_plane_state *p) +{ + return container_of(p, struct ipu_plane_state, base); +} + static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p) { return container_of(p, struct ipu_plane, base); @@ -225,6 +236,8 @@ static int ipu_disable_plane(struct drm_plane *plane) ipu_dmfc_disable_channel(ipu_plane->dmfc); if (ipu_plane->dp) ipu_dp_disable(ipu_plane->ipu); + if (ipu_prg_present(ipu_plane->ipu)) + ipu_prg_channel_disable(ipu_plane->ipu_ch);
return 0; } @@ -239,13 +252,56 @@ static void ipu_plane_destroy(struct drm_plane *plane) kfree(ipu_plane); }
+void ipu_plane_state_reset(struct drm_plane *plane) +{ + struct ipu_plane_state *ipu_state; + + if (plane->state) { + ipu_state = to_ipu_plane_state(plane->state); + __drm_atomic_helper_plane_destroy_state(plane->state); + kfree(ipu_state); + } + + ipu_state = kzalloc(sizeof(*ipu_state), GFP_KERNEL); + + if (ipu_state) { + ipu_state->base.plane = plane; + ipu_state->base.rotation = DRM_ROTATE_0; + } + + plane->state = &ipu_state->base; +} + +struct drm_plane_state *ipu_plane_duplicate_state(struct drm_plane *plane) +{ + struct ipu_plane_state *state; + + if (WARN_ON(!plane->state)) + return NULL; + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state) + __drm_atomic_helper_plane_duplicate_state(plane, &state->base); + + return &state->base; +} + +void ipu_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct ipu_plane_state *ipu_state = to_ipu_plane_state(state); + + __drm_atomic_helper_plane_destroy_state(state); + kfree(ipu_state); +} + static const struct drm_plane_funcs ipu_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = ipu_plane_destroy, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .reset = ipu_plane_state_reset, + .atomic_duplicate_state = ipu_plane_duplicate_state, + .atomic_destroy_state = ipu_plane_destroy_state, };
static int ipu_plane_atomic_check(struct drm_plane *plane, @@ -458,14 +514,30 @@ static void ipu_plane_atomic_disable(struct drm_plane *plane, ipu_disable_plane(plane); }
+static int ipu_chan_assign_axi_id(int ipu_chan) +{ + switch (ipu_chan) { + case IPUV3_CHANNEL_MEM_BG_SYNC: + return 1; + case IPUV3_CHANNEL_MEM_FG_SYNC: + return 2; + case IPUV3_CHANNEL_MEM_DC_SYNC: + return 3; + default: + return 0; + } +} + static void ipu_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct ipu_plane *ipu_plane = to_ipu_plane(plane); struct drm_plane_state *state = plane->state; + struct ipu_plane_state *ipu_state = to_ipu_plane_state(state); struct drm_crtc_state *crtc_state = state->crtc->state; struct drm_framebuffer *fb = state->fb; struct drm_rect *dst = &state->dst; + unsigned int axi_id = ipu_chan_assign_axi_id(ipu_plane->dma); unsigned long eba, ubo, vbo; unsigned long alpha_eba = 0; enum ipu_color_space ics; @@ -476,7 +548,22 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
eba = drm_plane_state_to_eba(state, 0);
+ /* + * Configure PRG channel and attached PRE, this changes the EBA to an + * internal SRAM location. + */ + if (ipu_state->use_pre) { + ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, + drm_rect_width(&state->src) >> 16, + drm_rect_height(&state->src) >> 16, + state->fb->pitches[0], + state->fb->pixel_format, &eba); + } + if (old_state->fb && !drm_atomic_crtc_needs_modeset(crtc_state)) { + /* nothing to do if PRE is used */ + if (ipu_state->use_pre) + return; active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch); ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba); ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active); @@ -535,6 +622,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ipu_cpmem_set_high_priority(ipu_plane->ipu_ch); ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1); ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]); + ipu_cpmem_set_axi_id(ipu_plane->ipu_ch, axi_id); switch (fb->pixel_format) { case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: @@ -610,6 +698,34 @@ static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = { .atomic_update = ipu_plane_atomic_update, };
+int ipu_planes_assign_pre(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state; + struct drm_plane *plane; + int available_pres = ipu_prg_max_active_channels(); + int i; + + for_each_plane_in_state(state, plane, plane_state, i) { + struct ipu_plane_state *ipu_state = + to_ipu_plane_state(plane_state); + struct ipu_plane *ipu_plane = to_ipu_plane(plane); + + if (ipu_prg_present(ipu_plane->ipu) && available_pres && + plane_state->fb && + ipu_prg_format_supported(ipu_plane->ipu, + plane_state->fb->pixel_format, + plane_state->fb->modifier)) { + ipu_state->use_pre = true; + available_pres--; + } else { + ipu_state->use_pre = false; + } + } + + return 0; +} + struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu, int dma, int dp, unsigned int possible_crtcs, enum drm_plane_type type)
dri-devel@lists.freedesktop.org