This adds a i.MX51/53 IPU (Image Processing Unit) KMS driver. The
driver has been tested on the i.MX51 babbage board and the i.MX53
LOCO board in different clone mode and dual head setups.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 drivers/gpu/drm/Kconfig        |    9 +
 drivers/gpu/drm/imx/Makefile   |    2 +
 drivers/gpu/drm/imx/imx-drm.c  |  936 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/imx/imx-priv.h |    9 +
 4 files changed, 956 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/imx/imx-drm.c
 create mode 100644 drivers/gpu/drm/imx/imx-priv.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 01d5444..93a2c5a 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -177,3 +177,12 @@ config DRM_IMX_IPUV3
        depends on DRM && ARCH_MXC
        help
          Choose this if you have a i.MX51/53 processor.
+
+config DRM_IMX
+       tristate "i.MX IPUv3 drm support"
+       depends on DRM_IMX_IPUV3
+       select DRM_KMS_ENCON
+       select DRM_KMS_HELPER
+       select FB_CFB_FILLRECT
+       select FB_CFB_COPYAREA
+       select FB_CFB_IMAGEBLIT
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
index 776e6b4..0a53cf4 100644
--- a/drivers/gpu/drm/imx/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
@@ -1 +1,3 @@
 obj-$(CONFIG_DRM_IMX_IPUV3) += ipu-v3/
+
+obj-$(CONFIG_DRM_IMX)  += imx-drm.o
diff --git a/drivers/gpu/drm/imx/imx-drm.c b/drivers/gpu/drm/imx/imx-drm.c
new file mode 100644
index 0000000..e9857c9
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-drm.c
@@ -0,0 +1,936 @@
+/*
+ * i.MX IPUv3 Graphics driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * 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.
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <drm/imx-ipu-v3.h>
+#include <asm/fb.h>
+#include <drm/drm_encon.h>
+
+#define DRIVER_NAME            "i.MX"
+#define DRIVER_DESC            "i.MX IPUv3 Graphics"
+#define DRIVER_DATE            "20110604"
+#define DRIVER_MAJOR           1
+#define DRIVER_MINOR           0
+#define DRIVER_PATCHLEVEL      0
+
+struct ipu_resource {
+       int ipu_channel_bg;
+       int dc_channel;
+       int dp_channel;
+       int display;
+       u32 interface_pix_fmt; /* FIXME: move to platform data */
+};
+
+static struct ipu_resource ipu_resources[] = {
+       {
+               .ipu_channel_bg = 23, /* IPUV3_CHANNEL_MEM_BG_SYNC */
+               .dc_channel = 5,
+               .dp_channel = IPU_DP_FLOW_SYNC_BG,
+               .display = 0,
+               .interface_pix_fmt = IPU_PIX_FMT_RGB24,
+       } , {
+               .ipu_channel_bg = 28, /* IPUV3_CHANNEL_MEM_DC_SYNC */
+               .dc_channel = 1,
+               .dp_channel = -1,
+               .display = 1,
+               .interface_pix_fmt = IPU_PIX_FMT_RGB565,
+       },
+};
+
+struct ipu_crtc {
+       struct drm_crtc         base;
+       int                     pipe;
+       struct ipu_resource     *ipu_res;
+       struct ipu_channel      *ipu_ch;
+       struct ipu_dc           *dc;
+       struct ipu_dp           *dp;
+       struct dmfc_channel     *dmfc;
+       struct ipu_di           *di;
+       int                     di_no;
+       struct clk              *pixclk;
+       int                     enabled;
+};
+
+struct ipu_framebuffer {
+       struct drm_framebuffer  base;
+       void                    *virt;
+       dma_addr_t              phys;
+       size_t                  len;
+};
+
+struct ipu_drm_private {
+       struct ipu_crtc         crtc[2];
+       struct drm_encoder_connector *encon[2];
+       struct drm_fb_helper    fb_helper;
+       struct ipu_framebuffer  ifb;
+       int                     num_crtcs;
+};
+
+static struct fb_ops ipu_ipufb_ops = {
+       .owner = THIS_MODULE,
+       .fb_check_var = drm_fb_helper_check_var,
+       .fb_set_par = drm_fb_helper_set_par,
+       .fb_fillrect = cfb_fillrect,
+       .fb_copyarea = cfb_copyarea,
+       .fb_imageblit = cfb_imageblit,
+       .fb_pan_display = drm_fb_helper_pan_display,
+       .fb_blank = drm_fb_helper_blank,
+       .fb_setcmap = drm_fb_helper_setcmap,
+       .fb_debug_enter = drm_fb_helper_debug_enter,
+       .fb_debug_leave = drm_fb_helper_debug_leave,
+};
+
+#define to_ipu_framebuffer(x) container_of(x, struct ipu_framebuffer, base)
+#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)
+
+static void ipu_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+       struct ipu_framebuffer *ipu_fb = to_ipu_framebuffer(fb);
+       struct drm_device *drm = fb->dev;
+
+       dma_free_writecombine(drm->dev, ipu_fb->len, ipu_fb->virt, 
ipu_fb->phys);
+
+       drm_framebuffer_cleanup(fb);
+
+       kfree(ipu_fb);
+}
+
+static int ipu_user_framebuffer_create_handle(struct drm_framebuffer *fb,
+                                               struct drm_file *file_priv,
+                                               unsigned int *handle)
+{
+       /* We do not use the handle atm */
+       *handle = 77;
+
+       return 0;
+}
+
+static const struct drm_framebuffer_funcs ipu_fb_funcs = {
+       .destroy = ipu_user_framebuffer_destroy,
+       .create_handle = ipu_user_framebuffer_create_handle,
+};
+
+static struct ipu_rgb def_rgb_32 = {
+       .red    = { .offset = 16, .length = 8, },
+       .green  = { .offset =  8, .length = 8, },
+       .blue   = { .offset =  0, .length = 8, },
+       .transp = { .offset = 24, .length = 8, },
+       .bits_per_pixel = 32,
+};
+
+static int calc_vref(struct drm_display_mode *mode)
+{
+       unsigned long htotal, vtotal;
+
+       htotal = mode->htotal;
+       vtotal = mode->vtotal;
+
+       if (!htotal || !vtotal)
+               return 60;
+
+       return mode->clock * 1000 / vtotal / htotal;
+}
+
+static int calc_bandwidth(struct drm_display_mode *mode, unsigned int vref)
+{
+       return mode->hdisplay * mode->vdisplay * vref;
+}
+
+static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
+{
+       if (ipu_crtc->enabled)
+               return;
+
+       ipu_di_enable(ipu_crtc->di);
+       ipu_dmfc_enable_channel(ipu_crtc->dmfc);
+       ipu_idmac_enable_channel(ipu_crtc->ipu_ch);
+       ipu_dc_enable_channel(ipu_crtc->dc);
+       if (ipu_crtc->dp)
+               ipu_dp_enable_channel(ipu_crtc->dp);
+
+       ipu_crtc->enabled = 1;
+}
+
+static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
+{
+       if (!ipu_crtc->enabled)
+               return;
+
+       if (ipu_crtc->dp)
+               ipu_dp_disable_channel(ipu_crtc->dp);
+       ipu_dc_disable_channel(ipu_crtc->dc);
+       ipu_idmac_disable_channel(ipu_crtc->ipu_ch);
+       ipu_dmfc_disable_channel(ipu_crtc->dmfc);
+       ipu_di_disable(ipu_crtc->di);
+
+       ipu_crtc->enabled = 0;
+}
+
+static int ipu_fb_set_par(struct drm_crtc *crtc,
+               struct drm_display_mode *mode,
+               unsigned long phys)
+{
+       struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+       struct drm_device *drm = crtc->dev;
+       struct drm_framebuffer *fb = crtc->fb;
+       int ret;
+       struct ipu_di_signal_cfg sig_cfg;
+       u32 out_pixel_fmt;
+       struct ipu_ch_param *cpmem = ipu_get_cpmem(ipu_crtc->ipu_ch);
+
+       ipu_fb_disable(ipu_crtc);
+
+       memset(cpmem, 0, sizeof(*cpmem));
+
+       memset(&sig_cfg, 0, sizeof(sig_cfg));
+       out_pixel_fmt = ipu_crtc->ipu_res->interface_pix_fmt;
+
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+               sig_cfg.interlaced = 1;
+       if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+               sig_cfg.Hsync_pol = 1;
+       if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+               sig_cfg.Vsync_pol = 1;
+       sig_cfg.enable_pol = 1;
+
+       sig_cfg.width = mode->hdisplay;
+       sig_cfg.height = mode->vdisplay;
+       sig_cfg.pixel_fmt = out_pixel_fmt;
+       sig_cfg.h_start_width = mode->htotal - mode->hsync_end;
+       sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start;
+       sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay;
+       sig_cfg.v_start_width = mode->vtotal - mode->vsync_end;
+       sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start;
+       sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay;
+       sig_cfg.v_to_h_sync = 0;
+
+       clk_set_rate(ipu_crtc->pixclk, mode->clock * 1000);
+
+       if (ipu_crtc->dp) {
+               ret = ipu_dp_setup_channel(ipu_crtc->dp, IPU_COLORSPACE_RGB,
+                               IPU_COLORSPACE_RGB);
+               if (ret) {
+                       dev_dbg(drm->dev, "initializing display processor 
failed with %d\n",
+                               ret);
+                       return ret;
+               }
+               ipu_dp_set_global_alpha(ipu_crtc->dp, 1, 0, 1);
+       }
+
+       ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di_no, 
sig_cfg.interlaced,
+                       out_pixel_fmt, mode->hdisplay);
+       if (ret) {
+               dev_dbg(drm->dev, "initializing display controller failed with 
%d\n",
+                               ret);
+               return ret;
+       }
+
+       ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
+       if (ret) {
+               dev_dbg(drm->dev, "initializing panel failed with %d\n", ret);
+               return ret;
+       }
+
+       ipu_cpmem_set_resolution(cpmem, mode->hdisplay, mode->vdisplay);
+       ipu_cpmem_set_stride(cpmem, fb->pitch);
+       ipu_cpmem_set_buffer(cpmem, 0, phys);
+       ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32);
+       ipu_cpmem_set_high_priority(cpmem);
+
+       ret = ipu_dmfc_init_channel(ipu_crtc->dmfc, mode->hdisplay);
+       if (ret) {
+               dev_dbg(drm->dev, "initializing dmfc channel failed with %d\n",
+                               ret);
+               return ret;
+       }
+
+       ret = ipu_dmfc_alloc_bandwidth(ipu_crtc->dmfc,
+                       calc_bandwidth(mode, calc_vref(mode)));
+       if (ret) {
+               dev_dbg(drm->dev, "allocating dmfc bandwidth failed with %d\n",
+                               ret);
+               return ret;
+       }
+
+       ipu_fb_enable(ipu_crtc);
+
+       return ret;
+}
+
+int ipu_framebuffer_init(struct drm_device *drm,
+                          struct ipu_framebuffer *ipu_fb,
+                          struct drm_mode_fb_cmd *mode_cmd)
+{
+       int ret;
+
+       if (mode_cmd->pitch & 63)
+               return -EINVAL;
+
+       switch (mode_cmd->bpp) {
+       case 8:
+       case 16:
+       case 24:
+       case 32:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = drm_framebuffer_init(drm, &ipu_fb->base, &ipu_fb_funcs);
+       if (ret) {
+               DRM_ERROR("framebuffer init failed %d\n", ret);
+               return ret;
+       }
+
+       drm_helper_mode_fill_fb_struct(&ipu_fb->base, mode_cmd);
+
+       return 0;
+}
+
+static int ipu_ipufb_create(struct drm_fb_helper *helper,
+                         struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_device *drm = helper->dev;
+       struct ipu_drm_private *priv = drm->dev_private;
+       struct fb_info *info;
+       struct drm_framebuffer *fb;
+       struct ipu_framebuffer *ipu_fb;
+       struct drm_mode_fb_cmd mode_cmd;
+       int size, ret;
+
+       /* we don't do packed 24bpp */
+       if (sizes->surface_bpp == 24)
+               sizes->surface_bpp = 32;
+
+       mode_cmd.width = sizes->surface_width;
+       mode_cmd.height = sizes->surface_height;
+
+       mode_cmd.bpp = sizes->surface_bpp;
+       mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64);
+       mode_cmd.depth = sizes->surface_depth;
+
+       size = mode_cmd.pitch * mode_cmd.height;
+       size = ALIGN(size, PAGE_SIZE);
+
+       mutex_lock(&drm->struct_mutex);
+
+       info = framebuffer_alloc(0, drm->dev);
+       if (!info) {
+               ret = -ENOMEM;
+               goto out_unpin;
+       }
+
+       info->par = helper;
+
+       ret = ipu_framebuffer_init(drm, &priv->ifb, &mode_cmd);
+       if (ret)
+               goto out_unpin;
+
+       ipu_fb = &priv->ifb;
+       fb = &ipu_fb->base;
+
+       priv->fb_helper.fb = fb;
+       priv->fb_helper.fbdev = info;
+
+       strcpy(info->fix.id, "imx_ipudrmfb");
+
+       info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+       info->fbops = &ipu_ipufb_ops;
+
+       info->screen_base = dma_alloc_writecombine(drm->dev,
+                               size,
+                               (dma_addr_t *)&info->fix.smem_start,
+                               GFP_DMA);
+       if (!info->screen_base) {
+               dev_err(drm->dev, "Unable to allocate framebuffer memory 
(%d)\n",
+                               size);
+               return -ENOMEM;
+       }
+
+       memset(info->screen_base, 0x80, size);
+
+       ipu_fb->virt = info->screen_base;
+       ipu_fb->phys = info->fix.smem_start;
+       ipu_fb->len = size;
+
+       info->fix.smem_len = size;
+
+       ret = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret) {
+               ret = -ENOMEM;
+               goto out_unpin;
+       }
+       info->screen_size = size;
+
+       drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
+       drm_fb_helper_fill_var(info, &priv->fb_helper, sizes->fb_width, 
sizes->fb_height);
+
+       info->pixmap.size = 64*1024;
+       info->pixmap.buf_align = 8;
+       info->pixmap.access_align = 32;
+       info->pixmap.flags = FB_PIXMAP_SYSTEM;
+       info->pixmap.scan_align = 1;
+
+       DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height);
+
+       mutex_unlock(&drm->struct_mutex);
+
+       return 0;
+
+out_unpin:
+       mutex_unlock(&drm->struct_mutex);
+
+       return ret;
+}
+
+static void ipu_ipufb_destroy(struct drm_device *drm)
+{
+       struct ipu_drm_private *priv = drm->dev_private;
+       struct ipu_framebuffer *ipu_fb = &priv->ifb;
+       struct fb_info *info = priv->fb_helper.fbdev;
+
+       unregister_framebuffer(info);
+       if (info->cmap.len)
+               fb_dealloc_cmap(&info->cmap);
+       framebuffer_release(info);
+
+        drm_fb_helper_fini(&priv->fb_helper);
+
+       drm_framebuffer_cleanup(&ipu_fb->base);
+
+       dma_free_writecombine(drm->dev, ipu_fb->len, ipu_fb->virt, 
ipu_fb->phys);
+}
+
+static int ipu_fb_find_or_create_single(struct drm_fb_helper *helper,
+               struct drm_fb_helper_surface_size *sizes)
+{
+       int new_fb = 0;
+       int ret;
+
+       if (!helper->fb) {
+               ret = ipu_ipufb_create(helper, sizes);
+               if (ret)
+                       return ret;
+               new_fb = 1;
+       }
+
+       return new_fb;
+}
+
+static void ipu_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+                                u16 blue, int regno)
+{
+}
+
+static void ipu_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+                            u16 *blue, int regno)
+{
+}
+
+static void ipu_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+static struct drm_fb_helper_funcs ipu_fb_helper_funcs = {
+       .gamma_set = ipu_crtc_fb_gamma_set,
+       .gamma_get = ipu_crtc_fb_gamma_get,
+       .fb_probe = ipu_fb_find_or_create_single,
+};
+
+static struct drm_framebuffer *
+ipu_user_framebuffer_create(struct drm_device *drm,
+                             struct drm_file *filp,
+                             struct drm_mode_fb_cmd *mode_cmd)
+{
+       struct ipu_framebuffer *ipu_fb;
+       int ret;
+
+       ipu_fb = kzalloc(sizeof(*ipu_fb), GFP_KERNEL);
+       if (!ipu_fb)
+               return ERR_PTR(-ENOMEM);
+
+       ret = ipu_framebuffer_init(drm, ipu_fb, mode_cmd);
+       if (ret) {
+               kfree(ipu_fb);
+               return ERR_PTR(ret);
+       }
+
+       ipu_fb->len = mode_cmd->width * mode_cmd->height * (mode_cmd->bpp >> 3);
+       ipu_fb->virt = dma_alloc_writecombine(drm->dev,
+                               ipu_fb->len,
+                               (dma_addr_t *)&ipu_fb->phys,
+                               GFP_DMA);
+       return &ipu_fb->base;
+}
+
+void ipu_fb_output_poll_changed(struct drm_device *dev)
+{
+}
+
+static const struct drm_mode_config_funcs ipu_mode_funcs = {
+       .fb_create = ipu_user_framebuffer_create,
+       .output_poll_changed = ipu_fb_output_poll_changed,
+};
+
+static void ipu_crtc_disable(struct drm_crtc *crtc)
+{
+       struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+       ipu_fb_disable(ipu_crtc);
+}
+
+static int ipu_crtc_mode_set(struct drm_crtc *crtc,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode,
+                              int x, int y,
+                              struct drm_framebuffer *old_fb)
+{
+       struct drm_framebuffer *fb = crtc->fb;
+       struct ipu_framebuffer *ipu_fb;
+       unsigned long phys;
+
+       ipu_fb = to_ipu_framebuffer(fb);
+
+       phys = ipu_fb->phys;
+       phys += x * 4; /* FIXME */
+       phys += y * fb->pitch;
+
+       ipu_fb_set_par(crtc, mode, phys);
+
+       return 0;
+}
+
+static int ipu_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                             struct drm_framebuffer *old_fb)
+{
+       struct drm_framebuffer *fb = crtc->fb;
+       struct ipu_framebuffer *ipu_fb;
+       struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); 
+       unsigned long phys;
+
+       ipu_fb = to_ipu_framebuffer(fb);
+
+       phys = ipu_fb->phys;
+       phys += x * 4; /* FIXME */
+       phys += y * fb->pitch;
+
+       ipu_cpmem_set_stride(ipu_get_cpmem(ipu_crtc->ipu_ch), fb->pitch);
+       ipu_cpmem_set_buffer(ipu_get_cpmem(ipu_crtc->ipu_ch),
+                         0, phys);
+       return 0;
+}
+
+static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+}
+
+static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void ipu_crtc_prepare(struct drm_crtc *crtc)
+{
+}
+
+static void ipu_crtc_commit(struct drm_crtc *crtc)
+{
+}
+
+static struct drm_crtc_helper_funcs ipu_helper_funcs = {
+       .dpms = ipu_crtc_dpms,
+       .mode_fixup = ipu_crtc_mode_fixup,
+       .mode_set = ipu_crtc_mode_set,
+       .mode_set_base = ipu_crtc_mode_set_base,
+       .prepare = ipu_crtc_prepare,
+       .commit = ipu_crtc_commit,
+       .load_lut = ipu_crtc_load_lut,
+       .disable = ipu_crtc_disable,
+};
+
+static int ipu_page_flip(struct drm_crtc *crtc,
+                         struct drm_framebuffer *fb,
+                         struct drm_pending_vblank_event *event)
+{
+       printk("%s\n", __func__);
+       dump_stack();
+       return 0;
+}
+
+static const struct drm_crtc_funcs ipu_crtc_funcs = {
+       .set_config = drm_crtc_helper_set_config,
+       .page_flip = ipu_page_flip,
+};
+
+static void ipu_put_resources(struct drm_device *drm, struct ipu_crtc 
*ipu_crtc)
+{
+       if (!IS_ERR(ipu_crtc->pixclk))
+               clk_put(ipu_crtc->pixclk);
+       if (!IS_ERR_OR_NULL(ipu_crtc->ipu_ch))
+               ipu_idmac_put(ipu_crtc->ipu_ch);
+       if (!IS_ERR_OR_NULL(ipu_crtc->dmfc))
+               ipu_dmfc_put(ipu_crtc->dmfc);
+       if (!IS_ERR_OR_NULL(ipu_crtc->dp))
+               ipu_dp_put(ipu_crtc->dp);
+       if (!IS_ERR_OR_NULL(ipu_crtc->di))
+               ipu_di_put(ipu_crtc->di);
+}
+
+static int ipu_get_resources(struct drm_device *drm, struct ipu_crtc *ipu_crtc)
+{
+       struct ipu_soc *ipu = dev_get_drvdata(drm->dev->parent);
+       struct ipu_resource *res = &ipu_resources[ipu_crtc->pipe];
+       int ret;
+       ipu_crtc->ipu_res = res;
+
+       if (ipu_crtc->pipe == 0)
+               ipu_crtc->pixclk = clk_get(drm->dev, "pixclock0");
+       else
+               ipu_crtc->pixclk = clk_get(drm->dev, "pixclock1");
+       if (IS_ERR(ipu_crtc->pixclk)) {
+               ret = PTR_ERR(ipu_crtc->pixclk);
+               goto err_out;
+       }
+
+       ipu_crtc->ipu_ch = ipu_idmac_get(ipu, res->ipu_channel_bg);
+       if (IS_ERR_OR_NULL(ipu_crtc->ipu_ch)) {
+               ret = PTR_ERR(ipu_crtc->ipu_ch);
+               goto err_out;
+       }
+
+       ipu_crtc->dc = ipu_dc_get(ipu, res->dc_channel);
+       if (IS_ERR(ipu_crtc->dc)) {
+               ret = PTR_ERR(ipu_crtc->dc);
+               goto err_out;
+       }
+
+       ipu_crtc->dmfc = ipu_dmfc_get(ipu, res->ipu_channel_bg);
+       if (IS_ERR(ipu_crtc->dmfc)) {
+               ret = PTR_ERR(ipu_crtc->dmfc);
+               goto err_out;
+       }
+
+       if (res->dp_channel >= 0) {
+               ipu_crtc->dp = ipu_dp_get(ipu, res->dp_channel);
+               if (IS_ERR(ipu_crtc->dp)) {
+                       ret = PTR_ERR(ipu_crtc->ipu_ch);
+                       goto err_out;
+               }
+       }
+
+       ipu_crtc->di = ipu_di_get(ipu, res->display);
+       if (IS_ERR(ipu_crtc->di)) {
+               ret = PTR_ERR(ipu_crtc->di);
+               goto err_out;
+       }
+
+       return 0;
+err_out:
+       ipu_put_resources(drm, ipu_crtc);
+
+       return ret;
+}
+
+static int ipu_crtc_init(struct drm_device *drm, int pipe)
+{
+       struct ipu_drm_private *priv = drm->dev_private;
+       struct ipu_crtc *ipu_crtc = &priv->crtc[pipe];
+       int ret;
+
+       ipu_crtc->pipe = pipe;
+       ipu_crtc->di_no = pipe;
+
+       ret = ipu_get_resources(drm, ipu_crtc);
+       if (ret)
+               return ret;
+
+       drm_crtc_init(drm, &ipu_crtc->base, &ipu_crtc_funcs);
+       drm_mode_crtc_set_gamma_size(&ipu_crtc->base, 256);
+       drm_crtc_helper_add(&ipu_crtc->base, &ipu_helper_funcs);
+
+       return 0;
+}
+
+static void ipu_crtc_cleanup(struct drm_device *drm, int pipe)
+{
+       struct ipu_drm_private *priv = drm->dev_private;
+       struct ipu_crtc *ipu_crtc = &priv->crtc[pipe];
+
+       drm_crtc_cleanup(&ipu_crtc->base);
+       ipu_put_resources(drm, ipu_crtc);
+}
+
+static int ipu_fbdev_init(struct drm_device *drm)
+{
+       struct ipu_drm_private *priv = drm->dev_private;
+       struct drm_encoder_connector *encon;
+       int i, ret;
+
+       drm_mode_config_init(drm);
+
+       drm->mode_config.min_width = 0;
+       drm->mode_config.min_height = 0;
+
+       drm->mode_config.funcs = (void *)&ipu_mode_funcs;
+
+       drm->mode_config.max_width = 4096;
+       drm->mode_config.max_height = 4096;
+
+       drm->mode_config.fb_base = 0xdeadbeef;
+
+       for (i = 0; i < 2; i++) {
+               ret = ipu_crtc_init(drm, i);
+               if (ret)
+                       goto out;
+               priv->encon[i] = drm_encon_get(drm, i);
+               encon = priv->encon[i];
+               if (!encon)
+                       continue;
+               encon->encoder.possible_crtcs = 1 << i;
+               drm_encoder_connector_init(drm, encon);
+               priv->num_crtcs++;
+       }
+
+       priv->fb_helper.funcs = &ipu_fb_helper_funcs;
+
+       ret = drm_fb_helper_init(drm, &priv->fb_helper, priv->num_crtcs,
+                       priv->num_crtcs);
+       if (ret)
+               goto out;
+
+       drm_fb_helper_single_add_all_connectors(&priv->fb_helper);
+       drm_fb_helper_initial_config(&priv->fb_helper, 32);
+
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE
+       drm_fb_helper_restore_fbdev_mode(&priv->fb_helper);
+#endif
+       return 0;
+out:
+       do {
+               ipu_crtc_cleanup(drm, i);
+               if (priv->encon[i])
+                       drm_encoder_connector_cleanup(drm, priv->encon[i]);
+       } while (i--);
+
+       return ret;
+}
+
+static int ipu_mmap(struct file *filp, struct vm_area_struct * vma)
+{
+       struct drm_file *priv = filp->private_data;
+       struct drm_device *drm = priv->minor->dev;
+       struct drm_mode_object *obj;
+       struct drm_framebuffer *fb;
+       struct ipu_framebuffer *ipu_fb;
+       unsigned long off;
+       unsigned long start;
+       u32 len;
+
+       obj = drm_mode_object_find(drm, vma->vm_pgoff, DRM_MODE_OBJECT_FB);
+       if (!obj) {
+               dev_err(drm->dev, "could not find object %ld\n", vma->vm_pgoff);
+               return -ENOENT;
+       }
+
+       fb = obj_to_fb(obj);
+       ipu_fb = to_ipu_framebuffer(fb);
+
+       off = 0;
+
+       start = ipu_fb->phys;
+       len = PAGE_ALIGN((start & ~PAGE_MASK) + ipu_fb->len);
+       start &= PAGE_MASK;
+
+       if ((vma->vm_end - vma->vm_start + off) > len)
+               return -EINVAL;
+       off += start;
+       vma->vm_pgoff = off >> PAGE_SHIFT;
+       /* This is an IO map - tell maydump to skip this VMA */
+       vma->vm_flags |= VM_IO | VM_RESERVED;
+       vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+       fb_pgprotect(filp, vma, off);
+       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
+               return -EAGAIN;
+       return 0;
+}
+
+static int ipu_driver_load(struct drm_device *drm, unsigned long flags)
+{
+       struct ipu_drm_private *priv;
+       int ret;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+        if (!priv)
+                return -ENOMEM;
+
+        drm->dev_private = priv;
+
+       ret = ipu_fbdev_init(drm);
+       if (ret)
+               goto out;
+
+       drm_kms_helper_poll_init(drm);
+
+       return 0;
+
+out:
+       kfree(priv);
+
+       return ret;
+}
+
+static int ipu_driver_open(struct drm_device *drm, struct drm_file *file)
+{
+       return 0;
+}
+
+static void ipu_driver_lastclose(struct drm_device *drm)
+{
+       struct ipu_drm_private *priv = drm->dev_private;
+
+       drm_fb_helper_restore_fbdev_mode(&priv->fb_helper);
+}
+
+static int ipu_driver_unload(struct drm_device *drm)
+{
+       struct ipu_drm_private *priv = drm->dev_private;
+       int i;
+
+       ipu_ipufb_destroy(drm);
+
+       for (i = 0; i < 2; i++) {
+               ipu_crtc_cleanup(drm, i);
+
+               if (priv->encon[i])
+                       drm_encoder_connector_cleanup(drm, priv->encon[i]);
+       }
+
+       kfree(priv);
+
+       return 0;
+}
+
+static int ipu_suspend(struct drm_device *drm, pm_message_t state)
+{
+       return 0;
+}
+
+static int ipu_resume(struct drm_device *drm)
+{
+       return 0;
+}
+
+static int ipu_enable_vblank(struct drm_device *drm, int crtc)
+{
+       return 0;
+}
+
+static void ipu_disable_vblank(struct drm_device *drm, int crtc)
+{
+}
+
+struct drm_ioctl_desc ipu_ioctls[] = {
+};
+
+static struct drm_driver driver = {
+       .driver_features = DRIVER_MODESET,
+       .load = ipu_driver_load,
+       .unload = ipu_driver_unload,
+       .open = ipu_driver_open,
+       .lastclose = ipu_driver_lastclose,
+
+       /* Used in place of ipu_pm_ops for non-DRIVER_MODESET */
+       .suspend = ipu_suspend,
+       .resume = ipu_resume,
+
+       .enable_vblank = ipu_enable_vblank,
+       .disable_vblank = ipu_disable_vblank,
+       .reclaim_buffers = drm_core_reclaim_buffers,
+       .ioctls = ipu_ioctls,
+       .num_ioctls = ARRAY_SIZE(ipu_ioctls),
+       .fops = {
+                .owner = THIS_MODULE,
+                .open = drm_open,
+                .release = drm_release,
+                .unlocked_ioctl = drm_ioctl,
+                .mmap = ipu_mmap,
+                .poll = drm_poll,
+                .fasync = drm_fasync,
+                .read = drm_read,
+#ifdef CONFIG_COMPAT
+                .compat_ioctl = ipu_compat_ioctl,
+#endif
+                .llseek = noop_llseek,
+       },
+
+       .name = DRIVER_NAME,
+       .desc = DRIVER_DESC,
+       .date = DRIVER_DATE,
+       .major = DRIVER_MAJOR,
+       .minor = DRIVER_MINOR,
+       .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static int __devinit ipu_drm_probe(struct platform_device *pdev)
+{
+       pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+       return drm_platform_init(&driver, pdev);
+}
+
+static int __devexit ipu_drm_remove(struct platform_device *pdev)
+{
+       drm_platform_exit(&driver, pdev);
+
+       return 0;
+}
+
+static struct platform_driver ipu_drm_driver = {
+       .driver = {
+               .name = "imx-drm",
+       },
+       .probe = ipu_drm_probe,
+       .remove = __devexit_p(ipu_drm_remove),
+};
+
+int __init ipu_drm_init(void)
+{
+       return platform_driver_register(&ipu_drm_driver);
+}
+
+void __exit ipu_drm_exit(void)
+{
+       platform_driver_unregister(&ipu_drm_driver);
+}
+
+late_initcall(ipu_drm_init);
+module_exit(ipu_drm_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer at pengutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/imx/imx-priv.h b/drivers/gpu/drm/imx/imx-priv.h
new file mode 100644
index 0000000..32efc59
--- /dev/null
+++ b/drivers/gpu/drm/imx/imx-priv.h
@@ -0,0 +1,9 @@
+struct imx_ipu_encoder {
+       struct drm_encoder base;
+};
+
+struct imx_ipu_connector {
+       struct drm_connector base;
+       struct imx_ipu_encoder *encoder;
+};
+
-- 
1.7.5.3

Reply via email to