Re: [PATCH 1/2] drm: Add drm driver for OpenCores VGA/LCD display controller
From: Andrea Merello
Date: Mon Jul 04 2016 - 05:28:16 EST
On Fri, Jun 10, 2016 at 4:27 PM, Daniel Vetter <daniel@xxxxxxxx> wrote:
> On Thu, Jun 09, 2016 at 03:32:55PM +0200, Andrea Merello wrote:
>> This driver supports the VGA/LCD core available from OpenCores:
>> http://opencores.org/project,vga_lcd
>>
>> It's intended as a replacement for the "ocfb" framebuffer driver
>>
>> Signed-off-by: Andrea Merello <andrea.merello@xxxxxxxxx>
>> Cc: Stefan Kristiansson <stefan.kristiansson@xxxxxxxxxxxxx>
>> Cc: Tomi Valkeinen <tomi.valkeinen@xxxxxx>
>> Cc: Francesco Diotalevi <francesco.diotalevi@xxxxxx>
>> Cc: Claudio Lorini <claudio.lorini@xxxxxx>
>
> Bunch of comments below, but this driver might be a good candidate for the
> drm_simple_display_pipe helpers that Noralf Tronnes is working on. Would
> allow you to cut down a pile more boilerplate I think. Please take a look.
> I think the only thing you'd need is a new small function to set the
> drm_bridge for the encoder in struct drm_simple_display_pipe
Almost done, but I've got a bunch of:
[drm:drm_atomic_helper_commit_cleanup_done] *ERROR* [CRTC:24:crtc
-0] flip_done timed out
I guessed that I needed to send vblank events, but where are vblank
events supposed to be sent when using those new
drm_simple_display_pipe
helpers? (this IP seems to have support for vblank interrupts, but the
ocfb driver don't use it, and I'm unsure if we can assume all
implementations of this IP actually have the irq really attached).
Currently I've done as arcgpu driver does; that means adding
.atomic_begin to drm_crtc_helper_funcs and sending vblank events from
there. But this ends up to be inside drm_simple_kms_helper.c.. And I
guess this could be not ok for all drivers.
Maybe we could add another callback to the
drm_simple_display_pipe_funcs, that ends up to be called from
.atomic_begin (and possibly export a new helper that sends vblank
events, for drivers that don't want to do this by themselves) ?
.. But I'm not really familiar with drm-event stuff (and honestly with
the whole drm subsytem) .. Any advice welcome :)
Andrea
> -Daniel
>
>> ---
>> drivers/gpu/drm/Kconfig | 2 +
>> drivers/gpu/drm/Makefile | 1 +
>> drivers/gpu/drm/ocdrm/Kconfig | 7 +
>> drivers/gpu/drm/ocdrm/Makefile | 7 +
>> drivers/gpu/drm/ocdrm/ocdrm_crtc.c | 336 ++++++++++++++++++++++++++++++++++
>> drivers/gpu/drm/ocdrm/ocdrm_crtc.h | 48 +++++
>> drivers/gpu/drm/ocdrm/ocdrm_drv.c | 312 +++++++++++++++++++++++++++++++
>> drivers/gpu/drm/ocdrm/ocdrm_drv.h | 89 +++++++++
>> drivers/gpu/drm/ocdrm/ocdrm_encoder.c | 95 ++++++++++
>> drivers/gpu/drm/ocdrm/ocdrm_encoder.h | 48 +++++
>> 10 files changed, 945 insertions(+)
>> create mode 100644 drivers/gpu/drm/ocdrm/Kconfig
>> create mode 100644 drivers/gpu/drm/ocdrm/Makefile
>> create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_crtc.c
>> create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_crtc.h
>> create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_drv.c
>> create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_drv.h
>> create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_encoder.c
>> create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_encoder.h
>>
>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index fc35731..48f56e4 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -290,3 +290,5 @@ source "drivers/gpu/drm/arc/Kconfig"
>> source "drivers/gpu/drm/hisilicon/Kconfig"
>>
>> source "drivers/gpu/drm/mediatek/Kconfig"
>> +
>> +source "drivers/gpu/drm/ocdrm/Kconfig"
>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>> index be43afb..871da6a 100644
>> --- a/drivers/gpu/drm/Makefile
>> +++ b/drivers/gpu/drm/Makefile
>> @@ -82,3 +82,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
>> obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
>> obj-$(CONFIG_DRM_ARCPGU)+= arc/
>> obj-y += hisilicon/
>> +obj-$(CONFIG_DRM_OCDRM) += ocdrm/
>> diff --git a/drivers/gpu/drm/ocdrm/Kconfig b/drivers/gpu/drm/ocdrm/Kconfig
>> new file mode 100644
>> index 0000000..a918503
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/Kconfig
>> @@ -0,0 +1,7 @@
>> +config DRM_OCDRM
>> + tristate "DRM Support for opencores OCFB"
>> + depends on DRM
>> + default n
>> + select DRM_KMS_HELPER
>> + select DRM_KMS_CMA_HELPER
>> + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
>> diff --git a/drivers/gpu/drm/ocdrm/Makefile b/drivers/gpu/drm/ocdrm/Makefile
>> new file mode 100644
>> index 0000000..4ea17d2
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/Makefile
>> @@ -0,0 +1,7 @@
>> +#
>> +# Makefile for the drm device driver. This driver provides support for the
>> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>> +
>> +ocdrm-y := ocdrm_crtc.o ocdrm_drv.o ocdrm_encoder.o
>> +
>> +obj-$(CONFIG_DRM_OCDRM) += ocdrm.o
>> diff --git a/drivers/gpu/drm/ocdrm/ocdrm_crtc.c b/drivers/gpu/drm/ocdrm/ocdrm_crtc.c
>> new file mode 100644
>> index 0000000..ebfe03e
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/ocdrm_crtc.c
>> @@ -0,0 +1,336 @@
>> +/*
>> + * Open cores VGA/LCD 2.0 core DRM driver
>> + * Copyright (c) 2016 Istituto Italiano di Tecnologia
>> + * Electronic Design Lab.
>> + *
>> + * Author: Andrea Merello <andrea.merello@xxxxxxxxx>
>> + *
>> + * Based on the following drivers:
>> + * - Analog Devices AXI HDMI DRM driver, which is
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * - ARC PGU DRM driver.
>> + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
>> + *
>> + * - ARM HDLCD Driver
>> + * Copyright (C) 2013-2015 ARM Limited
>> + *
>> + * - Atmel atmel-hlcdc driver, which is
>> + * Copyright (C) 2014 Traphandler
>> + * Copyright (C) 2014 Free Electrons
>> + *
>> + * - OpenCores VGA/LCD 2.0 core frame buffer driver
>> + * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@xxxxxxxxxxxxx
>> + *
>> + * - R-Car Display Unit DRM driver
>> + * Copyright (C) 2013-2015 Renesas Electronics Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/slab.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_gem_cma_helper.h>
>> +#include <drm/drm_plane_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_atomic_helper.h>
>> +
>> +#include "ocdrm_crtc.h"
>> +
>> +
>> +static inline struct ocdrm_priv *crtc_to_ocdrm(struct drm_crtc *crtc)
>> +{
>> + return container_of(crtc, struct ocdrm_priv, crtc);
>> +}
>> +
>> +static inline struct ocdrm_priv *plane_to_ocdrm(struct drm_plane *plane)
>> +{
>> + return container_of(plane, struct ocdrm_priv, plane);
>> +}
>> +
>> +static void ocdrm_plane_atomic_update(struct drm_plane *plane,
>> + struct drm_plane_state *old_state)
>> +{
>> + struct drm_gem_cma_object *obj;
>> + u32 val;
>> + uint32_t pixel_format;
>> + int hgate;
>> + struct ocdrm_priv *priv = plane_to_ocdrm(plane);
>> +
>> + if (!plane->state->crtc || !plane->state->fb)
>> + return;
>> +
>> + pixel_format = plane->state->fb->pixel_format;
>> + hgate = plane->state->crtc->state->adjusted_mode.crtc_hdisplay;
>> +
>> + val = ocdrm_readreg(priv, OCFB_CTRL);
>> + ocdrm_writereg(priv, OCFB_CTRL, val & ~OCFB_CTRL_VEN);
>> +
>> + if (!drm_atomic_plane_disabling(plane, plane->state)) {
>> + obj = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
>> + ocdrm_writereg(priv, OCFB_VBARA, obj->paddr);
>> +
>> + val &= ~(OCFB_CTRL_CD8 | OCFB_CTRL_CD16 |
>> + OCFB_CTRL_CD24 | OCFB_CTRL_CD32);
>> + val &= ~(OCFB_CTRL_VBL8 | OCFB_CTRL_VBL4 |
>> + OCFB_CTRL_VBL2 | OCFB_CTRL_VBL1);
>> +
>> + switch (pixel_format) {
>> + /* TODO
>> + *case DRM_FORMAT_RGB332:
>> + * hgate /= 4;
>> + * val |= OCFB_CTRL_CD8;
>> + * val |= OCFB_CTRL_PC;
>> + * break;
>> + */
>> +
>> + case DRM_FORMAT_RGB565:
>> + dev_dbg(priv->drm_dev->dev, "16 bpp\n");
>> + hgate /= 2;
>> + val |= OCFB_CTRL_CD16;
>> + break;
>> +
>> + case DRM_FORMAT_RGB888:
>> + dev_dbg(priv->drm_dev->dev, "24 bpp\n");
>> + hgate = hgate * 3 / 4;
>> + val |= OCFB_CTRL_CD24;
>> + break;
>> +
>> + case DRM_FORMAT_XRGB8888:
>> + dev_dbg(priv->drm_dev->dev, "32 bpp\n");
>> + val |= OCFB_CTRL_CD32;
>> + break;
>> +
>> + default:
>> + dev_err(priv->drm_dev->dev, "Invalid pixelformat specified\n");
>> + return;
>> + }
>> +
>> + if ((0 == (obj->paddr & 0x1f)) && (0 == (hgate % 8))) {
>> + dev_dbg(priv->drm_dev->dev, "dma burst 8 cycles\n");
>> + val |= OCFB_CTRL_VBL8;
>> + } else if ((0 == (obj->paddr & 0xf)) && (0 == (hgate % 4))) {
>> + dev_dbg(priv->drm_dev->dev, "dma burst 4 cycles\n");
>> + val |= OCFB_CTRL_VBL4;
>> + } else if ((0 == (obj->paddr & 0x7)) && (0 == (hgate % 2))) {
>> + dev_dbg(priv->drm_dev->dev, "dma burst 2 cycles\n");
>> + val |= OCFB_CTRL_VBL2;
>> + } else {
>> + dev_dbg(priv->drm_dev->dev, "dma burst 1 cycle\n");
>> + val |= OCFB_CTRL_VBL1;
>> + }
>> +
>> + ocdrm_writereg(priv, OCFB_CTRL, val | OCFB_CTRL_VEN);
>> + }
>> +}
>> +
>> +static void ocdrm_crtc_enable(struct drm_crtc *crtc)
>> +{
>> +
>> + struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
>> +
>> + if (!priv->clk_enabled)
>> + clk_prepare_enable(priv->pixel_clock);
>> + priv->clk_enabled = true;
>> +}
>> +
>> +static void ocdrm_crtc_disable(struct drm_crtc *crtc)
>> +{
>> + struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
>> +
>> + /* why the plane has been not disabled ? .. we get here from destroy */
>> + ocdrm_writereg(priv, OCFB_CTRL, 0);
>> +
>> + if (priv->clk_enabled)
>> + clk_disable_unprepare(priv->pixel_clock);
>> + priv->clk_enabled = false;
>> +}
>> +
>> +static void ocdrm_crtc_mode_set_nofb(struct drm_crtc *crtc)
>> +{
>> + u32 ctrl;
>> + int ret;
>> + struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
>> + struct drm_display_mode *m = &crtc->state->adjusted_mode;
>> + uint32_t hsync_len = m->crtc_hsync_end - m->crtc_hsync_start;
>> + uint32_t vsync_len = m->crtc_vsync_end - m->crtc_vsync_start;
>> + uint32_t vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
>> + uint32_t hback_porch = m->crtc_htotal - m->crtc_hsync_end;
>> +
>> + ctrl = ocdrm_readreg(priv, OCFB_CTRL);
>> + ocdrm_writereg(priv, OCFB_CTRL, ctrl & ~OCFB_CTRL_VEN);
>> +
>> + /* Horizontal timings */
>> + ocdrm_writereg(priv, OCFB_HTIM, (hsync_len - 1) << 24 |
>> + (hback_porch - 1) << 16 | (m->crtc_hdisplay - 1));
>> +
>> + /* Vertical timings */
>> + ocdrm_writereg(priv, OCFB_VTIM, (vsync_len - 1) << 24 |
>> + (vback_porch - 1) << 16 | (m->crtc_vdisplay - 1));
>> +
>> + ocdrm_writereg(priv, OCFB_HVLEN, ((uint32_t)m->crtc_htotal - 1) << 16 |
>> + (m->crtc_vtotal - 1));
>> +
>> + dev_dbg(priv->drm_dev->dev, "set mode H slen %u, bporch %u, tot %u\n",
>> + hsync_len, hback_porch, m->crtc_htotal);
>> + dev_dbg(priv->drm_dev->dev, "set mode V slen %u, bporch %u, tot %u\n",
>> + vsync_len, vback_porch, m->crtc_vtotal);
>> +
>> + if (m->flags & DRM_MODE_FLAG_NHSYNC)
>> + ctrl |= OCFB_CTRL_HSL;
>> + else
>> + ctrl &= ~OCFB_CTRL_HSL;
>> +
>> + if (m->flags & DRM_MODE_FLAG_NVSYNC)
>> + ctrl |= OCFB_CTRL_VSL;
>> + else
>> + ctrl &= ~OCFB_CTRL_VSL;
>> +
>> + dev_dbg(priv->drm_dev->dev, "VPOL %d, HPOL %d\n",
>> + m->flags & DRM_MODE_FLAG_NVSYNC,
>> + m->flags & DRM_MODE_FLAG_NHSYNC);
>> +
>> +
>> + /* Set sync polarity. */
>> + ocdrm_writereg(priv, OCFB_CTRL, ctrl);
>> +
>> + if (priv->clk_enabled)
>> + clk_disable_unprepare(priv->pixel_clock);
>> +
>> + ret = clk_set_rate(priv->pixel_clock, m->crtc_clock * 1000);
>> + if (ret) {
>> + dev_err(priv->drm_dev->dev, "failed to set pixclk %d\n", ret);
>> + return;
>> + }
>> +
>> + if (priv->clk_enabled)
>> + clk_prepare_enable(priv->pixel_clock);
>> +
>> + dev_dbg(priv->drm_dev->dev, "pixel clock: %d\n", m->crtc_clock);
>> +
>> + /* if video was enabled, then enable it */
>> + ocdrm_writereg(priv, OCFB_CTRL, ctrl);
>> +}
>> +
>> +static bool ocdrm_crtc_mode_fixup(struct drm_crtc *crtc,
>> + const struct drm_display_mode *mode,
>> + struct drm_display_mode *adjusted_mode)
>> +{
>> + struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
>> +
>> + if (mode->clock < 16000 || mode->clock > 165000)
>> + return false;
>> +
>> + adjusted_mode->clock = clk_round_rate(priv->pixel_clock,
>> + mode->clock * 1000) / 1000;
>> + return true;
>> +}
>> +
>> +static int ocdrm_crtc_atomic_check(struct drm_crtc *crtc,
>> + struct drm_crtc_state *state)
>> +{
>> + struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
>> + struct drm_display_mode *m = &state->adjusted_mode;
>> + uint32_t hsync_len = m->crtc_hsync_end - m->crtc_hsync_start;
>> + uint32_t vsync_len = m->crtc_vsync_end - m->crtc_vsync_start;
>> + uint32_t vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
>> + uint32_t hback_porch = m->crtc_htotal - m->crtc_hsync_end;
>> + int rate;
>> +
>> + if (m->clock < 16000 || m->clock > 165000)
>> + return false;
>> +
>> + rate = clk_round_rate(priv->pixel_clock, m->clock * 1000) / 1000;
>> +
>> + if (m->clock != rate)
>> + return -EINVAL;
>> +
>> + if (hsync_len > 255 || vsync_len > 255 ||
>> + vback_porch > 255 || hback_porch > 255)
>> + return -EINVAL;
>> +
>> + return 0;
>> +}
>> +
>> +static struct drm_crtc_helper_funcs ocdrm_crtc_helper_funcs = {
>> + .mode_fixup = ocdrm_crtc_mode_fixup,
>> + .mode_set = drm_helper_crtc_mode_set,
>> + .mode_set_base = drm_helper_crtc_mode_set_base,
>
> You don't need the above 2 callbacks when exlusively using atomic.
>
>> + .mode_set_nofb = ocdrm_crtc_mode_set_nofb,
>> + .disable = ocdrm_crtc_disable,
>> + .enable = ocdrm_crtc_enable,
>> + .atomic_check = ocdrm_crtc_atomic_check,
>> +};
>> +
>> +static void ocdrm_crtc_destroy(struct drm_crtc *crtc)
>> +{
>> + ocdrm_crtc_disable(crtc);
>> + drm_crtc_cleanup(crtc);
>> +}
>> +
>> +static struct drm_crtc_funcs ocdrm_crtc_funcs = {
>> + .page_flip = drm_atomic_helper_page_flip,
>> + .set_config = drm_atomic_helper_set_config,
>> + .destroy = ocdrm_crtc_destroy,
>> + .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_plane_helper_funcs ocdrm_plane_helper_funcs = {
>> + .atomic_update = ocdrm_plane_atomic_update,
>> + .atomic_disable = NULL,
>> + .prepare_fb = NULL,
>> + .cleanup_fb = NULL
>
> Don't set to NULL, that's always the case for global data.
>
>> +};
>> +
>> +static const struct drm_plane_funcs ocdrm_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,
>> +};
>> +
>> +int ocdrm_crtc_create(struct ocdrm_priv *priv)
>> +{
>> + int ret;
>> + uint32_t format[] = { DRM_FORMAT_RGB565,
>> + DRM_FORMAT_RGB888,
>> + DRM_FORMAT_XRGB8888,
>> + };
>> +
>> + drm_plane_helper_add(&priv->plane, &ocdrm_plane_helper_funcs);
>> +
>> + ret = drm_universal_plane_init(priv->drm_dev, &priv->plane, 0,
>> + &ocdrm_plane_funcs,
>> + format, ARRAY_SIZE(format),
>> + DRM_PLANE_TYPE_PRIMARY, NULL);
>> + if (ret) {
>> + dev_err(priv->drm_dev->dev, "cannot initialize plane");
>> + return ret;
>> + }
>> +
>> + drm_crtc_helper_add(&priv->crtc, &ocdrm_crtc_helper_funcs);
>> + ret = drm_crtc_init_with_planes(priv->drm_dev, &priv->crtc,
>> + &priv->plane, NULL,
>> + &ocdrm_crtc_funcs, NULL);
>> + if (ret)
>> + dev_err(priv->drm_dev->dev, "cannot initialize crtc");
>> + return ret;
>> +
>> + return 0;
>> +}
>> diff --git a/drivers/gpu/drm/ocdrm/ocdrm_crtc.h b/drivers/gpu/drm/ocdrm/ocdrm_crtc.h
>> new file mode 100644
>> index 0000000..778327e
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/ocdrm_crtc.h
>> @@ -0,0 +1,48 @@
>> +/*
>> + * Open cores VGA/LCD 2.0 core DRM driver
>> + * Copyright (c) 2016 Istituto Italiano di Tecnologia
>> + * Electronic Design Lab.
>> + *
>> + * Author: Andrea Merello <andrea.merello@xxxxxxxxx>
>> + *
>> + * Based on the following drivers:
>> + * - Analog Devices AXI HDMI DRM driver, which is
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * - ARC PGU DRM driver.
>> + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
>> + *
>> + * - ARM HDLCD Driver
>> + * Copyright (C) 2013-2015 ARM Limited
>> + *
>> + * - Atmel atmel-hlcdc driver, which is
>> + * Copyright (C) 2014 Traphandler
>> + * Copyright (C) 2014 Free Electrons
>> + *
>> + * - OpenCores VGA/LCD 2.0 core frame buffer driver
>> + * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@xxxxxxxxxxxxx
>> + *
>> + * - R-Car Display Unit DRM driver
>> + * Copyright (C) 2013-2015 Renesas Electronics Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef _OCDRM_CRTC_H_
>> +#define _OCDRM_CRTC_H_
>> +
>> +#include "ocdrm_drv.h"
>> +
>> +int ocdrm_crtc_create(struct ocdrm_priv *priv);
>> +
>> +#endif
>> diff --git a/drivers/gpu/drm/ocdrm/ocdrm_drv.c b/drivers/gpu/drm/ocdrm/ocdrm_drv.c
>> new file mode 100644
>> index 0000000..4b335dc
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/ocdrm_drv.c
>> @@ -0,0 +1,312 @@
>> +/*
>> + * Open cores VGA/LCD 2.0 core DRM driver
>> + * Copyright (c) 2016 Istituto Italiano di Tecnologia
>> + * Electronic Design Lab.
>> + *
>> + * Author: Andrea Merello <andrea.merello@xxxxxxxxx>
>> + *
>> + * Based on the following drivers:
>> + * - Analog Devices AXI HDMI DRM driver, which is
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * - ARC PGU DRM driver.
>> + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
>> + *
>> + * - ARM HDLCD Driver
>> + * Copyright (C) 2013-2015 ARM Limited
>> + *
>> + * - Atmel atmel-hlcdc driver, which is
>> + * Copyright (C) 2014 Traphandler
>> + * Copyright (C) 2014 Free Electrons
>> + *
>> + * - OpenCores VGA/LCD 2.0 core frame buffer driver
>> + * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@xxxxxxxxxxxxx
>> + *
>> + * - R-Car Display Unit DRM driver
>> + * Copyright (C) 2013-2015 Renesas Electronics Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/of.h>
>> +#include <linux/i2c.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_dma.h>
>> +#include <linux/clk.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm.h>
>> +#include <drm/drm_atomic.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_gem_cma_helper.h>
>> +
>> +#include "ocdrm_drv.h"
>> +#include "ocdrm_crtc.h"
>> +#include "ocdrm_encoder.h"
>> +
>> +#define DRIVER_NAME "ocdrm"
>> +#define DRIVER_DESC "OpenCores DRM"
>> +#define DRIVER_DATE "20160527"
>> +#define DRIVER_MAJOR 1
>> +#define DRIVER_MINOR 0
>> +
>> +static void ocdrm_output_poll_changed(struct drm_device *drm)
>> +{
>> + struct ocdrm_priv *priv = drm->dev_private;
>> +
>> + drm_fbdev_cma_hotplug_event(priv->fbdev);
>> +}
>> +
>> +static int ocdrm_atomic_commit(struct drm_device *dev,
>> + struct drm_atomic_state *state, bool async)
>> +{
>> + return drm_atomic_helper_commit(dev, state, false);
>> +}
>> +
>> +static struct drm_mode_config_funcs ocdrm_mode_config_funcs = {
>> + .output_poll_changed = ocdrm_output_poll_changed,
>> + .fb_create = drm_fb_cma_create,
>> + .atomic_check = drm_atomic_helper_check,
>> + .atomic_commit = ocdrm_atomic_commit,
>
> Just use drm_atomic_helper_commit please, there's patches in-flight to
> give you nonblocking atomic for free. Cheating with the above trick (like
> arcpgu does) needs to stop.
>
>> +};
>> +
>> +u32 ocdrm_readreg(struct ocdrm_priv *priv, loff_t offset)
>> +{
>> + if (priv->little_endian)
>> + return ioread32(priv->regs + offset);
>> + else
>> + return ioread32be(priv->regs + offset);
>> +}
>> +
>> +void ocdrm_writereg(struct ocdrm_priv *priv, loff_t offset, u32 data)
>> +{
>> + if (priv->little_endian)
>> + iowrite32(data, priv->regs + offset);
>> + else
>> + iowrite32be(data, priv->regs + offset);
>> +}
>> +
>> +static void ocdrm_mode_config_init(struct ocdrm_priv *priv)
>> +{
>> + struct drm_device *dev = priv->drm_dev;
>> +
>> + dev->mode_config.min_width = 0;
>> + dev->mode_config.min_height = 0;
>> +
>> + dev->mode_config.max_width = 1500;
>> + dev->mode_config.max_height = 1500;
>> +
>> + dev->mode_config.funcs = &ocdrm_mode_config_funcs;
>> +}
>> +
>> +static void ocdrm_detect_endian(struct ocdrm_priv *priv)
>> +{
>> + priv->little_endian = false;
>> + ocdrm_writereg(priv, OCFB_VBARA, 0xfffffff0);
>> + if (ocdrm_readreg(priv, OCFB_VBARA) != 0xfffffff0)
>> + priv->little_endian = true;
>> +
>> + ocdrm_writereg(priv, OCFB_VBARA, 0x0);
>> +}
>> +
>> +static int ocdrm_load(struct drm_device *dev)
>> +{
>> + struct ocdrm_priv *priv;
>> + int ret;
>> + struct resource *res;
>> + struct platform_device *pdev = to_platform_device(dev->dev);
>> +
>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> + dev->dev_private = priv;
>> + priv->drm_dev = dev;
>> +
>> + priv->pixel_clock = devm_clk_get(&pdev->dev, NULL);
>> + if (IS_ERR(priv->pixel_clock))
>> + return -EPROBE_DEFER;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + priv->regs = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(priv->regs))
>> + return PTR_ERR(priv->regs);
>> +
>> + if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
>> + return -ENOMEM;
>> +
>> + ocdrm_detect_endian(priv);
>> +
>> + drm_mode_config_init(dev);
>> + ocdrm_mode_config_init(priv);
>> +
>> + ret = ocdrm_crtc_create(priv);
>> + if (ret)
>> + goto err_crtc;
>> +
>> + ret = ocdrm_encoder_create(priv);
>> + if (ret)
>> + goto err_crtc;
>> +
>> + drm_mode_config_reset(dev);
>> + drm_kms_helper_poll_init(dev);
>> +
>> + priv->fbdev = drm_fbdev_cma_init(dev, 16, dev->mode_config.num_crtc,
>> + dev->mode_config.num_connector);
>> +
>> + if (IS_ERR(priv->fbdev)) {
>> + DRM_ERROR("failed to initialize drm fbdev\n");
>> + ret = PTR_ERR(priv->fbdev);
>> + goto err_crtc;
>> + }
>> +
>> + platform_set_drvdata(pdev, priv);
>> + return 0;
>> +
>> +err_crtc:
>> + drm_mode_config_cleanup(dev);
>> + return ret;
>> +}
>> +
>> +static int ocdrm_unload(struct drm_device *dev)
>> +{
>> + struct ocdrm_priv *priv = dev->dev_private;
>> +
>> + if (priv->fbdev) {
>> + drm_fbdev_cma_fini(priv->fbdev);
>> + priv->fbdev = NULL;
>> + }
>> + drm_kms_helper_poll_fini(dev);
>> + drm_vblank_cleanup(dev);
>> + drm_mode_config_cleanup(dev);
>> +
>> + return 0;
>> +}
>> +
>> +static void ocdrm_lastclose(struct drm_device *dev)
>> +{
>> + struct ocdrm_priv *priv = dev->dev_private;
>> +
>> + drm_fbdev_cma_restore_mode(priv->fbdev);
>> +}
>> +
>> +static const struct file_operations ocdrm_driver_fops = {
>> + .owner = THIS_MODULE,
>> + .open = drm_open,
>> + .mmap = drm_gem_cma_mmap,
>> + .poll = drm_poll,
>> + .read = drm_read,
>> + .unlocked_ioctl = drm_ioctl,
>> +#ifdef CONFIG_COMPAT
>> + .compat_ioctl = drm_compat_ioctl,
>> +#endif
>> + .llseek = no_llseek,
>> + .release = drm_release,
>> +};
>> +
>> +static struct drm_driver ocdrm_drm_driver = {
>> + .driver_features = DRIVER_MODESET | DRIVER_GEM |
>> + DRIVER_ATOMIC | DRIVER_PRIME,
>> + .lastclose = ocdrm_lastclose,
>> + .fops = &ocdrm_driver_fops,
>> + .name = DRIVER_NAME,
>> + .desc = DRIVER_DESC,
>> + .date = DRIVER_DATE,
>> + .major = DRIVER_MAJOR,
>> + .minor = DRIVER_MINOR,
>> + .dumb_create = drm_gem_cma_dumb_create,
>> + .dumb_map_offset = drm_gem_cma_dumb_map_offset,
>> + .dumb_destroy = drm_gem_dumb_destroy,
>> + .get_vblank_counter = drm_vblank_no_hw_counter,
>> + .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
>> + .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
>> + .gem_free_object = drm_gem_cma_free_object,
>> + .gem_vm_ops = &drm_gem_cma_vm_ops,
>> + .gem_prime_export = drm_gem_prime_export,
>> + .gem_prime_import = drm_gem_prime_import,
>> + .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,
>> +
>> +};
>> +
>> +static const struct of_device_id ocdrm_of_match[] = {
>> + { .compatible = "opencores,ocfb-drm", },
>> + {},
>> +};
>> +MODULE_DEVICE_TABLE(of, ocdrm_of_match);
>> +
>> +static int ocdrm_probe(struct platform_device *pdev)
>> +{
>> + int ret;
>> + struct drm_device *drm;
>> +
>> + drm = drm_dev_alloc(&ocdrm_drm_driver, &pdev->dev);
>> + if (!drm)
>> + return -ENOMEM;
>> +
>> + ret = ocdrm_load(drm);
>> + if (ret)
>> + goto err_unref;
>> +
>> + ret = drm_dev_register(drm, 0);
>> + if (ret)
>> + goto err_unload;
>> +
>> + ret = drm_connector_register_all(drm);
>> + if (ret)
>> + goto err_unregister;
>> + return 0;
>> +
>> +err_unregister:
>> + drm_dev_unregister(drm);
>> +err_unload:
>> + ocdrm_unload(drm);
>> +err_unref:
>> + drm_dev_unref(drm);
>> +
>> + return ret;
>> +}
>> +
>> +static int ocdrm_remove(struct platform_device *pdev)
>> +{
>> + struct ocdrm_priv *priv = platform_get_drvdata(pdev);
>> + struct drm_device *drm = priv->drm_dev;
>> +
>> + drm_connector_unregister_all(priv->drm_dev);
>> + drm_dev_unregister(drm);
>> + ocdrm_unload(drm);
>> + drm_dev_unref(drm);
>> +
>> + return 0;
>> +}
>> +
>> +static struct platform_driver ocdrm_platform_driver = {
>> + .driver = {
>> + .name = "oc-drm",
>> + .owner = THIS_MODULE,
>> + .of_match_table = ocdrm_of_match,
>> + },
>> + .probe = ocdrm_probe,
>> + .remove = ocdrm_remove,
>> +};
>> +module_platform_driver(ocdrm_platform_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR("Andrea Merello <andrea.merello@xxxxxxxxx>");
>> +MODULE_DESCRIPTION("OpenCores DRM driver");
>> diff --git a/drivers/gpu/drm/ocdrm/ocdrm_drv.h b/drivers/gpu/drm/ocdrm/ocdrm_drv.h
>> new file mode 100644
>> index 0000000..14e5539
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/ocdrm_drv.h
>> @@ -0,0 +1,89 @@
>> +/*
>> + * Open cores VGA/LCD 2.0 core DRM driver
>> + * Copyright (c) 2016 Istituto Italiano di Tecnologia
>> + * Electronic Design Lab.
>> + *
>> + * Author: Andrea Merello <andrea.merello@xxxxxxxxx>
>> + *
>> + * Based on the following drivers:
>> + * - Analog Devices AXI HDMI DRM driver, which is
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * - ARC PGU DRM driver.
>> + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
>> + *
>> + * - ARM HDLCD Driver
>> + * Copyright (C) 2013-2015 ARM Limited
>> + *
>> + * - Atmel atmel-hlcdc driver, which is
>> + * Copyright (C) 2014 Traphandler
>> + * Copyright (C) 2014 Free Electrons
>> + *
>> + * - OpenCores VGA/LCD 2.0 core frame buffer driver
>> + * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@xxxxxxxxxxxxx
>> + *
>> + * - R-Car Display Unit DRM driver
>> + * Copyright (C) 2013-2015 Renesas Electronics Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef _OCDRM_DRV_H_
>> +#define _OCDRM_DRV_H_
>> +
>> +#include <drm/drm.h>
>> +#include <drm/drm_fb_cma_helper.h>
>> +#include <linux/of.h>
>> +#include <linux/clk.h>
>> +
>> +/* OCFB register defines */
>> +#define OCFB_CTRL 0x000
>> +#define OCFB_STAT 0x004
>> +#define OCFB_HTIM 0x008
>> +#define OCFB_VTIM 0x00c
>> +#define OCFB_HVLEN 0x010
>> +#define OCFB_VBARA 0x014
>> +#define OCFB_PALETTE 0x800
>> +
>> +#define OCFB_CTRL_VEN 0x00000001 /* Video Enable */
>> +#define OCFB_CTRL_HIE 0x00000002 /* HSync Interrupt Enable */
>> +#define OCFB_CTRL_PC 0x00000800 /* 8-bit Pseudo Color Enable*/
>> +#define OCFB_CTRL_CD8 0x00000000 /* Color Depth 8 */
>> +#define OCFB_CTRL_CD16 0x00000200 /* Color Depth 16 */
>> +#define OCFB_CTRL_CD24 0x00000400 /* Color Depth 24 */
>> +#define OCFB_CTRL_CD32 0x00000600 /* Color Depth 32 */
>> +#define OCFB_CTRL_VBL1 0x00000000 /* Burst Length 1 */
>> +#define OCFB_CTRL_VBL2 0x00000080 /* Burst Length 2 */
>> +#define OCFB_CTRL_VBL4 0x00000100 /* Burst Length 4 */
>> +#define OCFB_CTRL_VBL8 0x00000180 /* Burst Length 8 */
>> +#define OCFB_CTRL_HSL 0x00001000 /* HSync active low */
>> +#define OCFB_CTRL_VSL 0x00002000 /* VSync active low */
>> +
>> +#define PALETTE_SIZE 256
>> +
>> +struct ocdrm_priv {
>> + struct drm_device *drm_dev;
>> + struct drm_fbdev_cma *fbdev;
>> + struct drm_crtc crtc;
>> + struct drm_plane plane;
>> + struct drm_encoder encoder;
>> + struct clk *pixel_clock;
>> + bool clk_enabled;
>> + void __iomem *regs;
>> + bool little_endian;
>> +};
>> +
>> +extern void ocdrm_writereg(struct ocdrm_priv *priv, loff_t offset, u32 data);
>> +extern u32 ocdrm_readreg(struct ocdrm_priv *priv, loff_t offset);
>> +
>> +#endif
>> diff --git a/drivers/gpu/drm/ocdrm/ocdrm_encoder.c b/drivers/gpu/drm/ocdrm/ocdrm_encoder.c
>> new file mode 100644
>> index 0000000..f157bb5
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/ocdrm_encoder.c
>> @@ -0,0 +1,95 @@
>> +/*
>> + * Open cores VGA/LCD 2.0 core DRM driver
>> + * Copyright (c) 2016 Istituto Italiano di Tecnologia
>> + * Electronic Design Lab.
>> + *
>> + * Author: Andrea Merello <andrea.merello@xxxxxxxxx>
>> + *
>> + * Based on the following drivers:
>> + * - Analog Devices AXI HDMI DRM driver, which is
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * - ARC PGU DRM driver.
>> + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
>> + *
>> + * - ARM HDLCD Driver
>> + * Copyright (C) 2013-2015 ARM Limited
>> + *
>> + * - Atmel atmel-hlcdc driver, which is
>> + * Copyright (C) 2014 Traphandler
>> + * Copyright (C) 2014 Free Electrons
>> + *
>> + * - OpenCores VGA/LCD 2.0 core frame buffer driver
>> + * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@xxxxxxxxxxxxx
>> + *
>> + * - R-Car Display Unit DRM driver
>> + * Copyright (C) 2013-2015 Renesas Electronics Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/of.h>
>> +#include <linux/of_graph.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include "ocdrm_encoder.h"
>> +
>> +static void ocdrm_encoder_nop(struct drm_encoder *encoder)
>> +{
>> +}
>> +
>> +static const struct drm_encoder_funcs ocdrm_encoder_funcs = {
>> + .destroy = drm_encoder_cleanup
>> +};
>> +
>> +static struct drm_encoder_helper_funcs ocdrm_encoder_helper_funcs = {
>> + .commit = ocdrm_encoder_nop,
>> + .enable = ocdrm_encoder_nop,
>> + .disable = ocdrm_encoder_nop
>> +};
>
> Please nuke all these no-op functions. Even more so, just nuke the entire
> helper struct, it can be NULL.
>> +
>> +int ocdrm_encoder_create(struct ocdrm_priv *priv)
>> +{
>> + struct drm_encoder *encoder;
>> + struct drm_bridge *bridge;
>> + struct device_node *ep, *bridge_node;
>> + int ret;
>> +
>> + encoder = &priv->encoder;
>> + encoder->possible_crtcs = 1 << drm_crtc_index(&priv->crtc);
>> +
>> + drm_encoder_helper_add(encoder, &ocdrm_encoder_helper_funcs);
>> + ret = drm_encoder_init(priv->drm_dev, encoder, &ocdrm_encoder_funcs,
>> + DRM_MODE_ENCODER_NONE, NULL);
>> + if (ret)
>> + return ret;
>> + ep = of_graph_get_next_endpoint(priv->drm_dev->dev->of_node, NULL);
>> + if (!ep)
>> + return -ENODEV;
>> +
>> + bridge_node = of_graph_get_remote_port_parent(ep);
>> + if (!bridge_node)
>> + return -ENODEV;
>> +
>> + bridge = of_drm_find_bridge(bridge_node);
>> + if (!bridge)
>> + return -EPROBE_DEFER;
>> +
>> + bridge->encoder = encoder;
>> + encoder->bridge = bridge;
>> + drm_bridge_attach(priv->drm_dev, bridge);
>> + return 0;
>> +}
>> diff --git a/drivers/gpu/drm/ocdrm/ocdrm_encoder.h b/drivers/gpu/drm/ocdrm/ocdrm_encoder.h
>> new file mode 100644
>> index 0000000..a5ee5f6
>> --- /dev/null
>> +++ b/drivers/gpu/drm/ocdrm/ocdrm_encoder.h
>> @@ -0,0 +1,48 @@
>> +/*
>> + * Open cores VGA/LCD 2.0 core DRM driver
>> + * Copyright (c) 2016 Istituto Italiano di Tecnologia
>> + * Electronic Design Lab.
>> + *
>> + * Author: Andrea Merello <andrea.merello@xxxxxxxxx>
>> + *
>> + * Based on the following drivers:
>> + * - Analog Devices AXI HDMI DRM driver, which is
>> + * Copyright 2012 Analog Devices Inc.
>> + *
>> + * - ARC PGU DRM driver.
>> + * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
>> + *
>> + * - ARM HDLCD Driver
>> + * Copyright (C) 2013-2015 ARM Limited
>> + *
>> + * - Atmel atmel-hlcdc driver, which is
>> + * Copyright (C) 2014 Traphandler
>> + * Copyright (C) 2014 Free Electrons
>> + *
>> + * - OpenCores VGA/LCD 2.0 core frame buffer driver
>> + * Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson@xxxxxxxxxxxxx
>> + *
>> + * - R-Car Display Unit DRM driver
>> + * Copyright (C) 2013-2015 Renesas Electronics Corporation
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef _OCDRM_ENCODER_H_
>> +#define _OCDRM_ENCODER_H_
>> +
>> +#include "ocdrm_drv.h"
>> +
>> +int ocdrm_encoder_create(struct ocdrm_priv *priv);
>> +
>> +#endif
>> --
>> 1.9.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@xxxxxxxxxxxxxxxxxxxxx
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch