From patchwork Tue Jun 7 10:45:50 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sascha Hauer X-Patchwork-Id: 855862 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p57AmSwD031146 for ; Tue, 7 Jun 2011 10:48:48 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CCA8C9EB4A for ; Tue, 7 Jun 2011 03:48:27 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from metis.ext.pengutronix.de (metis.ext.pengutronix.de [92.198.50.35]) by gabe.freedesktop.org (Postfix) with ESMTP id 6603F9E83E for ; Tue, 7 Jun 2011 03:46:15 -0700 (PDT) Received: from octopus.hi.pengutronix.de ([2001:6f8:1178:2:215:17ff:fe12:23b0]) by metis.ext.pengutronix.de with esmtp (Exim 4.72) (envelope-from ) id 1QTtnZ-0002z0-Cg; Tue, 07 Jun 2011 12:46:13 +0200 Received: from sha by octopus.hi.pengutronix.de with local (Exim 4.76) (envelope-from ) id 1QTtnX-0007Ec-6O; Tue, 07 Jun 2011 12:46:11 +0200 From: Sascha Hauer To: linux-arm-kernel@lists.infradead.org Subject: [PATCH 5/5] DRM: Add i.MX IPUv3 support Date: Tue, 7 Jun 2011 12:45:50 +0200 Message-Id: <1307443550-25549-6-git-send-email-s.hauer@pengutronix.de> X-Mailer: git-send-email 1.7.5.3 In-Reply-To: <1307443550-25549-1-git-send-email-s.hauer@pengutronix.de> References: <1307443550-25549-1-git-send-email-s.hauer@pengutronix.de> X-SA-Exim-Connect-IP: 2001:6f8:1178:2:215:17ff:fe12:23b0 X-SA-Exim-Mail-From: sha@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: dri-devel@lists.freedesktop.org Cc: Konstantinos Margaritis , Eric Miao , Jason Chen , DRI mailing list X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 07 Jun 2011 10:48:48 +0000 (UTC) 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 --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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; +}; +