Re: [PATCH 1/2] drm: Add drm driver for OpenCores VGA/LCD display controller

From: Andrea Merello
Date: Mon Jun 27 2016 - 07:33:50 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

Yes, it seems a good option. I'll look into it.

Beside this, OK for all your comments below.

BTW I forgot to mention that, beside we are using it with a HDMI
bridge, it would need something like the "RGB to VGA bridge" patch
from Maxime Ripard [1] to actually replace the old "ocfb" driver. Is
it likely to be accepted for merge sooner or later ?

[1] https://patchwork.kernel.org/patch/9102271/


> -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