On Mon, Nov 28, 2016 at 03:23:54PM +0100, Jean-Francois Moine wrote:
Allwinner's recent SoCs, as A64, A83T and H3, contain a new display engine, DE2. This patch adds a DRM video driver for this device.
Signed-off-by: Jean-Francois Moine moinejf@free.fr
Scrolled around a bit, seemed all reasonable.
Acked-by: Daniel Vetter daniel.vetter@ffwll.ch
Not sure a new driver for each chip is reasonable, experience says that long-term you want to share quite a pile of code between different hw platforms from the same vendor. But that's entirely up to you. -Daniel
drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/sun8i/Kconfig | 19 + drivers/gpu/drm/sun8i/Makefile | 7 + drivers/gpu/drm/sun8i/de2_crtc.c | 449 +++++++++++++++++++++++ drivers/gpu/drm/sun8i/de2_crtc.h | 50 +++ drivers/gpu/drm/sun8i/de2_drv.c | 317 ++++++++++++++++ drivers/gpu/drm/sun8i/de2_drv.h | 48 +++ drivers/gpu/drm/sun8i/de2_plane.c | 734 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 1627 insertions(+) create mode 100644 drivers/gpu/drm/sun8i/Kconfig create mode 100644 drivers/gpu/drm/sun8i/Makefile create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.c create mode 100644 drivers/gpu/drm/sun8i/de2_crtc.h create mode 100644 drivers/gpu/drm/sun8i/de2_drv.c create mode 100644 drivers/gpu/drm/sun8i/de2_drv.h create mode 100644 drivers/gpu/drm/sun8i/de2_plane.c
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 95fc041..bb1bfbc 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -202,6 +202,8 @@ source "drivers/gpu/drm/shmobile/Kconfig"
source "drivers/gpu/drm/sun4i/Kconfig"
+source "drivers/gpu/drm/sun8i/Kconfig"
source "drivers/gpu/drm/omapdrm/Kconfig"
source "drivers/gpu/drm/tilcdc/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 883f3e7..3e1eaa0 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-y += omapdrm/ obj-$(CONFIG_DRM_SUN4I) += sun4i/ +obj-$(CONFIG_DRM_SUN8I) += sun8i/ obj-y += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_BOCHS) += bochs/ diff --git a/drivers/gpu/drm/sun8i/Kconfig b/drivers/gpu/drm/sun8i/Kconfig new file mode 100644 index 0000000..6940895 --- /dev/null +++ b/drivers/gpu/drm/sun8i/Kconfig @@ -0,0 +1,19 @@ +# +# Allwinner DE2 Video configuration +#
+config DRM_SUN8I
- bool
+config DRM_SUN8I_DE2
- tristate "Support for Allwinner Video with DE2 interface"
- depends on DRM && OF
- depends on ARCH_SUNXI || COMPILE_TEST
- select DRM_GEM_CMA_HELPER
- select DRM_KMS_CMA_HELPER
- select DRM_KMS_HELPER
- select DRM_SUN8I
- help
Choose this option if your Allwinner chipset has the DE2 interface
as the A64, A83T and H3. If M is selected the module will be called
sun8i-de2-drm.
diff --git a/drivers/gpu/drm/sun8i/Makefile b/drivers/gpu/drm/sun8i/Makefile new file mode 100644 index 0000000..f107919 --- /dev/null +++ b/drivers/gpu/drm/sun8i/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Allwinner's sun8i DRM device driver +#
+sun8i-de2-drm-objs := de2_drv.o de2_crtc.o de2_plane.o
+obj-$(CONFIG_DRM_SUN8I_DE2) += sun8i-de2-drm.o diff --git a/drivers/gpu/drm/sun8i/de2_crtc.c b/drivers/gpu/drm/sun8i/de2_crtc.c new file mode 100644 index 0000000..4e94ccc --- /dev/null +++ b/drivers/gpu/drm/sun8i/de2_crtc.c @@ -0,0 +1,449 @@ +/*
- Allwinner DRM driver - DE2 CRTC
- Copyright (C) 2016 Jean-Francois Moine moinejf@free.fr
- 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.
- */
+#include <linux/component.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> +#include <linux/io.h> +#include <linux/of_irq.h> +#include <linux/of_graph.h>
+#include "de2_drv.h" +#include "de2_crtc.h"
+/* I/O map */
+#define TCON_GCTL_REG 0x00 +#define TCON_GCTL_TCON_ENABLE BIT(31) +#define TCON_GINT0_REG 0x04 +#define TCON_GINT0_TCON1_Vb_Int_En BIT(30) +#define TCON_GINT0_TCON1_Vb_Int_Flag BIT(14) +#define TCON_GINT0_TCON1_Vb_Line_Int_Flag BIT(12) +#define TCON0_CTL_REG 0x40 +#define TCON0_CTL_TCON_ENABLE BIT(31) +#define TCON1_CTL_REG 0x90 +#define TCON1_CTL_TCON_ENABLE BIT(31) +#define TCON1_CTL_INTERLACE_ENABLE BIT(20) +#define TCON1_CTL_Start_Delay_SHIFT 4 +#define TCON1_CTL_Start_Delay_MASK GENMASK(8, 4) +#define TCON1_BASIC0_REG 0x94 /* XI/YI */ +#define TCON1_BASIC1_REG 0x98 /* LS_XO/LS_YO */ +#define TCON1_BASIC2_REG 0x9c /* XO/YO */ +#define TCON1_BASIC3_REG 0xa0 /* HT/HBP */ +#define TCON1_BASIC4_REG 0xa4 /* VT/VBP */ +#define TCON1_BASIC5_REG 0xa8 /* HSPW/VSPW */ +#define TCON1_PS_SYNC_REG 0xb0 +#define TCON1_IO_POL_REG 0xf0 +#define TCON1_IO_POL_IO0_inv BIT(24) +#define TCON1_IO_POL_IO1_inv BIT(25) +#define TCON1_IO_POL_IO2_inv BIT(26) +#define TCON1_IO_TRI_REG 0xf4 +#define TCON_CEU_CTL_REG 0x100 +#define TCON_CEU_CTL_ceu_en BIT(31) +#define TCON1_FILL_CTL_REG 0x300 +#define TCON1_FILL_START0_REG 0x304 +#define TCON1_FILL_END0_REG 0x308 +#define TCON1_FILL_DATA0_REG 0x30c
+#define XY(x, y) (((x) << 16) | (y))
+#define andl_relaxed(addr, val) \
- writel_relaxed(readl_relaxed(addr) & val, addr)
+#define orl_relaxed(addr, val) \
- writel_relaxed(readl_relaxed(addr) | val, addr)
+/* vertical blank functions */
+static void de2_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
+{
- struct drm_pending_vblank_event *event = crtc->state->event;
- if (event) {
crtc->state->event = NULL;
spin_lock_irq(&crtc->dev->event_lock);
if (drm_crtc_vblank_get(crtc) == 0)
drm_crtc_arm_vblank_event(crtc, event);
else
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irq(&crtc->dev->event_lock);
- }
+}
+static irqreturn_t de2_lcd_irq(int irq, void *dev_id) +{
- struct lcd *lcd = (struct lcd *) dev_id;
- u32 isr;
- isr = readl_relaxed(lcd->mmio + TCON_GINT0_REG);
- drm_crtc_handle_vblank(&lcd->crtc);
- writel_relaxed(isr &
~(TCON_GINT0_TCON1_Vb_Int_Flag |
TCON_GINT0_TCON1_Vb_Line_Int_Flag),
lcd->mmio + TCON_GINT0_REG);
- return IRQ_HANDLED;
+}
+int de2_enable_vblank(struct drm_device *drm, unsigned int crtc_ix) +{
- struct priv *priv = drm_to_priv(drm);
- struct lcd *lcd = priv->lcds[crtc_ix];
- orl_relaxed(lcd->mmio + TCON_GINT0_REG, TCON_GINT0_TCON1_Vb_Int_En);
- return 0;
+}
+void de2_disable_vblank(struct drm_device *drm, unsigned int crtc_ix) +{
- struct priv *priv = drm_to_priv(drm);
- struct lcd *lcd = priv->lcds[crtc_ix];
- andl_relaxed(lcd->mmio + TCON_GINT0_REG, ~TCON_GINT0_TCON1_Vb_Int_En);
+}
+void de2_vblank_reset(struct lcd *lcd) +{
- drm_crtc_vblank_reset(&lcd->crtc);
+}
+/* frame functions */ +static int de2_crtc_set_clock(struct lcd *lcd, int rate) +{
- struct clk *parent_clk;
- u32 parent_rate;
- int ret;
- /* determine and set the best rate for the parent clock (pll-video) */
- if ((270000 * 2) % rate == 0)
parent_rate = 270000000;
- else if (297000 % rate == 0)
parent_rate = 297000000;
- else
return -EINVAL; /* unsupported clock */
- parent_clk = clk_get_parent(lcd->clk);
- ret = clk_set_rate(parent_clk, parent_rate);
- if (ret) {
dev_err(lcd->dev, "set parent rate failed %d\n", ret);
return ret;
- }
- ret = clk_set_rate(lcd->clk, rate * 1000);
- if (ret) {
dev_err(lcd->dev, "set rate failed %d\n", ret);
return ret;
- }
- /* enable the clock */
- reset_control_deassert(lcd->reset);
- clk_prepare_enable(lcd->bus);
- clk_prepare_enable(lcd->clk);
- return ret;
+}
+static void de2_tcon_init(struct lcd *lcd) +{
- andl_relaxed(lcd->mmio + TCON0_CTL_REG, ~TCON0_CTL_TCON_ENABLE);
- andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
- andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
- /* disable/ack interrupts */
- writel_relaxed(0, lcd->mmio + TCON_GINT0_REG);
+}
+static void de2_tcon_enable(struct lcd *lcd) +{
- struct drm_crtc *crtc = &lcd->crtc;
- const struct drm_display_mode *mode = &crtc->mode;
- int interlace = mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 1;
- int start_delay;
- u32 data;
- orl_relaxed(lcd->mmio + TCON_GCTL_REG, TCON_GCTL_TCON_ENABLE);
- data = XY(mode->hdisplay - 1, mode->vdisplay / interlace - 1);
- writel_relaxed(data, lcd->mmio + TCON1_BASIC0_REG);
- writel_relaxed(data, lcd->mmio + TCON1_BASIC1_REG);
- writel_relaxed(data, lcd->mmio + TCON1_BASIC2_REG);
- writel_relaxed(XY(mode->htotal - 1,
mode->htotal - mode->hsync_start - 1),
lcd->mmio + TCON1_BASIC3_REG);
- writel_relaxed(XY(mode->vtotal * (3 - interlace),
mode->vtotal - mode->vsync_start - 1),
lcd->mmio + TCON1_BASIC4_REG);
- writel_relaxed(XY(mode->hsync_end - mode->hsync_start - 1,
mode->vsync_end - mode->vsync_start - 1),
lcd->mmio + TCON1_BASIC5_REG);
- data = TCON1_IO_POL_IO2_inv;
- if (mode->flags & DRM_MODE_FLAG_PVSYNC)
data |= TCON1_IO_POL_IO0_inv;
- if (mode->flags & DRM_MODE_FLAG_PHSYNC)
data |= TCON1_IO_POL_IO1_inv;
- writel_relaxed(data, lcd->mmio + TCON1_IO_POL_REG);
- andl_relaxed(lcd->mmio + TCON_CEU_CTL_REG, ~TCON_CEU_CTL_ceu_en);
- if (interlace == 2)
orl_relaxed(lcd->mmio + TCON1_CTL_REG,
TCON1_CTL_INTERLACE_ENABLE);
- else
andl_relaxed(lcd->mmio + TCON1_CTL_REG,
~TCON1_CTL_INTERLACE_ENABLE);
- writel_relaxed(0, lcd->mmio + TCON1_FILL_CTL_REG);
- writel_relaxed(mode->vtotal + 1, lcd->mmio + TCON1_FILL_START0_REG);
- writel_relaxed(mode->vtotal, lcd->mmio + TCON1_FILL_END0_REG);
- writel_relaxed(0, lcd->mmio + TCON1_FILL_DATA0_REG);
- start_delay = (mode->vtotal - mode->vdisplay) / interlace - 5;
- if (start_delay > 31)
start_delay = 31;
- data = readl_relaxed(lcd->mmio + TCON1_CTL_REG);
- data &= ~TCON1_CTL_Start_Delay_MASK;
- data |= start_delay << TCON1_CTL_Start_Delay_SHIFT;
- writel_relaxed(data, lcd->mmio + TCON1_CTL_REG);
- orl_relaxed(lcd->mmio + TCON1_CTL_REG, TCON1_CTL_TCON_ENABLE);
+}
+static void de2_tcon_disable(struct lcd *lcd) +{
- andl_relaxed(lcd->mmio + TCON1_CTL_REG, ~TCON1_CTL_TCON_ENABLE);
- andl_relaxed(lcd->mmio + TCON_GCTL_REG, ~TCON_GCTL_TCON_ENABLE);
+}
+static void de2_crtc_enable(struct drm_crtc *crtc) +{
- struct lcd *lcd = crtc_to_lcd(crtc);
- struct drm_display_mode *mode = &crtc->mode;
- if (de2_crtc_set_clock(lcd, mode->clock) < 0)
return;
- lcd->clk_enabled = true;
- /* start the TCON and the DE */
- de2_tcon_enable(lcd);
- de2_de_enable(lcd);
- /* turn on blanking interrupt */
- drm_crtc_vblank_on(crtc);
+}
+static void de2_crtc_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
+{
- struct lcd *lcd = crtc_to_lcd(crtc);
- if (!lcd->clk_enabled)
return; /* already disabled */
- lcd->clk_enabled = false;
- de2_de_disable(lcd);
- drm_crtc_vblank_off(crtc);
- de2_tcon_disable(lcd);
- clk_disable_unprepare(lcd->clk);
- clk_disable_unprepare(lcd->bus);
- reset_control_assert(lcd->reset);
+}
+static const struct drm_crtc_funcs de2_crtc_funcs = {
- .destroy = drm_crtc_cleanup,
- .set_config = drm_atomic_helper_set_config,
- .page_flip = drm_atomic_helper_page_flip,
- .reset = drm_atomic_helper_crtc_reset,
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+static const struct drm_crtc_helper_funcs de2_crtc_helper_funcs = {
- .atomic_flush = de2_atomic_flush,
- .enable = de2_crtc_enable,
- .atomic_disable = de2_crtc_disable,
+};
+/* device init */ +static int de2_lcd_bind(struct device *dev, struct device *master,
void *data)
+{
- struct drm_device *drm = data;
- struct priv *priv = drm_to_priv(drm);
- struct lcd *lcd = dev_get_drvdata(dev);
- struct drm_crtc *crtc = &lcd->crtc;
- int ret, i, crtc_ix;
- lcd->priv = priv;
- /* set the CRTC reference */
- crtc_ix = drm_crtc_index(crtc);
- if (crtc_ix >= ARRAY_SIZE(priv->lcds)) {
dev_err(drm->dev, "Bad crtc index");
return -ENOENT;
- }
- priv->lcds[crtc_ix] = lcd;
- /* and the mixer index (DT port index in the DE) */
- for (i = 0; ; i++) {
struct device_node *port;
port = of_parse_phandle(drm->dev->of_node, "ports", i);
if (!port)
break;
if (port == lcd->crtc.port) {
lcd->mixer = i;
break;
}
- }
- ret = de2_plane_init(drm, lcd);
- if (ret < 0)
return ret;
- drm_crtc_helper_add(crtc, &de2_crtc_helper_funcs);
- return drm_crtc_init_with_planes(drm, crtc,
&lcd->planes[DE2_PRIMARY_PLANE],
&lcd->planes[DE2_CURSOR_PLANE],
&de2_crtc_funcs, NULL);
+}
+static void de2_lcd_unbind(struct device *dev, struct device *master,
void *data)
+{
- struct platform_device *pdev = to_platform_device(dev);
- struct lcd *lcd = platform_get_drvdata(pdev);
- if (lcd->priv)
lcd->priv->lcds[drm_crtc_index(&lcd->crtc)] = NULL;
+}
+static const struct component_ops de2_lcd_ops = {
- .bind = de2_lcd_bind,
- .unbind = de2_lcd_unbind,
+};
+static int de2_lcd_probe(struct platform_device *pdev) +{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node, *tmp, *parent, *port;
- struct lcd *lcd;
- struct resource *res;
- int id, irq, ret;
- lcd = devm_kzalloc(dev, sizeof(*lcd), GFP_KERNEL);
- if (!lcd)
return -ENOMEM;
- dev_set_drvdata(dev, lcd);
- lcd->dev = dev;
- lcd->mixer = id;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
dev_err(dev, "failed to get memory resource\n");
return -EINVAL;
- }
- lcd->mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR(lcd->mmio)) {
dev_err(dev, "failed to map registers\n");
return PTR_ERR(lcd->mmio);
- }
- /* possible CRTC */
- parent = np;
- tmp = of_get_child_by_name(np, "ports");
- if (tmp)
parent = tmp;
- port = of_get_child_by_name(parent, "port");
- of_node_put(tmp);
- if (!port) {
dev_err(dev, "no port node\n");
return -ENXIO;
- }
- lcd->crtc.port = port;
- lcd->bus = devm_clk_get(dev, "bus");
- if (IS_ERR(lcd->bus)) {
dev_err(dev, "get bus clock err %d\n", (int) PTR_ERR(lcd->bus));
ret = PTR_ERR(lcd->bus);
goto err;
- }
- lcd->clk = devm_clk_get(dev, "clock");
- if (IS_ERR(lcd->clk)) {
ret = PTR_ERR(lcd->clk);
dev_err(dev, "get video clock err %d\n", ret);
goto err;
- }
- lcd->reset = devm_reset_control_get(dev, NULL);
- if (IS_ERR(lcd->reset)) {
ret = PTR_ERR(lcd->reset);
dev_err(dev, "get reset err %d\n", ret);
goto err;
- }
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
dev_err(dev, "unable to get irq\n");
ret = -EINVAL;
goto err;
- }
- de2_tcon_init(lcd); /* stop TCON and avoid interrupts */
- ret = devm_request_irq(dev, irq, de2_lcd_irq, 0,
dev_name(dev), lcd);
- if (ret < 0) {
dev_err(dev, "unable to request irq %d\n", irq);
goto err;
- }
- return component_add(dev, &de2_lcd_ops);
+err:
- of_node_put(lcd->crtc.port);
- return ret;
+}
+static int de2_lcd_remove(struct platform_device *pdev) +{
- struct lcd *lcd = platform_get_drvdata(pdev);
- component_del(&pdev->dev, &de2_lcd_ops);
- of_node_put(lcd->crtc.port);
- return 0;
+}
+static const struct of_device_id de2_lcd_ids[] = {
- { .compatible = "allwinner,sun8i-a83t-tcon", },
- { }
+};
+struct platform_driver de2_lcd_platform_driver = {
- .probe = de2_lcd_probe,
- .remove = de2_lcd_remove,
- .driver = {
.name = "sun8i-de2-tcon",
.of_match_table = of_match_ptr(de2_lcd_ids),
- },
+}; diff --git a/drivers/gpu/drm/sun8i/de2_crtc.h b/drivers/gpu/drm/sun8i/de2_crtc.h new file mode 100644 index 0000000..c0d34a7 --- /dev/null +++ b/drivers/gpu/drm/sun8i/de2_crtc.h @@ -0,0 +1,50 @@ +#ifndef __DE2_CRTC_H__ +#define __DE2_CRTC_H__ +/*
- Copyright (C) 2016 Jean-Fran??ois Moine
- 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.
- */
+#include <drm/drm_plane_helper.h>
+struct clk; +struct reset_control; +struct priv;
+/* planes */ +#define DE2_PRIMARY_PLANE 0 +#define DE2_CURSOR_PLANE 1 +#define DE2_N_PLANES 5 /* number of planes - see plane_tb[] in de2_plane.c */
+struct lcd {
- void __iomem *mmio;
- struct device *dev;
- struct drm_crtc crtc;
- struct priv *priv; /* DRM/DE private data */
- u8 mixer; /* LCD (mixer) number */
- u8 delayed; /* bitmap of planes with delayed update */
- u8 clk_enabled; /* used for error in crtc_enable */
- struct clk *clk;
- struct clk *bus;
- struct reset_control *reset;
- struct drm_plane planes[DE2_N_PLANES];
+};
+#define crtc_to_lcd(x) container_of(x, struct lcd, crtc)
+/* in de2_plane.c */ +void de2_de_enable(struct lcd *lcd); +void de2_de_disable(struct lcd *lcd); +int de2_plane_init(struct drm_device *drm, struct lcd *lcd);
+#endif /* __DE2_CRTC_H__ */ diff --git a/drivers/gpu/drm/sun8i/de2_drv.c b/drivers/gpu/drm/sun8i/de2_drv.c new file mode 100644 index 0000000..f96babe --- /dev/null +++ b/drivers/gpu/drm/sun8i/de2_drv.c @@ -0,0 +1,317 @@ +/*
- Allwinner DRM driver - DE2 DRM driver
- Copyright (C) 2016 Jean-Francois Moine moinejf@free.fr
- 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.
- */
+#include <linux/module.h> +#include <linux/of_device.h> +#include <drm/drm_of.h> +#include <linux/component.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h>
+#include "de2_drv.h"
+#define DRIVER_NAME "sun8i-de2" +#define DRIVER_DESC "Allwinner DRM DE2" +#define DRIVER_DATE "20161101" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0
+static const struct of_device_id de2_drm_of_match[] = {
- { .compatible = "allwinner,sun8i-a83t-display-engine",
.data = (void *) SOC_A83T },
- { .compatible = "allwinner,sun8i-h3-display-engine",
.data = (void *) SOC_H3 },
- { },
+}; +MODULE_DEVICE_TABLE(of, de2_drm_of_match);
+static void de2_fb_output_poll_changed(struct drm_device *drm) +{
- struct priv *priv = drm_to_priv(drm);
- if (priv->fbdev)
drm_fbdev_cma_hotplug_event(priv->fbdev);
+}
+static const struct drm_mode_config_funcs de2_mode_config_funcs = {
- .fb_create = drm_fb_cma_create,
- .output_poll_changed = de2_fb_output_poll_changed,
- .atomic_check = drm_atomic_helper_check,
- .atomic_commit = drm_atomic_helper_commit,
+};
+/* -- DRM operations -- */
+static void de2_lastclose(struct drm_device *drm) +{
- struct priv *priv = drm_to_priv(drm);
- if (priv->fbdev)
drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+static const struct file_operations de2_fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
- .poll = drm_poll,
- .read = drm_read,
- .llseek = no_llseek,
- .mmap = drm_gem_cma_mmap,
+};
+static struct drm_driver de2_drm_driver = {
- .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
DRIVER_ATOMIC,
- .lastclose = de2_lastclose,
- .get_vblank_counter = drm_vblank_no_hw_counter,
- .enable_vblank = de2_enable_vblank,
- .disable_vblank = de2_disable_vblank,
- .gem_free_object = drm_gem_cma_free_object,
- .gem_vm_ops = &drm_gem_cma_vm_ops,
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_import = drm_gem_prime_import,
- .gem_prime_export = drm_gem_prime_export,
- .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
- .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
- .gem_prime_vmap = drm_gem_cma_prime_vmap,
- .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
- .gem_prime_mmap = drm_gem_cma_prime_mmap,
- .dumb_create = drm_gem_cma_dumb_create,
- .dumb_map_offset = drm_gem_cma_dumb_map_offset,
- .dumb_destroy = drm_gem_dumb_destroy,
- .fops = &de2_fops,
- .name = DRIVER_NAME,
- .desc = DRIVER_DESC,
- .date = DRIVER_DATE,
- .major = DRIVER_MAJOR,
- .minor = DRIVER_MINOR,
+};
+/*
- Platform driver
- */
+static int de2_drm_bind(struct device *dev) +{
- struct drm_device *drm;
- struct priv *priv;
- struct resource *res;
- struct lcd *lcd;
- int i, ret;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
return -ENOMEM;
- drm = &priv->drm;
- dev_set_drvdata(dev, drm);
- /* get the resources */
- priv->soc_type = (int) of_match_device(de2_drm_of_match, dev)->data;
- res = platform_get_resource(to_platform_device(dev),
IORESOURCE_MEM, 0);
- if (!res) {
dev_err(dev, "failed to get memory resource\n");
ret = -EINVAL;
goto out1;
- }
- priv->mmio = devm_ioremap_resource(dev, res);
- if (IS_ERR(priv->mmio)) {
ret = PTR_ERR(priv->mmio);
dev_err(dev, "failed to map registers %d\n", ret);
goto out1;
- }
- priv->gate = devm_clk_get(dev, "bus");
- if (IS_ERR(priv->gate)) {
ret = PTR_ERR(priv->gate);
dev_err(dev, "bus gate err %d\n", ret);
goto out1;
- }
- priv->clk = devm_clk_get(dev, "clock");
- if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
dev_err(dev, "clock err %d\n", ret);
goto out1;
- }
- priv->reset = devm_reset_control_get(dev, NULL);
- if (IS_ERR(priv->reset)) {
ret = PTR_ERR(priv->reset);
dev_err(dev, "reset err %d\n", ret);
goto out1;
- }
- mutex_init(&priv->mutex); /* protect DE I/O accesses */
- ret = drm_dev_init(drm, &de2_drm_driver, dev);
- if (ret != 0) {
dev_err(dev, "dev_init failed %d\n", ret);
goto out1;
- }
- drm_mode_config_init(drm);
- drm->mode_config.min_width = 32; /* needed for cursor */
- drm->mode_config.min_height = 32;
- drm->mode_config.max_width = 1920;
- drm->mode_config.max_height = 1080;
- drm->mode_config.funcs = &de2_mode_config_funcs;
- drm->irq_enabled = true;
- /* start the subdevices */
- ret = component_bind_all(dev, drm);
- if (ret < 0)
goto out2;
- /* initialize and disable vertical blanking on all CRTCs */
- ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
- if (ret < 0)
dev_warn(dev, "vblank_init failed %d\n", ret);
- for (i = 0; i < ARRAY_SIZE(priv->lcds); i++) {
lcd = priv->lcds[i];
if (lcd)
de2_vblank_reset(lcd);
- }
- drm_mode_config_reset(drm);
- priv->fbdev = drm_fbdev_cma_init(drm,
32, /* bpp */
drm->mode_config.num_crtc,
drm->mode_config.num_connector);
- if (IS_ERR(priv->fbdev)) {
ret = PTR_ERR(priv->fbdev);
priv->fbdev = NULL;
goto out3;
- }
- drm_kms_helper_poll_init(drm);
- ret = drm_dev_register(drm, 0);
- if (ret < 0)
goto out4;
- return 0;
+out4:
- drm_fbdev_cma_fini(priv->fbdev);
+out3:
- component_unbind_all(dev, drm);
+out2:
- drm_dev_unref(drm);
+out1:
- kfree(priv);
- return ret;
+}
+static void de2_drm_unbind(struct device *dev) +{
- struct drm_device *drm = dev_get_drvdata(dev);
- struct priv *priv = drm_to_priv(drm);
- drm_dev_unregister(drm);
- drm_fbdev_cma_fini(priv->fbdev);
- drm_kms_helper_poll_fini(drm);
- drm_vblank_cleanup(drm);
- drm_mode_config_cleanup(drm);
- component_unbind_all(dev, drm);
- kfree(priv);
+}
+static const struct component_master_ops de2_drm_comp_ops = {
- .bind = de2_drm_bind,
- .unbind = de2_drm_unbind,
+};
+/*
- drm_of_component_probe() does:
- bind of the ports (lcd-controller.port)
- bind of the remote nodes (hdmi, tve..)
- */
+static int compare_of(struct device *dev, void *data) +{
- struct device_node *np = data;
- if (of_node_cmp(np->name, "port") == 0) {
np = of_get_parent(np);
of_node_put(np);
- }
- return dev->of_node == np;
+}
+static int de2_drm_probe(struct platform_device *pdev) +{
- int ret;
- ret = drm_of_component_probe(&pdev->dev,
compare_of,
&de2_drm_comp_ops);
- if (ret == -EINVAL)
ret = -ENXIO;
- return ret;
+}
+static int de2_drm_remove(struct platform_device *pdev) +{
- component_master_del(&pdev->dev, &de2_drm_comp_ops);
- return 0;
+}
+static struct platform_driver de2_drm_platform_driver = {
- .probe = de2_drm_probe,
- .remove = de2_drm_remove,
- .driver = {
.name = DRIVER_NAME,
.of_match_table = de2_drm_of_match,
- },
+};
+static int __init de2_drm_init(void) +{
- int ret;
- ret = platform_driver_register(&de2_lcd_platform_driver);
- if (ret < 0)
return ret;
- ret = platform_driver_register(&de2_drm_platform_driver);
- if (ret < 0)
platform_driver_unregister(&de2_lcd_platform_driver);
- return ret;
+}
+static void __exit de2_drm_fini(void) +{
- platform_driver_unregister(&de2_lcd_platform_driver);
- platform_driver_unregister(&de2_drm_platform_driver);
+}
+module_init(de2_drm_init); +module_exit(de2_drm_fini);
+MODULE_AUTHOR("Jean-Francois Moine moinejf@free.fr"); +MODULE_DESCRIPTION("Allwinner DE2 DRM Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/sun8i/de2_drv.h b/drivers/gpu/drm/sun8i/de2_drv.h new file mode 100644 index 0000000..c42c30a --- /dev/null +++ b/drivers/gpu/drm/sun8i/de2_drv.h @@ -0,0 +1,48 @@ +#ifndef __DE2_DRM_H__ +#define __DE2_DRM_H__ +/*
- Copyright (C) 2016 Jean-Fran??ois Moine
- 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.
- */
+#include <drm/drmP.h> +#include <linux/clk.h> +#include <linux/reset.h>
+struct drm_fbdev_cma; +struct lcd;
+#define N_LCDS 2
+struct priv {
- struct drm_device drm;
- void __iomem *mmio;
- struct clk *clk;
- struct clk *gate;
- struct reset_control *reset;
- struct mutex mutex; /* protect DE I/O access */
- u8 soc_type;
+#define SOC_A83T 0 +#define SOC_H3 1
- u8 started; /* bitmap of started mixers */
- u8 clean; /* bitmap of clean mixers */
- struct drm_fbdev_cma *fbdev;
- struct lcd *lcds[N_LCDS]; /* CRTCs */
+};
+#define drm_to_priv(x) container_of(x, struct priv, drm)
+/* in de2_crtc.c */ +int de2_enable_vblank(struct drm_device *drm, unsigned int crtc); +void de2_disable_vblank(struct drm_device *drm, unsigned int crtc); +void de2_vblank_reset(struct lcd *lcd); +extern struct platform_driver de2_lcd_platform_driver;
+#endif /* __DE2_DRM_H__ */ diff --git a/drivers/gpu/drm/sun8i/de2_plane.c b/drivers/gpu/drm/sun8i/de2_plane.c new file mode 100644 index 0000000..2fd72dc --- /dev/null +++ b/drivers/gpu/drm/sun8i/de2_plane.c @@ -0,0 +1,734 @@ +/*
- Allwinner DRM driver - Display Engine 2
- Copyright (C) 2016 Jean-Francois Moine moinejf@free.fr
- Adapted from the sun8iw6 and sun8iw7 disp2 drivers
- Copyright (c) 2016 Allwinnertech Co., Ltd.
- 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.
- */
+#include <linux/io.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h>
+#include "de2_drv.h" +#include "de2_crtc.h"
+/* DE2 I/O map */
+#define DE2_MOD_REG 0x0000 /* 1 bit per LCD */ +#define DE2_GATE_REG 0x0004 +#define DE2_RESET_REG 0x0008 +#define DE2_DIV_REG 0x000c /* 4 bits per LCD */ +#define DE2_SEL_REG 0x0010
+#define DE2_MIXER0_BASE 0x00100000 /* LCD 0 */ +#define DE2_MIXER1_BASE 0x00200000 /* LCD 1 */
+/* mixer registers (addr / mixer base) */ +#define MIXER_GLB_REGS 0x00000 /* global control */ +#define MIXER_BLD_REGS 0x01000 /* alpha blending */ +#define MIXER_CHAN_REGS 0x02000 /* VI/UI overlay channels */ +#define MIXER_CHAN_SZ 0x1000 /* size of a channel */ +#define MIXER_VSU_REGS 0x20000 /* VSU */ +#define MIXER_GSU1_REGS 0x30000 /* GSUs */ +#define MIXER_GSU2_REGS 0x40000 +#define MIXER_GSU3_REGS 0x50000 +#define MIXER_FCE_REGS 0xa0000 /* FCE */ +#define MIXER_BWS_REGS 0xa2000 /* BWS */ +#define MIXER_LTI_REGS 0xa4000 /* LTI */ +#define MIXER_PEAK_REGS 0xa6000 /* PEAK */ +#define MIXER_ASE_REGS 0xa8000 /* ASE */ +#define MIXER_FCC_REGS 0xaa000 /* FCC */ +#define MIXER_DCSC_REGS 0xb0000 /* DCSC/SMBL */
+/* global control */ +#define MIXER_GLB_CTL_REG 0x00 +#define MIXER_GLB_CTL_rt_en BIT(0) +#define MIXER_GLB_CTL_finish_irq_en BIT(4) +#define MIXER_GLB_CTL_rtwb_port BIT(12) +#define MIXER_GLB_STATUS_REG 0x04 +#define MIXER_GLB_DBUFF_REG 0x08 +#define MIXER_GLB_SIZE_REG 0x0c
+/* alpha blending */ +#define MIXER_BLD_FCOLOR_CTL_REG 0x00 +#define MIXER_BLD_FCOLOR_CTL_PEN(pipe) (0x0100 << (pipe)) +#define MIXER_BLD_ATTR_N 4 /* number of attribute blocks */ +#define MIXER_BLD_ATTR_SIZE (4 * 4) /* size of an attribute block */ +#define MIXER_BLD_ATTRx_FCOLOR(x) (0x04 + MIXER_BLD_ATTR_SIZE * (x)) +#define MIXER_BLD_ATTRx_INSIZE(x) (0x08 + MIXER_BLD_ATTR_SIZE * (x)) +#define MIXER_BLD_ATTRx_OFFSET(x) (0x0c + MIXER_BLD_ATTR_SIZE * (x)) +#define MIXER_BLD_ROUTE_REG 0x80 +#define MIXER_BLD_ROUTE(chan, pipe) ((chan) << ((pipe) * 4)) +#define MIXER_BLD_PREMULTIPLY_REG 0x84 +#define MIXER_BLD_BKCOLOR_REG 0x88 +#define MIXER_BLD_OUTPUT_SIZE_REG 0x8c +#define MIXER_BLD_MODEx_REG(x) (0x90 + 4 * (x)) /* x = 0..3 */ +#define MIXER_BLD_MODE_SRCOVER 0x03010301 +#define MIXER_BLD_OUT_CTL_REG 0xfc
+/* VI channel (channel 0) */ +#define VI_CFG_N 4 /* number of layers */ +#define VI_CFG_SIZE 0x30 /* size of a layer */ +#define VI_CFGx_ATTR(l) (0x00 + VI_CFG_SIZE * (l)) +#define VI_CFG_ATTR_en BIT(0) +#define VI_CFG_ATTR_fcolor_en BIT(4) +#define VI_CFG_ATTR_fmt_SHIFT 8 +#define VI_CFG_ATTR_fmt_MASK GENMASK(12, 8) +#define VI_CFG_ATTR_ui_sel BIT(15) +#define VI_CFG_ATTR_top_down BIT(23) +#define VI_CFGx_SIZE(l) (0x04 + VI_CFG_SIZE * (l)) +#define VI_CFGx_COORD(l) (0x08 + VI_CFG_SIZE * (l)) +#define VI_N_PLANES 3 +#define VI_CFGx_PITCHy(l, p) (0x0c + VI_CFG_SIZE * (l) + 4 * (p)) +#define VI_CFGx_TOP_LADDRy(l, p) (0x18 + VI_CFG_SIZE * (l) + 4 * (p)) +#define VI_CFGx_BOT_LADDRy(l, p) (0x24 + VI_CFG_SIZE * (l) + 4 * (p)) +#define VI_FCOLORx(l) (0xc0 + 4 * (l)) +#define VI_TOP_HADDRx(p) (0xd0 + 4 * (p)) +#define VI_BOT_HADDRx(p) (0xdc + 4 * (p)) +#define VI_OVL_SIZEx(n) (0xe8 + 4 * (n)) +#define VI_HORI_DSx(n) (0xf0 + 4 * (n)) +#define VI_VERT_DSx(n) (0xf8 + 4 * (n)) +#define VI_SIZE 0x100
+/* UI channel (channels 1..3) */ +#define UI_CFG_N 4 /* number of layers */ +#define UI_CFG_SIZE (8 * 4) /* size of a layer */ +#define UI_CFGx_ATTR(l) (0x00 + UI_CFG_SIZE * (l)) +#define UI_CFG_ATTR_en BIT(0) +#define UI_CFG_ATTR_alpmod_SHIFT 1 +#define UI_CFG_ATTR_alpmod_MASK GENMASK(2, 1) +#define UI_CFG_ATTR_fcolor_en BIT(4) +#define UI_CFG_ATTR_fmt_SHIFT 8 +#define UI_CFG_ATTR_fmt_MASK GENMASK(12, 8) +#define UI_CFG_ATTR_top_down BIT(23) +#define UI_CFG_ATTR_alpha_SHIFT 24 +#define UI_CFG_ATTR_alpha_MASK GENMASK(31, 24) +#define UI_CFGx_SIZE(l) (0x04 + UI_CFG_SIZE * (l)) +#define UI_CFGx_COORD(l) (0x08 + UI_CFG_SIZE * (l)) +#define UI_CFGx_PITCH(l) (0x0c + UI_CFG_SIZE * (l)) +#define UI_CFGx_TOP_LADDR(l) (0x10 + UI_CFG_SIZE * (l)) +#define UI_CFGx_BOT_LADDR(l) (0x14 + UI_CFG_SIZE * (l)) +#define UI_CFGx_FCOLOR(l) (0x18 + UI_CFG_SIZE * (l)) +#define UI_TOP_HADDR 0x80 +#define UI_BOT_HADDR 0x84 +#define UI_OVL_SIZE 0x88 +#define UI_SIZE 0x8c
+/* coordinates and sizes */ +#define XY(x, y) (((y) << 16) | (x)) +#define WH(w, h) ((((h) - 1) << 16) | ((w) - 1))
+/* UI video formats */ +#define DE2_FORMAT_ARGB_8888 0 +#define DE2_FORMAT_BGRA_8888 3 +#define DE2_FORMAT_XRGB_8888 4 +#define DE2_FORMAT_RGB_888 8 +#define DE2_FORMAT_BGR_888 9
+/* VI video formats */ +#define DE2_FORMAT_YUV422_I_YVYU 1 /* YVYU */ +#define DE2_FORMAT_YUV422_I_UYVY 2 /* UYVY */ +#define DE2_FORMAT_YUV422_I_YUYV 3 /* YUYV */ +#define DE2_FORMAT_YUV422_P 6 /* YYYY UU VV planar */ +#define DE2_FORMAT_YUV420_P 10 /* YYYY U V planar */
+/* plane formats */ +static const uint32_t ui_formats[] = {
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_BGRA8888,
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_RGB888,
- DRM_FORMAT_BGR888,
+};
+static const uint32_t vi_formats[] = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_YUYV,
- DRM_FORMAT_YVYU,
- DRM_FORMAT_YUV422,
- DRM_FORMAT_YUV420,
- DRM_FORMAT_UYVY,
- DRM_FORMAT_BGRA8888,
- DRM_FORMAT_RGB888,
- DRM_FORMAT_BGR888,
+};
+/*
- plane table
- The chosen channel/layer assignment of the planes respects
- the following constraints:
- the cursor must be in a channel higher than the primary channel
- there are 4 channels in the LCD 0 and only 2 channels in the LCD 1
- */
+static const struct {
- u8 chan;
- u8 layer;
- u8 pipe;
- u8 type; /* plane type */
- const uint32_t *formats;
- u8 n_formats;
+} plane_tb[] = {
- [DE2_PRIMARY_PLANE] = { /* primary plane: channel 0 (VI) */
0, 0, 0,
DRM_PLANE_TYPE_PRIMARY,
ui_formats, ARRAY_SIZE(ui_formats),
- },
- [DE2_CURSOR_PLANE] = { /* cursor: channel 1 (UI) */
1, 0, 1,
DRM_PLANE_TYPE_CURSOR,
ui_formats, ARRAY_SIZE(ui_formats),
- },
- {
0, 1, 0, /* 1st overlay: channel 0, layer 1 */
DRM_PLANE_TYPE_OVERLAY,
vi_formats, ARRAY_SIZE(vi_formats),
- },
- {
0, 2, 0, /* 2nd overlay: channel 0, layer 2 */
DRM_PLANE_TYPE_OVERLAY,
vi_formats, ARRAY_SIZE(vi_formats),
- },
- {
0, 3, 0, /* 3rd overlay: channel 0, layer 3 */
DRM_PLANE_TYPE_OVERLAY,
vi_formats, ARRAY_SIZE(vi_formats),
- },
+};
+static inline void andl_relaxed(void __iomem *addr, u32 val) +{
- writel_relaxed(readl_relaxed(addr) & val, addr);
+}
+static inline void orl_relaxed(void __iomem *addr, u32 val) +{
- writel_relaxed(readl_relaxed(addr) | val, addr);
+}
+/* alert the DE processor about changes in a mixer configuration */ +static void de2_mixer_select(struct priv *priv,
int mixer,
void __iomem *mixer_io)
+{
- /* select the mixer */
- andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
- /* double register switch */
- writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
+}
+/*
- cleanup a mixer
- This is needed only once after power on.
- */
+static void de2_mixer_cleanup(struct priv *priv, int mixer,
u32 size)
+{
- void __iomem *mixer_io = priv->mmio;
- void __iomem *chan_io;
- u32 data;
- unsigned int i;
- mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
- chan_io = mixer_io + MIXER_CHAN_REGS;
- andl_relaxed(priv->mmio + DE2_SEL_REG, ~1);
- writel_relaxed(1, mixer_io + MIXER_GLB_REGS + MIXER_GLB_DBUFF_REG);
- writel_relaxed(MIXER_GLB_CTL_rt_en,
mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
- writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
- writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
- /*
* clear the VI/UI channels
* LCD0: 1 VI and 3 UIs
* LCD1: 1 VI and 1 UI
*/
- memset_io(chan_io, 0, VI_SIZE);
- memset_io(chan_io + MIXER_CHAN_SZ, 0, UI_SIZE);
- if (mixer == 0) {
memset_io(chan_io + MIXER_CHAN_SZ * 2, 0, UI_SIZE);
memset_io(chan_io + MIXER_CHAN_SZ * 3, 0, UI_SIZE);
- }
- /* alpha blending */
- writel_relaxed(0x00000001 | /* fcolor for primary */
MIXER_BLD_FCOLOR_CTL_PEN(0),
mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
- for (i = 0; i < MIXER_BLD_ATTR_N; i++) {
writel_relaxed(0xff000000,
mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_FCOLOR(i));
writel_relaxed(size,
mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_INSIZE(i));
writel_relaxed(0,
mixer_io + MIXER_BLD_REGS + MIXER_BLD_ATTRx_OFFSET(i));
- }
- writel_relaxed(0, mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
- /* prepare the pipe route for the planes */
- data = 0;
- for (i = 0; i < DE2_N_PLANES; i++)
data |= MIXER_BLD_ROUTE(plane_tb[i].chan, plane_tb[i].pipe);
- writel_relaxed(data, mixer_io + MIXER_BLD_REGS + MIXER_BLD_ROUTE_REG);
- writel_relaxed(0, mixer_io + MIXER_BLD_REGS +
MIXER_BLD_PREMULTIPLY_REG);
- writel_relaxed(0xff000000, mixer_io + MIXER_BLD_REGS +
MIXER_BLD_BKCOLOR_REG);
- writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
MIXER_BLD_OUTPUT_SIZE_REG);
- writel_relaxed(MIXER_BLD_MODE_SRCOVER,
mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(0));
- writel_relaxed(MIXER_BLD_MODE_SRCOVER,
mixer_io + MIXER_BLD_REGS + MIXER_BLD_MODEx_REG(1));
- /* disable the enhancements */
- writel_relaxed(0, mixer_io + MIXER_VSU_REGS);
- writel_relaxed(0, mixer_io + MIXER_GSU1_REGS);
- writel_relaxed(0, mixer_io + MIXER_GSU2_REGS);
- writel_relaxed(0, mixer_io + MIXER_GSU3_REGS);
- writel_relaxed(0, mixer_io + MIXER_FCE_REGS);
- writel_relaxed(0, mixer_io + MIXER_BWS_REGS);
- writel_relaxed(0, mixer_io + MIXER_LTI_REGS);
- writel_relaxed(0, mixer_io + MIXER_PEAK_REGS);
- writel_relaxed(0, mixer_io + MIXER_ASE_REGS);
- writel_relaxed(0, mixer_io + MIXER_FCC_REGS);
- writel_relaxed(0, mixer_io + MIXER_DCSC_REGS);
+}
+/* enable a mixer */ +static void de2_mixer_enable(struct lcd *lcd) +{
- struct priv *priv = lcd->priv;
- void __iomem *mixer_io = priv->mmio;
- struct drm_display_mode *mode = &lcd->crtc.mode;
- u32 size = WH(mode->hdisplay, mode->vdisplay);
- u32 data;
- int mixer = lcd->mixer;
- int i;
- mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
- /* if not done yet, start the DE processor */
- if (!priv->started) {
reset_control_deassert(priv->reset);
clk_prepare_enable(priv->gate);
clk_prepare_enable(priv->clk);
- }
- priv->started |= 1 << mixer;
- /* set the A83T clock divider (500 / 2) = 250MHz */
- if (priv->soc_type == SOC_A83T)
writel_relaxed(0x00000011, /* div = 2 for both LCDs */
priv->mmio + DE2_DIV_REG);
- /* deassert the mixer and enable its clock */
- orl_relaxed(priv->mmio + DE2_RESET_REG, mixer == 0 ? 1 : 4);
- data = 1 << mixer; /* 1 bit / lcd */
- orl_relaxed(priv->mmio + DE2_GATE_REG, data);
- orl_relaxed(priv->mmio + DE2_MOD_REG, data);
- /* if not done yet, cleanup and enable */
- if (!(priv->clean & (1 << mixer))) {
priv->clean |= 1 << mixer;
de2_mixer_cleanup(priv, mixer, size);
return;
- }
- /* enable */
- de2_mixer_select(priv, mixer, mixer_io);
- writel_relaxed(MIXER_GLB_CTL_rt_en,
mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
- writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_STATUS_REG);
- /* set the size of the frame buffer */
- writel_relaxed(size, mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
- for (i = 0; i < MIXER_BLD_ATTR_N; i++)
writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
MIXER_BLD_ATTRx_INSIZE(i));
- writel_relaxed(size, mixer_io + MIXER_BLD_REGS +
MIXER_BLD_OUTPUT_SIZE_REG);
- writel_relaxed(mode->flags & DRM_MODE_FLAG_INTERLACE ? 2 : 0,
mixer_io + MIXER_BLD_REGS + MIXER_BLD_OUT_CTL_REG);
+}
+/* enable a LCD (DE mixer) */ +void de2_de_enable(struct lcd *lcd) +{
- mutex_lock(&lcd->priv->mutex);
- de2_mixer_enable(lcd);
- mutex_unlock(&lcd->priv->mutex);
+}
+/* disable a LCD (DE mixer) */ +void de2_de_disable(struct lcd *lcd) +{
- struct priv *priv = lcd->priv;
- void __iomem *mixer_io = priv->mmio;
- int mixer = lcd->mixer;
- u32 data;
- mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
- mutex_lock(&priv->mutex);
- de2_mixer_select(priv, mixer, mixer_io);
- writel_relaxed(0, mixer_io + MIXER_GLB_REGS + MIXER_GLB_CTL_REG);
- data = ~(1 << mixer);
- andl_relaxed(priv->mmio + DE2_MOD_REG, data);
- andl_relaxed(priv->mmio + DE2_GATE_REG, data);
- andl_relaxed(priv->mmio + DE2_RESET_REG, data);
- mutex_unlock(&priv->mutex);
- /* if all mixers are disabled, stop the DE */
- priv->started &= ~(1 << mixer);
- if (!priv->started) {
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->gate);
reset_control_assert(priv->reset);
- }
+}
+static void de2_vi_update(void __iomem *chan_io,
struct drm_gem_cma_object *gem,
int layer,
unsigned int fmt,
u32 ui_sel,
u32 size,
u32 coord,
struct drm_framebuffer *fb,
u32 screen_size)
+{
- int i;
- writel_relaxed(VI_CFG_ATTR_en |
(fmt << VI_CFG_ATTR_fmt_SHIFT) |
ui_sel,
chan_io + VI_CFGx_ATTR(layer));
- writel_relaxed(size, chan_io + VI_CFGx_SIZE(layer));
- writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
- for (i = 0; i < VI_N_PLANES; i++) {
writel_relaxed(fb->pitches[i] ? fb->pitches[i] :
fb->pitches[0],
chan_io + VI_CFGx_PITCHy(layer, i));
writel_relaxed(gem->paddr + fb->offsets[i],
chan_io + VI_CFGx_TOP_LADDRy(layer, i));
- }
- writel_relaxed(0xff000000, chan_io + VI_FCOLORx(layer));
- if (layer == 0) {
writel_relaxed(screen_size,
chan_io + VI_OVL_SIZEx(0));
- }
+}
+static void de2_ui_update(void __iomem *chan_io,
struct drm_gem_cma_object *gem,
int layer,
unsigned int fmt,
u32 alpha_glob,
u32 size,
u32 coord,
struct drm_framebuffer *fb,
u32 screen_size)
+{
- writel_relaxed(UI_CFG_ATTR_en |
(fmt << UI_CFG_ATTR_fmt_SHIFT) |
alpha_glob,
chan_io + UI_CFGx_ATTR(layer));
- writel_relaxed(size, chan_io + UI_CFGx_SIZE(layer));
- writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
- writel_relaxed(fb->pitches[0], chan_io + UI_CFGx_PITCH(layer));
- writel_relaxed(gem->paddr + fb->offsets[0],
chan_io + UI_CFGx_TOP_LADDR(layer));
- if (layer == 0)
writel_relaxed(screen_size, chan_io + UI_OVL_SIZE);
+}
+static void de2_plane_update(struct priv *priv, struct lcd *lcd,
int plane_num,
struct drm_plane_state *state,
struct drm_plane_state *old_state)
+{
- void __iomem *mixer_io = priv->mmio;
- void __iomem *chan_io;
- struct drm_framebuffer *fb = state->fb;
- struct drm_gem_cma_object *gem;
- u32 size = WH(state->crtc_w, state->crtc_h);
- u32 coord, screen_size;
- u32 fcolor;
- u32 ui_sel, alpha_glob;
- int mixer = lcd->mixer;
- int chan, layer, x, y;
- unsigned int fmt;
- chan = plane_tb[plane_num].chan;
- layer = plane_tb[plane_num].layer;
- mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
- chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
- x = state->crtc_x >= 0 ? state->crtc_x : 0;
- y = state->crtc_y >= 0 ? state->crtc_y : 0;
- coord = XY(x, y);
- /* if plane update was delayed, force a full update */
- if (priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &
(1 << plane_num)) {
priv->lcds[drm_crtc_index(&lcd->crtc)]->delayed &=
~(1 << plane_num);
- /* handle plane move */
- } else if (fb == old_state->fb) {
de2_mixer_select(priv, mixer, mixer_io);
if (chan == 0)
writel_relaxed(coord, chan_io + VI_CFGx_COORD(layer));
else
writel_relaxed(coord, chan_io + UI_CFGx_COORD(layer));
return;
- }
- gem = drm_fb_cma_get_gem_obj(fb, 0);
- ui_sel = alpha_glob = 0;
- switch (fb->pixel_format) {
- case DRM_FORMAT_ARGB8888:
fmt = DE2_FORMAT_ARGB_8888;
ui_sel = VI_CFG_ATTR_ui_sel;
break;
- case DRM_FORMAT_BGRA8888:
fmt = DE2_FORMAT_BGRA_8888;
ui_sel = VI_CFG_ATTR_ui_sel;
break;
- case DRM_FORMAT_XRGB8888:
fmt = DE2_FORMAT_XRGB_8888;
ui_sel = VI_CFG_ATTR_ui_sel;
alpha_glob = (1 << UI_CFG_ATTR_alpmod_SHIFT) |
(0xff << UI_CFG_ATTR_alpha_SHIFT);
break;
- case DRM_FORMAT_RGB888:
fmt = DE2_FORMAT_RGB_888;
ui_sel = VI_CFG_ATTR_ui_sel;
break;
- case DRM_FORMAT_BGR888:
fmt = DE2_FORMAT_BGR_888;
ui_sel = VI_CFG_ATTR_ui_sel;
break;
- case DRM_FORMAT_YUYV:
fmt = DE2_FORMAT_YUV422_I_YUYV;
break;
- case DRM_FORMAT_YVYU:
fmt = DE2_FORMAT_YUV422_I_YVYU;
break;
- case DRM_FORMAT_YUV422:
fmt = DE2_FORMAT_YUV422_P;
break;
- case DRM_FORMAT_YUV420:
fmt = DE2_FORMAT_YUV420_P;
break;
- case DRM_FORMAT_UYVY:
fmt = DE2_FORMAT_YUV422_I_UYVY;
break;
- default:
pr_err("de2_plane_update: format %.4s not yet treated\n",
(char *) &fb->pixel_format);
return;
- }
- /* the overlay size is the one of the primary plane */
- screen_size = plane_num == DE2_PRIMARY_PLANE ?
size :
readl_relaxed(mixer_io + MIXER_GLB_REGS + MIXER_GLB_SIZE_REG);
- /* prepare pipe enable */
- fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
MIXER_BLD_FCOLOR_CTL_REG);
- fcolor |= MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe);
- de2_mixer_select(priv, mixer, mixer_io);
- if (chan == 0) /* VI channel */
de2_vi_update(chan_io, gem, layer, fmt, ui_sel, size, coord,
fb, screen_size);
- else /* UI channel */
de2_ui_update(chan_io, gem, layer, fmt, alpha_glob, size, coord,
fb, screen_size);
- writel_relaxed(fcolor, mixer_io + MIXER_BLD_REGS +
MIXER_BLD_FCOLOR_CTL_REG);
+}
+static int vi_nb_layers(void __iomem *chan_io) +{
- int layer, n = 0;
- for (layer = 0; layer < 4; layer++) {
if (readl_relaxed(chan_io + VI_CFGx_ATTR(layer)) != 0)
n++;
- }
- return n;
+}
+static int ui_nb_layers(void __iomem *chan_io) +{
- int layer, n = 0;
- for (layer = 0; layer < 4; layer++) {
if (readl_relaxed(chan_io + UI_CFGx_ATTR(layer)) != 0)
n++;
- }
- return n;
+}
+static void de2_plane_disable(struct priv *priv,
int mixer, int plane_num)
+{
- void __iomem *mixer_io = priv->mmio;
- void __iomem *chan_io;
- u32 fcolor;
- int chan, layer, n;
- chan = plane_tb[plane_num].chan;
- layer = plane_tb[plane_num].layer;
- mixer_io += (mixer == 0) ? DE2_MIXER0_BASE : DE2_MIXER1_BASE;
- chan_io = mixer_io + MIXER_CHAN_REGS + MIXER_CHAN_SZ * chan;
- if (chan == 0)
n = vi_nb_layers(chan_io);
- else
n = ui_nb_layers(chan_io);
- fcolor = readl_relaxed(mixer_io + MIXER_BLD_REGS +
MIXER_BLD_FCOLOR_CTL_REG);
- de2_mixer_select(priv, mixer, mixer_io);
- if (chan == 0)
writel_relaxed(0, chan_io + VI_CFGx_ATTR(layer));
- else
writel_relaxed(0, chan_io + UI_CFGx_ATTR(layer));
- /* disable the pipe if no more active layer */
- if (n <= 1)
writel_relaxed(fcolor &
~MIXER_BLD_FCOLOR_CTL_PEN(plane_tb[plane_num].pipe),
mixer_io + MIXER_BLD_REGS + MIXER_BLD_FCOLOR_CTL_REG);
+}
+static void de2_drm_plane_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
+{
- struct drm_plane_state *state = plane->state;
- struct drm_crtc *crtc = state->crtc;
- struct lcd *lcd = crtc_to_lcd(crtc);
- struct priv *priv = lcd->priv;
- int plane_num = plane - lcd->planes;
- /* if the crtc is disabled, mark update delayed */
- if (!(priv->started & (1 << lcd->mixer))) {
lcd->delayed |= 1 << plane_num;
return; /* mixer disabled */
- }
- mutex_lock(&priv->mutex);
- de2_plane_update(priv, lcd, plane_num, state, old_state);
- mutex_unlock(&priv->mutex);
+}
+static void de2_drm_plane_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
+{
- struct drm_crtc *crtc = old_state->crtc;
- struct lcd *lcd = crtc_to_lcd(crtc);
- struct priv *priv = lcd->priv;
- int plane_num = plane - lcd->planes;
- if (!(priv->started & (1 << lcd->mixer)))
return; /* mixer disabled */
- mutex_lock(&priv->mutex);
- de2_plane_disable(lcd->priv, lcd->mixer, plane_num);
- mutex_unlock(&priv->mutex);
+}
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
- .atomic_update = de2_drm_plane_update,
- .atomic_disable = de2_drm_plane_disable,
+};
+static const struct drm_plane_funcs plane_funcs = {
- .update_plane = drm_atomic_helper_update_plane,
- .disable_plane = drm_atomic_helper_disable_plane,
- .destroy = drm_plane_cleanup,
- .reset = drm_atomic_helper_plane_reset,
- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+static int de2_one_plane_init(struct drm_device *drm,
struct drm_plane *plane,
int possible_crtcs,
int plane_num)
+{
- int ret;
- ret = drm_universal_plane_init(drm, plane, possible_crtcs,
&plane_funcs,
plane_tb[plane_num].formats,
plane_tb[plane_num].n_formats,
plane_tb[plane_num].type, NULL);
- if (ret >= 0)
drm_plane_helper_add(plane, &plane_helper_funcs);
- return ret;
+}
+/* initialize the planes */ +int de2_plane_init(struct drm_device *drm, struct lcd *lcd) +{
- int i, n, ret, possible_crtcs = 1 << drm_crtc_index(&lcd->crtc);
- n = ARRAY_SIZE(plane_tb);
- if (n != DE2_N_PLANES) {
dev_err(lcd->dev, "Bug: incorrect number of planes %d != "
__stringify(DE2_N_PLANES) "\n", n);
return -EINVAL;
- }
- for (i = 0; i < n; i++) {
ret = de2_one_plane_init(drm, &lcd->planes[i],
possible_crtcs, i);
if (ret < 0) {
dev_err(lcd->dev, "plane init failed %d\n", ret);
break;
}
- }
- return ret;
+}
2.10.2
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel