summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/fsl-dcu
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
commit76cb841cb886eef6b3bee341a2266c76578724ad (patch)
treef5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/gpu/drm/fsl-dcu
parentInitial commit. (diff)
downloadlinux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz
linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/fsl-dcu')
-rw-r--r--drivers/gpu/drm/fsl-dcu/Kconfig13
-rw-r--r--drivers/gpu/drm/fsl-dcu/Makefile8
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c198
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h19
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c394
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h203
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c59
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h32
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c235
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h18
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c167
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.c111
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.h33
13 files changed, 1490 insertions, 0 deletions
diff --git a/drivers/gpu/drm/fsl-dcu/Kconfig b/drivers/gpu/drm/fsl-dcu/Kconfig
new file mode 100644
index 000000000..14a72c4c4
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/Kconfig
@@ -0,0 +1,13 @@
+config DRM_FSL_DCU
+ tristate "DRM Support for Freescale DCU"
+ depends on DRM && OF && ARM && COMMON_CLK
+ select BACKLIGHT_CLASS_DEVICE
+ select BACKLIGHT_LCD_SUPPORT
+ select DRM_KMS_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_PANEL
+ select REGMAP_MMIO
+ select VIDEOMODE_HELPERS
+ help
+ Choose this option if you have an Freescale DCU chipset.
+ If M is selected the module will be called fsl-dcu-drm.
diff --git a/drivers/gpu/drm/fsl-dcu/Makefile b/drivers/gpu/drm/fsl-dcu/Makefile
new file mode 100644
index 000000000..b55c4482d
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+fsl-dcu-drm-y := fsl_dcu_drm_drv.o \
+ fsl_dcu_drm_kms.o \
+ fsl_dcu_drm_rgb.o \
+ fsl_dcu_drm_plane.o \
+ fsl_dcu_drm_crtc.o \
+ fsl_tcon.o
+obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
new file mode 100644
index 000000000..0e3752437
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "fsl_dcu_drm_crtc.h"
+#include "fsl_dcu_drm_drv.h"
+#include "fsl_dcu_drm_plane.h"
+
+static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ regmap_write(fsl_dev->regmap,
+ DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG);
+
+ if (event) {
+ crtc->state->event = NULL;
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ }
+}
+
+static void fsl_dcu_drm_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ /* always disable planes on the CRTC */
+ drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true);
+
+ drm_crtc_vblank_off(crtc);
+
+ regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
+ DCU_MODE_DCU_MODE_MASK,
+ DCU_MODE_DCU_MODE(DCU_MODE_OFF));
+ regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
+ DCU_UPDATE_MODE_READREG);
+ clk_disable_unprepare(fsl_dev->pix_clk);
+}
+
+static void fsl_dcu_drm_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ clk_prepare_enable(fsl_dev->pix_clk);
+ regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE,
+ DCU_MODE_DCU_MODE_MASK,
+ DCU_MODE_DCU_MODE(DCU_MODE_NORMAL));
+ regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE,
+ DCU_UPDATE_MODE_READREG);
+
+ drm_crtc_vblank_on(crtc);
+}
+
+static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ struct drm_connector *con = &fsl_dev->connector.base;
+ struct drm_display_mode *mode = &crtc->state->mode;
+ unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0;
+
+ index = drm_crtc_index(crtc);
+ clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000);
+
+ /* Configure timings: */
+ hbp = mode->htotal - mode->hsync_end;
+ hfp = mode->hsync_start - mode->hdisplay;
+ hsw = mode->hsync_end - mode->hsync_start;
+ vbp = mode->vtotal - mode->vsync_end;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vsw = mode->vsync_end - mode->vsync_start;
+
+ /* INV_PXCK as default (most display sample data on rising edge) */
+ if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE))
+ pol |= DCU_SYN_POL_INV_PXCK;
+
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ pol |= DCU_SYN_POL_INV_HS_LOW;
+
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ pol |= DCU_SYN_POL_INV_VS_LOW;
+
+ regmap_write(fsl_dev->regmap, DCU_HSYN_PARA,
+ DCU_HSYN_PARA_BP(hbp) |
+ DCU_HSYN_PARA_PW(hsw) |
+ DCU_HSYN_PARA_FP(hfp));
+ regmap_write(fsl_dev->regmap, DCU_VSYN_PARA,
+ DCU_VSYN_PARA_BP(vbp) |
+ DCU_VSYN_PARA_PW(vsw) |
+ DCU_VSYN_PARA_FP(vfp));
+ regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
+ DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
+ DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
+ regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol);
+ regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
+ DCU_BGND_G(0) | DCU_BGND_B(0));
+ regmap_write(fsl_dev->regmap, DCU_DCU_MODE,
+ DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN);
+ regmap_write(fsl_dev->regmap, DCU_THRESHOLD,
+ DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) |
+ DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) |
+ DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL));
+ return;
+}
+
+static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = {
+ .atomic_disable = fsl_dcu_drm_crtc_atomic_disable,
+ .atomic_flush = fsl_dcu_drm_crtc_atomic_flush,
+ .atomic_enable = fsl_dcu_drm_crtc_atomic_enable,
+ .mode_set_nofb = fsl_dcu_drm_crtc_mode_set_nofb,
+};
+
+static int fsl_dcu_drm_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ unsigned int value;
+
+ regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
+ value &= ~DCU_INT_MASK_VBLANK;
+ regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
+
+ return 0;
+}
+
+static void fsl_dcu_drm_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ unsigned int value;
+
+ regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
+ value |= DCU_INT_MASK_VBLANK;
+ regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
+}
+
+static const struct drm_crtc_funcs fsl_dcu_drm_crtc_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .destroy = drm_crtc_cleanup,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .set_config = drm_atomic_helper_set_config,
+ .enable_vblank = fsl_dcu_drm_crtc_enable_vblank,
+ .disable_vblank = fsl_dcu_drm_crtc_disable_vblank,
+};
+
+int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev)
+{
+ struct drm_plane *primary;
+ struct drm_crtc *crtc = &fsl_dev->crtc;
+ int ret;
+
+ fsl_dcu_drm_init_planes(fsl_dev->drm);
+
+ primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm);
+ if (!primary)
+ return -ENOMEM;
+
+ ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL,
+ &fsl_dcu_drm_crtc_funcs, NULL);
+ if (ret) {
+ primary->funcs->destroy(primary);
+ return ret;
+ }
+
+ drm_crtc_helper_add(crtc, &fsl_dcu_drm_crtc_helper_funcs);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h
new file mode 100644
index 000000000..43d4da2c5
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __FSL_DCU_DRM_CRTC_H__
+#define __FSL_DCU_DRM_CRTC_H__
+
+struct fsl_dcu_drm_device;
+
+int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev);
+
+#endif /* __FSL_DCU_DRM_CRTC_H__ */
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
new file mode 100644
index 000000000..80232321a
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_modeset_helper.h>
+
+#include "fsl_dcu_drm_crtc.h"
+#include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
+
+static int legacyfb_depth = 24;
+module_param(legacyfb_depth, int, 0444);
+
+static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE)
+ return true;
+
+ return false;
+}
+
+static const struct regmap_config fsl_dcu_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .volatile_reg = fsl_dcu_drm_is_volatile_reg,
+};
+
+static void fsl_dcu_irq_uninstall(struct drm_device *dev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ regmap_write(fsl_dev->regmap, DCU_INT_STATUS, ~0);
+ regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0);
+}
+
+static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ int ret;
+
+ ret = fsl_dcu_drm_modeset_init(fsl_dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to initialize mode setting\n");
+ return ret;
+ }
+
+ ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to initialize vblank\n");
+ goto done;
+ }
+
+ ret = drm_irq_install(dev, fsl_dev->irq);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to install IRQ handler\n");
+ goto done;
+ }
+
+ if (legacyfb_depth != 16 && legacyfb_depth != 24 &&
+ legacyfb_depth != 32) {
+ dev_warn(dev->dev,
+ "Invalid legacyfb_depth. Defaulting to 24bpp\n");
+ legacyfb_depth = 24;
+ }
+ fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1);
+ if (IS_ERR(fsl_dev->fbdev)) {
+ ret = PTR_ERR(fsl_dev->fbdev);
+ fsl_dev->fbdev = NULL;
+ goto done;
+ }
+
+ return 0;
+done:
+ drm_kms_helper_poll_fini(dev);
+
+ if (fsl_dev->fbdev)
+ drm_fbdev_cma_fini(fsl_dev->fbdev);
+
+ drm_mode_config_cleanup(dev);
+ drm_irq_uninstall(dev);
+ dev->dev_private = NULL;
+
+ return ret;
+}
+
+static void fsl_dcu_unload(struct drm_device *dev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ drm_atomic_helper_shutdown(dev);
+ drm_kms_helper_poll_fini(dev);
+
+ if (fsl_dev->fbdev)
+ drm_fbdev_cma_fini(fsl_dev->fbdev);
+
+ drm_mode_config_cleanup(dev);
+ drm_irq_uninstall(dev);
+
+ dev->dev_private = NULL;
+}
+
+static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
+{
+ struct drm_device *dev = arg;
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ unsigned int int_status;
+ int ret;
+
+ ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status);
+ if (ret) {
+ dev_err(dev->dev, "read DCU_INT_STATUS failed\n");
+ return IRQ_NONE;
+ }
+
+ if (int_status & DCU_INT_STATUS_VBLANK)
+ drm_handle_vblank(dev, 0);
+
+ regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status);
+
+ return IRQ_HANDLED;
+}
+
+static void fsl_dcu_drm_lastclose(struct drm_device *dev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+ drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(fsl_dcu_drm_fops);
+
+static struct drm_driver fsl_dcu_drm_driver = {
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
+ | DRIVER_PRIME | DRIVER_ATOMIC,
+ .lastclose = fsl_dcu_drm_lastclose,
+ .load = fsl_dcu_load,
+ .unload = fsl_dcu_unload,
+ .irq_handler = fsl_dcu_drm_irq,
+ .irq_preinstall = fsl_dcu_irq_uninstall,
+ .irq_uninstall = fsl_dcu_irq_uninstall,
+ .gem_free_object_unlocked = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .fops = &fsl_dcu_drm_fops,
+ .name = "fsl-dcu-drm",
+ .desc = "Freescale DCU DRM",
+ .date = "20160425",
+ .major = 1,
+ .minor = 1,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_dcu_drm_pm_suspend(struct device *dev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
+ int ret;
+
+ if (!fsl_dev)
+ return 0;
+
+ disable_irq(fsl_dev->irq);
+
+ ret = drm_mode_config_helper_suspend(fsl_dev->drm);
+ if (ret) {
+ enable_irq(fsl_dev->irq);
+ return ret;
+ }
+
+ clk_disable_unprepare(fsl_dev->clk);
+
+ return 0;
+}
+
+static int fsl_dcu_drm_pm_resume(struct device *dev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
+ int ret;
+
+ if (!fsl_dev)
+ return 0;
+
+ ret = clk_prepare_enable(fsl_dev->clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable dcu clk\n");
+ return ret;
+ }
+
+ if (fsl_dev->tcon)
+ fsl_tcon_bypass_enable(fsl_dev->tcon);
+ fsl_dcu_drm_init_planes(fsl_dev->drm);
+ enable_irq(fsl_dev->irq);
+
+ drm_mode_config_helper_resume(fsl_dev->drm);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops fsl_dcu_drm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_dcu_drm_pm_suspend, fsl_dcu_drm_pm_resume)
+};
+
+static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = {
+ .name = "ls1021a",
+ .total_layer = 16,
+ .max_layer = 4,
+ .layer_regs = LS1021A_LAYER_REG_NUM,
+};
+
+static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = {
+ .name = "vf610",
+ .total_layer = 64,
+ .max_layer = 6,
+ .layer_regs = VF610_LAYER_REG_NUM,
+};
+
+static const struct of_device_id fsl_dcu_of_match[] = {
+ {
+ .compatible = "fsl,ls1021a-dcu",
+ .data = &fsl_dcu_ls1021a_data,
+ }, {
+ .compatible = "fsl,vf610-dcu",
+ .data = &fsl_dcu_vf610_data,
+ }, {
+ },
+};
+MODULE_DEVICE_TABLE(of, fsl_dcu_of_match);
+
+static int fsl_dcu_drm_probe(struct platform_device *pdev)
+{
+ struct fsl_dcu_drm_device *fsl_dev;
+ struct drm_device *drm;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *base;
+ struct drm_driver *driver = &fsl_dcu_drm_driver;
+ struct clk *pix_clk_in;
+ char pix_clk_name[32];
+ const char *pix_clk_in_name;
+ const struct of_device_id *id;
+ int ret;
+ u8 div_ratio_shift = 0;
+
+ fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL);
+ if (!fsl_dev)
+ return -ENOMEM;
+
+ id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
+ if (!id)
+ return -ENODEV;
+ fsl_dev->soc = id->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ return ret;
+ }
+
+ fsl_dev->irq = platform_get_irq(pdev, 0);
+ if (fsl_dev->irq < 0) {
+ dev_err(dev, "failed to get irq\n");
+ return fsl_dev->irq;
+ }
+
+ fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
+ &fsl_dcu_regmap_config);
+ if (IS_ERR(fsl_dev->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(fsl_dev->regmap);
+ }
+
+ fsl_dev->clk = devm_clk_get(dev, "dcu");
+ if (IS_ERR(fsl_dev->clk)) {
+ dev_err(dev, "failed to get dcu clock\n");
+ return PTR_ERR(fsl_dev->clk);
+ }
+ ret = clk_prepare_enable(fsl_dev->clk);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable dcu clk\n");
+ return ret;
+ }
+
+ pix_clk_in = devm_clk_get(dev, "pix");
+ if (IS_ERR(pix_clk_in)) {
+ /* legancy binding, use dcu clock as pixel clock input */
+ pix_clk_in = fsl_dev->clk;
+ }
+
+ if (of_property_read_bool(dev->of_node, "big-endian"))
+ div_ratio_shift = 24;
+
+ pix_clk_in_name = __clk_get_name(pix_clk_in);
+ snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
+ fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
+ pix_clk_in_name, 0, base + DCU_DIV_RATIO,
+ div_ratio_shift, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
+ if (IS_ERR(fsl_dev->pix_clk)) {
+ dev_err(dev, "failed to register pix clk\n");
+ ret = PTR_ERR(fsl_dev->pix_clk);
+ goto disable_clk;
+ }
+
+ fsl_dev->tcon = fsl_tcon_init(dev);
+
+ drm = drm_dev_alloc(driver, dev);
+ if (IS_ERR(drm)) {
+ ret = PTR_ERR(drm);
+ goto unregister_pix_clk;
+ }
+
+ fsl_dev->dev = dev;
+ fsl_dev->drm = drm;
+ fsl_dev->np = dev->of_node;
+ drm->dev_private = fsl_dev;
+ dev_set_drvdata(dev, fsl_dev);
+
+ ret = drm_dev_register(drm, 0);
+ if (ret < 0)
+ goto unref;
+
+ return 0;
+
+unref:
+ drm_dev_unref(drm);
+unregister_pix_clk:
+ clk_unregister(fsl_dev->pix_clk);
+disable_clk:
+ clk_disable_unprepare(fsl_dev->clk);
+ return ret;
+}
+
+static int fsl_dcu_drm_remove(struct platform_device *pdev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
+
+ drm_dev_unregister(fsl_dev->drm);
+ drm_dev_unref(fsl_dev->drm);
+ clk_disable_unprepare(fsl_dev->clk);
+ clk_unregister(fsl_dev->pix_clk);
+
+ return 0;
+}
+
+static struct platform_driver fsl_dcu_drm_platform_driver = {
+ .probe = fsl_dcu_drm_probe,
+ .remove = fsl_dcu_drm_remove,
+ .driver = {
+ .name = "fsl-dcu",
+ .pm = &fsl_dcu_drm_pm_ops,
+ .of_match_table = fsl_dcu_of_match,
+ },
+};
+
+module_platform_driver(fsl_dcu_drm_platform_driver);
+
+MODULE_DESCRIPTION("Freescale DCU DRM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
new file mode 100644
index 000000000..93bfb9801
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __FSL_DCU_DRM_DRV_H__
+#define __FSL_DCU_DRM_DRV_H__
+
+#include <drm/drm_encoder.h>
+
+#include "fsl_dcu_drm_crtc.h"
+#include "fsl_dcu_drm_output.h"
+#include "fsl_dcu_drm_plane.h"
+
+#define DCU_DCU_MODE 0x0010
+#define DCU_MODE_BLEND_ITER(x) ((x) << 20)
+#define DCU_MODE_RASTER_EN BIT(14)
+#define DCU_MODE_DCU_MODE(x) (x)
+#define DCU_MODE_DCU_MODE_MASK 0x03
+#define DCU_MODE_OFF 0
+#define DCU_MODE_NORMAL 1
+#define DCU_MODE_TEST 2
+#define DCU_MODE_COLORBAR 3
+
+#define DCU_BGND 0x0014
+#define DCU_BGND_R(x) ((x) << 16)
+#define DCU_BGND_G(x) ((x) << 8)
+#define DCU_BGND_B(x) (x)
+
+#define DCU_DISP_SIZE 0x0018
+#define DCU_DISP_SIZE_DELTA_Y(x) ((x) << 16)
+/*Regisiter value 1/16 of horizontal resolution*/
+#define DCU_DISP_SIZE_DELTA_X(x) ((x) >> 4)
+
+#define DCU_HSYN_PARA 0x001c
+#define DCU_HSYN_PARA_BP(x) ((x) << 22)
+#define DCU_HSYN_PARA_PW(x) ((x) << 11)
+#define DCU_HSYN_PARA_FP(x) (x)
+
+#define DCU_VSYN_PARA 0x0020
+#define DCU_VSYN_PARA_BP(x) ((x) << 22)
+#define DCU_VSYN_PARA_PW(x) ((x) << 11)
+#define DCU_VSYN_PARA_FP(x) (x)
+
+#define DCU_SYN_POL 0x0024
+#define DCU_SYN_POL_INV_PXCK BIT(6)
+#define DCU_SYN_POL_NEG BIT(5)
+#define DCU_SYN_POL_INV_VS_LOW BIT(1)
+#define DCU_SYN_POL_INV_HS_LOW BIT(0)
+
+#define DCU_THRESHOLD 0x0028
+#define DCU_THRESHOLD_LS_BF_VS(x) ((x) << 16)
+#define DCU_THRESHOLD_OUT_BUF_HIGH(x) ((x) << 8)
+#define DCU_THRESHOLD_OUT_BUF_LOW(x) (x)
+#define BF_VS_VAL 0x03
+#define BUF_MAX_VAL 0x78
+#define BUF_MIN_VAL 0x0a
+
+#define DCU_INT_STATUS 0x002C
+#define DCU_INT_STATUS_VSYNC BIT(0)
+#define DCU_INT_STATUS_UNDRUN BIT(1)
+#define DCU_INT_STATUS_LSBFVS BIT(2)
+#define DCU_INT_STATUS_VBLANK BIT(3)
+#define DCU_INT_STATUS_CRCREADY BIT(4)
+#define DCU_INT_STATUS_CRCOVERFLOW BIT(5)
+#define DCU_INT_STATUS_P1FIFOLO BIT(6)
+#define DCU_INT_STATUS_P1FIFOHI BIT(7)
+#define DCU_INT_STATUS_P2FIFOLO BIT(8)
+#define DCU_INT_STATUS_P2FIFOHI BIT(9)
+#define DCU_INT_STATUS_PROGEND BIT(10)
+#define DCU_INT_STATUS_IPMERROR BIT(11)
+#define DCU_INT_STATUS_LYRTRANS BIT(12)
+#define DCU_INT_STATUS_DMATRANS BIT(14)
+#define DCU_INT_STATUS_P3FIFOLO BIT(16)
+#define DCU_INT_STATUS_P3FIFOHI BIT(17)
+#define DCU_INT_STATUS_P4FIFOLO BIT(18)
+#define DCU_INT_STATUS_P4FIFOHI BIT(19)
+#define DCU_INT_STATUS_P1EMPTY BIT(26)
+#define DCU_INT_STATUS_P2EMPTY BIT(27)
+#define DCU_INT_STATUS_P3EMPTY BIT(28)
+#define DCU_INT_STATUS_P4EMPTY BIT(29)
+
+#define DCU_INT_MASK 0x0030
+#define DCU_INT_MASK_VSYNC BIT(0)
+#define DCU_INT_MASK_UNDRUN BIT(1)
+#define DCU_INT_MASK_LSBFVS BIT(2)
+#define DCU_INT_MASK_VBLANK BIT(3)
+#define DCU_INT_MASK_CRCREADY BIT(4)
+#define DCU_INT_MASK_CRCOVERFLOW BIT(5)
+#define DCU_INT_MASK_P1FIFOLO BIT(6)
+#define DCU_INT_MASK_P1FIFOHI BIT(7)
+#define DCU_INT_MASK_P2FIFOLO BIT(8)
+#define DCU_INT_MASK_P2FIFOHI BIT(9)
+#define DCU_INT_MASK_PROGEND BIT(10)
+#define DCU_INT_MASK_IPMERROR BIT(11)
+#define DCU_INT_MASK_LYRTRANS BIT(12)
+#define DCU_INT_MASK_DMATRANS BIT(14)
+#define DCU_INT_MASK_P3FIFOLO BIT(16)
+#define DCU_INT_MASK_P3FIFOHI BIT(17)
+#define DCU_INT_MASK_P4FIFOLO BIT(18)
+#define DCU_INT_MASK_P4FIFOHI BIT(19)
+#define DCU_INT_MASK_P1EMPTY BIT(26)
+#define DCU_INT_MASK_P2EMPTY BIT(27)
+#define DCU_INT_MASK_P3EMPTY BIT(28)
+#define DCU_INT_MASK_P4EMPTY BIT(29)
+
+#define DCU_DIV_RATIO 0x0054
+
+#define DCU_UPDATE_MODE 0x00cc
+#define DCU_UPDATE_MODE_MODE BIT(31)
+#define DCU_UPDATE_MODE_READREG BIT(30)
+
+#define DCU_DCFB_MAX 0x300
+
+#define DCU_CTRLDESCLN(layer, reg) (0x200 + (reg - 1) * 4 + (layer) * 0x40)
+
+#define DCU_LAYER_HEIGHT(x) ((x) << 16)
+#define DCU_LAYER_WIDTH(x) (x)
+
+#define DCU_LAYER_POSY(x) ((x) << 16)
+#define DCU_LAYER_POSX(x) (x)
+
+#define DCU_LAYER_EN BIT(31)
+#define DCU_LAYER_TILE_EN BIT(30)
+#define DCU_LAYER_DATA_SEL_CLUT BIT(29)
+#define DCU_LAYER_SAFETY_EN BIT(28)
+#define DCU_LAYER_TRANS(x) ((x) << 20)
+#define DCU_LAYER_BPP(x) ((x) << 16)
+#define DCU_LAYER_RLE_EN BIT(15)
+#define DCU_LAYER_LUOFFS(x) ((x) << 4)
+#define DCU_LAYER_BB_ON BIT(2)
+#define DCU_LAYER_AB_NONE 0
+#define DCU_LAYER_AB_CHROMA_KEYING 1
+#define DCU_LAYER_AB_WHOLE_FRAME 2
+
+#define DCU_LAYER_CKMAX_R(x) ((x) << 16)
+#define DCU_LAYER_CKMAX_G(x) ((x) << 8)
+#define DCU_LAYER_CKMAX_B(x) (x)
+
+#define DCU_LAYER_CKMIN_R(x) ((x) << 16)
+#define DCU_LAYER_CKMIN_G(x) ((x) << 8)
+#define DCU_LAYER_CKMIN_B(x) (x)
+
+#define DCU_LAYER_TILE_VER(x) ((x) << 16)
+#define DCU_LAYER_TILE_HOR(x) (x)
+
+#define DCU_LAYER_FG_FCOLOR(x) (x)
+
+#define DCU_LAYER_BG_BCOLOR(x) (x)
+
+#define DCU_LAYER_POST_SKIP(x) ((x) << 16)
+#define DCU_LAYER_PRE_SKIP(x) (x)
+
+#define FSL_DCU_RGB565 4
+#define FSL_DCU_RGB888 5
+#define FSL_DCU_ARGB8888 6
+#define FSL_DCU_ARGB1555 11
+#define FSL_DCU_ARGB4444 12
+#define FSL_DCU_YUV422 14
+
+#define VF610_LAYER_REG_NUM 9
+#define LS1021A_LAYER_REG_NUM 10
+
+struct clk;
+struct device;
+struct drm_device;
+
+struct fsl_dcu_soc_data {
+ const char *name;
+ /*total layer number*/
+ unsigned int total_layer;
+ /*max layer number DCU supported*/
+ unsigned int max_layer;
+ unsigned int layer_regs;
+};
+
+struct fsl_dcu_drm_device {
+ struct device *dev;
+ struct device_node *np;
+ struct regmap *regmap;
+ int irq;
+ struct clk *clk;
+ struct clk *pix_clk;
+ struct fsl_tcon *tcon;
+ /*protects hardware register*/
+ spinlock_t irq_lock;
+ struct drm_device *drm;
+ struct drm_fbdev_cma *fbdev;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
+ struct fsl_dcu_drm_connector connector;
+ const struct fsl_dcu_soc_data *soc;
+};
+
+int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev);
+
+#endif /* __FSL_DCU_DRM_DRV_H__ */
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c
new file mode 100644
index 000000000..ddc68e476
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "fsl_dcu_drm_crtc.h"
+#include "fsl_dcu_drm_drv.h"
+
+static const struct drm_mode_config_funcs fsl_dcu_drm_mode_config_funcs = {
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+ .fb_create = drm_gem_fb_create,
+};
+
+int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev)
+{
+ int ret;
+
+ drm_mode_config_init(fsl_dev->drm);
+
+ fsl_dev->drm->mode_config.min_width = 0;
+ fsl_dev->drm->mode_config.min_height = 0;
+ fsl_dev->drm->mode_config.max_width = 2031;
+ fsl_dev->drm->mode_config.max_height = 2047;
+ fsl_dev->drm->mode_config.funcs = &fsl_dcu_drm_mode_config_funcs;
+
+ ret = fsl_dcu_drm_crtc_create(fsl_dev);
+ if (ret)
+ goto err;
+
+ ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc);
+ if (ret)
+ goto err;
+
+ ret = fsl_dcu_create_outputs(fsl_dev);
+ if (ret)
+ goto err;
+
+ drm_mode_config_reset(fsl_dev->drm);
+ drm_kms_helper_poll_init(fsl_dev->drm);
+
+ return 0;
+
+err:
+ drm_mode_config_cleanup(fsl_dev->drm);
+ return ret;
+}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h
new file mode 100644
index 000000000..5a7b88e19
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_output.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __FSL_DCU_DRM_CONNECTOR_H__
+#define __FSL_DCU_DRM_CONNECTOR_H__
+
+struct fsl_dcu_drm_connector {
+ struct drm_connector base;
+ struct drm_encoder *encoder;
+ struct drm_panel *panel;
+};
+
+static inline struct fsl_dcu_drm_connector *
+to_fsl_dcu_connector(struct drm_connector *con)
+{
+ return con ? container_of(con, struct fsl_dcu_drm_connector, base)
+ : NULL;
+}
+
+int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
+ struct drm_crtc *crtc);
+int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev);
+
+#endif /* __FSL_DCU_DRM_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
new file mode 100644
index 000000000..9554b2457
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/regmap.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "fsl_dcu_drm_drv.h"
+#include "fsl_dcu_drm_plane.h"
+
+static int fsl_dcu_drm_plane_index(struct drm_plane *plane)
+{
+ struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private;
+ unsigned int total_layer = fsl_dev->soc->total_layer;
+ unsigned int index;
+
+ index = drm_plane_index(plane);
+ if (index < total_layer)
+ return total_layer - index - 1;
+
+ dev_err(fsl_dev->dev, "No more layer left\n");
+ return -EINVAL;
+}
+
+static int fsl_dcu_drm_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct drm_framebuffer *fb = state->fb;
+
+ if (!state->fb || !state->crtc)
+ return 0;
+
+ switch (fb->format->format) {
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_RGB888:
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB4444:
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_YUV422:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void fsl_dcu_drm_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private;
+ unsigned int value;
+ int index;
+
+ index = fsl_dcu_drm_plane_index(plane);
+ if (index < 0)
+ return;
+
+ regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), &value);
+ value &= ~DCU_LAYER_EN;
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), value);
+}
+
+static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+
+{
+ struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private;
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = plane->state->fb;
+ struct drm_gem_cma_object *gem;
+ unsigned int alpha = DCU_LAYER_AB_NONE, bpp;
+ int index;
+
+ if (!fb)
+ return;
+
+ index = fsl_dcu_drm_plane_index(plane);
+ if (index < 0)
+ return;
+
+ gem = drm_fb_cma_get_gem_obj(fb, 0);
+
+ switch (fb->format->format) {
+ case DRM_FORMAT_RGB565:
+ bpp = FSL_DCU_RGB565;
+ break;
+ case DRM_FORMAT_RGB888:
+ bpp = FSL_DCU_RGB888;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ alpha = DCU_LAYER_AB_WHOLE_FRAME;
+ /* fall-through */
+ case DRM_FORMAT_XRGB8888:
+ bpp = FSL_DCU_ARGB8888;
+ break;
+ case DRM_FORMAT_ARGB4444:
+ alpha = DCU_LAYER_AB_WHOLE_FRAME;
+ /* fall-through */
+ case DRM_FORMAT_XRGB4444:
+ bpp = FSL_DCU_ARGB4444;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ alpha = DCU_LAYER_AB_WHOLE_FRAME;
+ /* fall-through */
+ case DRM_FORMAT_XRGB1555:
+ bpp = FSL_DCU_ARGB1555;
+ break;
+ case DRM_FORMAT_YUV422:
+ bpp = FSL_DCU_YUV422;
+ break;
+ default:
+ return;
+ }
+
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 1),
+ DCU_LAYER_HEIGHT(state->crtc_h) |
+ DCU_LAYER_WIDTH(state->crtc_w));
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 2),
+ DCU_LAYER_POSY(state->crtc_y) |
+ DCU_LAYER_POSX(state->crtc_x));
+ regmap_write(fsl_dev->regmap,
+ DCU_CTRLDESCLN(index, 3), gem->paddr);
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4),
+ DCU_LAYER_EN |
+ DCU_LAYER_TRANS(0xff) |
+ DCU_LAYER_BPP(bpp) |
+ alpha);
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 5),
+ DCU_LAYER_CKMAX_R(0xFF) |
+ DCU_LAYER_CKMAX_G(0xFF) |
+ DCU_LAYER_CKMAX_B(0xFF));
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 6),
+ DCU_LAYER_CKMIN_R(0) |
+ DCU_LAYER_CKMIN_G(0) |
+ DCU_LAYER_CKMIN_B(0));
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), 0);
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 8),
+ DCU_LAYER_FG_FCOLOR(0));
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 9),
+ DCU_LAYER_BG_BCOLOR(0));
+
+ if (!strcmp(fsl_dev->soc->name, "ls1021a")) {
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 10),
+ DCU_LAYER_POST_SKIP(0) |
+ DCU_LAYER_PRE_SKIP(0));
+ }
+
+ return;
+}
+
+static const struct drm_plane_helper_funcs fsl_dcu_drm_plane_helper_funcs = {
+ .atomic_check = fsl_dcu_drm_plane_atomic_check,
+ .atomic_disable = fsl_dcu_drm_plane_atomic_disable,
+ .atomic_update = fsl_dcu_drm_plane_atomic_update,
+};
+
+static void fsl_dcu_drm_plane_destroy(struct drm_plane *plane)
+{
+ drm_plane_cleanup(plane);
+ kfree(plane);
+}
+
+static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .destroy = fsl_dcu_drm_plane_destroy,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .update_plane = drm_atomic_helper_update_plane,
+};
+
+static const u32 fsl_dcu_drm_plane_formats[] = {
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_YUV422,
+};
+
+void fsl_dcu_drm_init_planes(struct drm_device *dev)
+{
+ struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+ int i, j;
+
+ for (i = 0; i < fsl_dev->soc->total_layer; i++) {
+ for (j = 1; j <= fsl_dev->soc->layer_regs; j++)
+ regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(i, j), 0);
+ }
+}
+
+struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev)
+{
+ struct drm_plane *primary;
+ int ret;
+
+ primary = kzalloc(sizeof(*primary), GFP_KERNEL);
+ if (!primary) {
+ DRM_DEBUG_KMS("Failed to allocate primary plane\n");
+ return NULL;
+ }
+
+ /* possible_crtc's will be filled in later by crtc_init */
+ ret = drm_universal_plane_init(dev, primary, 0,
+ &fsl_dcu_drm_plane_funcs,
+ fsl_dcu_drm_plane_formats,
+ ARRAY_SIZE(fsl_dcu_drm_plane_formats),
+ NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret) {
+ kfree(primary);
+ primary = NULL;
+ }
+ drm_plane_helper_add(primary, &fsl_dcu_drm_plane_helper_funcs);
+
+ return primary;
+}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h
new file mode 100644
index 000000000..8ee45f813
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __FSL_DCU_DRM_PLANE_H__
+#define __FSL_DCU_DRM_PLANE_H__
+
+void fsl_dcu_drm_init_planes(struct drm_device *dev);
+struct drm_plane *fsl_dcu_drm_primary_create_plane(struct drm_device *dev);
+
+#endif /* __FSL_DCU_DRM_PLANE_H__ */
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
new file mode 100644
index 000000000..2298ed2a9
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015 Freescale Semiconductor, Inc.
+ *
+ * Freescale DCU drm device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/backlight.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
+
+static void fsl_dcu_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs encoder_funcs = {
+ .destroy = fsl_dcu_drm_encoder_destroy,
+};
+
+int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
+ struct drm_crtc *crtc)
+{
+ struct drm_encoder *encoder = &fsl_dev->encoder;
+ int ret;
+
+ encoder->possible_crtcs = 1;
+
+ /* Use bypass mode for parallel RGB/LVDS encoder */
+ if (fsl_dev->tcon)
+ fsl_tcon_bypass_enable(fsl_dev->tcon);
+
+ ret = drm_encoder_init(fsl_dev->drm, encoder, &encoder_funcs,
+ DRM_MODE_ENCODER_LVDS, NULL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector)
+{
+ struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector);
+
+ drm_connector_unregister(connector);
+ drm_panel_detach(fsl_con->panel);
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs fsl_dcu_drm_connector_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .destroy = fsl_dcu_drm_connector_destroy,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .reset = drm_atomic_helper_connector_reset,
+};
+
+static int fsl_dcu_drm_connector_get_modes(struct drm_connector *connector)
+{
+ struct fsl_dcu_drm_connector *fsl_connector;
+ int (*get_modes)(struct drm_panel *panel);
+ int num_modes = 0;
+
+ fsl_connector = to_fsl_dcu_connector(connector);
+ if (fsl_connector->panel && fsl_connector->panel->funcs &&
+ fsl_connector->panel->funcs->get_modes) {
+ get_modes = fsl_connector->panel->funcs->get_modes;
+ num_modes = get_modes(fsl_connector->panel);
+ }
+
+ return num_modes;
+}
+
+static int fsl_dcu_drm_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ if (mode->hdisplay & 0xf)
+ return MODE_ERROR;
+
+ return MODE_OK;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = fsl_dcu_drm_connector_get_modes,
+ .mode_valid = fsl_dcu_drm_connector_mode_valid,
+};
+
+static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev,
+ struct drm_panel *panel)
+{
+ struct drm_encoder *encoder = &fsl_dev->encoder;
+ struct drm_connector *connector = &fsl_dev->connector.base;
+ int ret;
+
+ fsl_dev->connector.encoder = encoder;
+
+ ret = drm_connector_init(fsl_dev->drm, connector,
+ &fsl_dcu_drm_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ if (ret < 0)
+ return ret;
+
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+ ret = drm_connector_register(connector);
+ if (ret < 0)
+ goto err_cleanup;
+
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret < 0)
+ goto err_sysfs;
+
+ ret = drm_panel_attach(panel, connector);
+ if (ret) {
+ dev_err(fsl_dev->dev, "failed to attach panel\n");
+ goto err_sysfs;
+ }
+
+ return 0;
+
+err_sysfs:
+ drm_connector_unregister(connector);
+err_cleanup:
+ drm_connector_cleanup(connector);
+ return ret;
+}
+
+int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
+{
+ struct device_node *panel_node;
+ struct drm_panel *panel;
+ struct drm_bridge *bridge;
+ int ret;
+
+ /* This is for backward compatibility */
+ panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0);
+ if (panel_node) {
+ fsl_dev->connector.panel = of_drm_find_panel(panel_node);
+ of_node_put(panel_node);
+ if (IS_ERR(fsl_dev->connector.panel))
+ return PTR_ERR(fsl_dev->connector.panel);
+
+ return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel);
+ }
+
+ ret = drm_of_find_panel_or_bridge(fsl_dev->np, 0, 0, &panel, &bridge);
+ if (ret)
+ return ret;
+
+ if (panel) {
+ fsl_dev->connector.panel = panel;
+ return fsl_dcu_attach_panel(fsl_dev, panel);
+ }
+
+ return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL);
+}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
new file mode 100644
index 000000000..b3d70a63c
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "fsl_tcon.h"
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
+{
+ regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+ FSL_TCON_CTRL1_TCON_BYPASS, 0);
+}
+
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
+{
+ regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+ FSL_TCON_CTRL1_TCON_BYPASS,
+ FSL_TCON_CTRL1_TCON_BYPASS);
+}
+
+static struct regmap_config fsl_tcon_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .name = "tcon",
+};
+
+static int fsl_tcon_init_regmap(struct device *dev,
+ struct fsl_tcon *tcon,
+ struct device_node *np)
+{
+ struct resource res;
+ void __iomem *regs;
+
+ if (of_address_to_resource(np, 0, &res))
+ return -EINVAL;
+
+ regs = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ tcon->regs = devm_regmap_init_mmio(dev, regs,
+ &fsl_tcon_regmap_config);
+ return PTR_ERR_OR_ZERO(tcon->regs);
+}
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev)
+{
+ struct fsl_tcon *tcon;
+ struct device_node *np;
+ int ret;
+
+ /* TCON node is not mandatory, some devices do not provide TCON */
+ np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
+ if (!np)
+ return NULL;
+
+ tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
+ if (!tcon)
+ goto err_node_put;
+
+ ret = fsl_tcon_init_regmap(dev, tcon, np);
+ if (ret) {
+ dev_err(dev, "Couldn't create the TCON regmap\n");
+ goto err_node_put;
+ }
+
+ tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
+ if (IS_ERR(tcon->ipg_clk)) {
+ dev_err(dev, "Couldn't get the TCON bus clock\n");
+ goto err_node_put;
+ }
+
+ ret = clk_prepare_enable(tcon->ipg_clk);
+ if (ret) {
+ dev_err(dev, "Couldn't enable the TCON clock\n");
+ goto err_node_put;
+ }
+
+ of_node_put(np);
+ dev_info(dev, "Using TCON in bypass mode\n");
+
+ return tcon;
+
+err_node_put:
+ of_node_put(np);
+ return NULL;
+}
+
+void fsl_tcon_free(struct fsl_tcon *tcon)
+{
+ clk_disable_unprepare(tcon->ipg_clk);
+ clk_put(tcon->ipg_clk);
+}
+
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.h b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
new file mode 100644
index 000000000..80a7617de
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __FSL_TCON_H__
+#define __FSL_TCON_H__
+
+#include <linux/bitops.h>
+
+#define FSL_TCON_CTRL1 0x0
+#define FSL_TCON_CTRL1_TCON_BYPASS BIT(29)
+
+struct fsl_tcon {
+ struct regmap *regs;
+ struct clk *ipg_clk;
+};
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev);
+void fsl_tcon_free(struct fsl_tcon *tcon);
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon);
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon);
+
+#endif /* __FSL_TCON_H__ */