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> --- 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, + .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 +}; + +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, +}; + +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 +}; + +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