diff options
Diffstat (limited to 'drivers/staging/psb/psb_fb.c')
-rw-r--r-- | drivers/staging/psb/psb_fb.c | 1687 |
1 files changed, 1687 insertions, 0 deletions
diff --git a/drivers/staging/psb/psb_fb.c b/drivers/staging/psb/psb_fb.c new file mode 100644 index 000000000000..111e754e3b56 --- /dev/null +++ b/drivers/staging/psb/psb_fb.c @@ -0,0 +1,1687 @@ +/************************************************************************** + * Copyright (c) 2007, Intel Corporation. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to + * develop this driver. + * + **************************************************************************/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/console.h> + +#include <drm/drmP.h> +#include <drm/drm.h> +#include <drm/drm_crtc.h> + +#include "psb_drv.h" +#include "intel_reg.h" +#include "intel_drv.h" +#include "ttm/ttm_userobj_api.h" +#include "psb_fb.h" +#include "psb_sgx.h" + +static int fill_fb_bitfield(struct fb_var_screeninfo *var, int depth) +{ + switch (depth) { + case 8: + var->red.offset = 0; + var->green.offset = 0; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 15: + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + var->transp.length = 1; + var->transp.offset = 15; + break; + case 16: + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + var->transp.offset = 0; + break; + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 8; + var->transp.offset = 24; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb); +static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle); + +static const struct drm_framebuffer_funcs psb_fb_funcs = { + .destroy = psb_user_framebuffer_destroy, + .create_handle = psb_user_framebuffer_create_handle, +}; + +struct psbfb_par { + struct drm_device *dev; + struct psb_framebuffer *psbfb; + + int dpms_state; + + int crtc_count; + /* crtc currently bound to this */ + uint32_t crtc_ids[2]; +}; + +#define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) + +static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct psbfb_par *par = info->par; + struct drm_framebuffer *fb = &par->psbfb->base; + uint32_t v; + + if (!fb) + return -ENOMEM; + + if (regno > 255) + return 1; + +#if 0 /* JB: not drop, check that this works */ + if (fb->bits_per_pixel == 8) { + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + head) { + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + if (crtc->funcs->gamma_set) + crtc->funcs->gamma_set(crtc, red, green, + blue, regno); + } + return 0; + } +#endif + + red = CMAP_TOHW(red, info->var.red.length); + blue = CMAP_TOHW(blue, info->var.blue.length); + green = CMAP_TOHW(green, info->var.green.length); + transp = CMAP_TOHW(transp, info->var.transp.length); + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + + if (regno < 16) { + switch (fb->bits_per_pixel) { + case 16: + ((uint32_t *) info->pseudo_palette)[regno] = v; + break; + case 24: + case 32: + ((uint32_t *) info->pseudo_palette)[regno] = v; + break; + } + } + + return 0; +} + +static struct drm_display_mode *psbfb_find_first_mode(struct + fb_var_screeninfo + *var, + struct fb_info *info, + struct drm_crtc + *crtc) +{ + struct psbfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_display_mode *drm_mode; + struct drm_display_mode *last_mode = NULL; + struct drm_connector *connector; + int found; + + found = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + if (connector->encoder && connector->encoder->crtc == crtc) { + found = 1; + break; + } + } + + /* found no connector, bail */ + if (!found) + return NULL; + + found = 0; + list_for_each_entry(drm_mode, &connector->modes, head) { + if (drm_mode->hdisplay == var->xres && + drm_mode->vdisplay == var->yres + && drm_mode->clock != 0) { + found = 1; + last_mode = drm_mode; + } + } + + /* No mode matching mode found */ + if (!found) + return NULL; + + return last_mode; +} + +static int psbfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct psbfb_par *par = info->par; + struct psb_framebuffer *psbfb = par->psbfb; + struct drm_device *dev = par->dev; + int ret; + int depth; + int pitch; + int bpp = var->bits_per_pixel; + + if (!psbfb) + return -ENOMEM; + + if (!var->pixclock) + return -EINVAL; + + /* don't support virtuals for now */ + if (var->xres_virtual > var->xres) + return -EINVAL; + + if (var->yres_virtual > var->yres) + return -EINVAL; + + switch (bpp) { +#if 0 /* JB: for now only support true color */ + case 8: + depth = 8; + break; +#endif + case 16: + depth = (var->green.length == 6) ? 16 : 15; + break; + case 24: /* assume this is 32bpp / depth 24 */ + bpp = 32; + /* fallthrough */ + case 32: + depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + return -EINVAL; + } + + pitch = ((var->xres * ((bpp + 1) / 8)) + 0x3f) & ~0x3f; + + /* Check that we can resize */ + if ((pitch * var->yres) > (psbfb->bo->num_pages << PAGE_SHIFT)) { +#if 1 + /* Need to resize the fb object. + * But the generic fbdev code doesn't really understand + * that we can do this. So disable for now. + */ + DRM_INFO("Can't support requested size, too big!\n"); + return -EINVAL; +#else + struct drm_psb_private *dev_priv = psb_priv(dev); + struct ttm_bo_device *bdev = &dev_priv->bdev; + struct ttm_buffer_object *fbo = NULL; + struct ttm_bo_kmap_obj tmp_kmap; + + /* a temporary BO to check if we could resize in setpar. + * Therefore no need to set NO_EVICT. + */ + ret = ttm_buffer_object_create(bdev, + pitch * var->yres, + ttm_bo_type_kernel, + TTM_PL_FLAG_TT | + TTM_PL_FLAG_VRAM | + TTM_PL_FLAG_NO_EVICT, + 0, 0, &fbo); + if (ret || !fbo) + return -ENOMEM; + + ret = ttm_bo_kmap(fbo, 0, fbo->num_pages, &tmp_kmap); + if (ret) { + ttm_bo_usage_deref_unlocked(&fbo); + return -EINVAL; + } + + ttm_bo_kunmap(&tmp_kmap); + /* destroy our current fbo! */ + ttm_bo_usage_deref_unlocked(&fbo); +#endif + } + + ret = fill_fb_bitfield(var, depth); + if (ret) + return ret; + +#if 1 + /* Here we walk the output mode list and look for modes. If we haven't + * got it, then bail. Not very nice, so this is disabled. + * In the set_par code, we create our mode based on the incoming + * parameters. Nicer, but may not be desired by some. + */ + { + struct drm_crtc *crtc; + int i; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + head) { + struct intel_crtc *intel_crtc = + to_intel_crtc(crtc); + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + if (intel_crtc->mode_set.num_connectors == 0) + continue; + + if (!psbfb_find_first_mode(&info->var, info, crtc)) + return -EINVAL; + } + } +#else + (void) i; + (void) dev; /* silence warnings */ + (void) crtc; + (void) drm_mode; + (void) connector; +#endif + + return 0; +} + +/* this will let fbcon do the mode init */ +static int psbfb_set_par(struct fb_info *info) +{ + struct psbfb_par *par = info->par; + struct psb_framebuffer *psbfb = par->psbfb; + struct drm_framebuffer *fb = &psbfb->base; + struct drm_device *dev = par->dev; + struct fb_var_screeninfo *var = &info->var; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_display_mode *drm_mode; + int pitch; + int depth; + int bpp = var->bits_per_pixel; + + if (!fb) + return -ENOMEM; + + switch (bpp) { + case 8: + depth = 8; + break; + case 16: + depth = (var->green.length == 6) ? 16 : 15; + break; + case 24: /* assume this is 32bpp / depth 24 */ + bpp = 32; + /* fallthrough */ + case 32: + depth = (var->transp.length > 0) ? 32 : 24; + break; + default: + DRM_ERROR("Illegal BPP\n"); + return -EINVAL; + } + + pitch = ((var->xres * ((bpp + 1) / 8)) + 0x3f) & ~0x3f; + + if ((pitch * var->yres) > (psbfb->bo->num_pages << PAGE_SHIFT)) { +#if 1 + /* Need to resize the fb object. + * But the generic fbdev code doesn't really understand + * that we can do this. So disable for now. + */ + DRM_INFO("Can't support requested size, too big!\n"); + return -EINVAL; +#else + int ret; + struct ttm_buffer_object *fbo = NULL, *tfbo; + struct ttm_bo_kmap_obj tmp_kmap, tkmap; + + ret = ttm_buffer_object_create(bdev, + pitch * var->yres, + ttm_bo_type_kernel, + TTM_PL_FLAG_MEM_TT | + TTM_PL_FLAG_MEM_VRAM | + TTM_PL_FLAG_NO_EVICT, + 0, 0, &fbo); + if (ret || !fbo) { + DRM_ERROR + ("failed to allocate new resized framebuffer\n"); + return -ENOMEM; + } + + ret = ttm_bo_kmap(fbo, 0, fbo->num_pages, &tmp_kmap); + if (ret) { + DRM_ERROR("failed to kmap framebuffer.\n"); + ttm_bo_usage_deref_unlocked(&fbo); + return -EINVAL; + } + + DRM_DEBUG("allocated %dx%d fb: 0x%08lx, bo %p\n", + fb->width, fb->height, fb->offset, fbo); + + /* set new screen base */ + info->screen_base = tmp_kmap.virtual; + + tkmap = fb->kmap; + fb->kmap = tmp_kmap; + ttm_bo_kunmap(&tkmap); + + tfbo = fb->bo; + fb->bo = fbo; + ttm_bo_usage_deref_unlocked(&tfbo); +#endif + } + + psbfb->offset = psbfb->bo->offset - dev_priv->pg->gatt_start; + fb->width = var->xres; + fb->height = var->yres; + fb->bits_per_pixel = bpp; + fb->pitch = pitch; + fb->depth = depth; + + info->fix.line_length = psbfb->base.pitch; + info->fix.visual = + (psbfb->base.depth == + 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; + + /* some fbdev's apps don't want these to change */ + info->fix.smem_start = dev->mode_config.fb_base + psbfb->offset; + +#if 0 + /* relates to resize - disable */ + info->fix.smem_len = info->fix.line_length * var->yres; + info->screen_size = info->fix.smem_len; /* ??? */ +#endif + + /* Should we walk the output's modelist or just create our own ??? + * For now, we create and destroy a mode based on the incoming + * parameters. But there's commented out code below which scans + * the output list too. + */ +#if 1 + /* This code is now in the for loop futher down. */ +#endif + + { + struct drm_crtc *crtc; + int ret; + int i; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + head) { + struct intel_crtc *intel_crtc = + to_intel_crtc(crtc); + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + if (intel_crtc->mode_set.num_connectors == 0) + continue; + +#if 1 + drm_mode = + psbfb_find_first_mode(&info->var, info, crtc); + if (!drm_mode) + DRM_ERROR("No matching mode found\n"); + intel_crtc->mode_set.mode = drm_mode; +#endif + +#if 0 /* FIXME: TH */ + if (crtc->fb == intel_crtc->mode_set.fb) { +#endif + DRM_DEBUG + ("setting mode on crtc %p with id %u\n", + crtc, crtc->base.id); + ret = + crtc->funcs-> + set_config(&intel_crtc->mode_set); + if (ret) { + DRM_ERROR("Failed setting mode\n"); + return ret; + } +#if 0 + } +#endif + } + DRM_DEBUG("Set par returned OK.\n"); + return 0; + } + + return 0; +} + +static int psbfb_2d_submit(struct drm_psb_private *dev_priv, uint32_t *cmdbuf, + unsigned size) +{ + int ret = 0; + int i; + unsigned submit_size; + + while (size > 0) { + submit_size = (size < 0x60) ? size : 0x60; + size -= submit_size; + ret = psb_2d_wait_available(dev_priv, submit_size); + if (ret) + return ret; + + submit_size <<= 2; + for (i = 0; i < submit_size; i += 4) { + PSB_WSGX32(*cmdbuf++, PSB_SGX_2D_SLAVE_PORT + i); + } + (void)PSB_RSGX32(PSB_SGX_2D_SLAVE_PORT + i - 4); + } + return 0; +} + +static int psb_accel_2d_fillrect(struct drm_psb_private *dev_priv, + uint32_t dst_offset, uint32_t dst_stride, + uint32_t dst_format, uint16_t dst_x, + uint16_t dst_y, uint16_t size_x, + uint16_t size_y, uint32_t fill) +{ + uint32_t buffer[10]; + uint32_t *buf; + + buf = buffer; + + *buf++ = PSB_2D_FENCE_BH; + + *buf++ = + PSB_2D_DST_SURF_BH | dst_format | (dst_stride << + PSB_2D_DST_STRIDE_SHIFT); + *buf++ = dst_offset; + + *buf++ = + PSB_2D_BLIT_BH | + PSB_2D_ROT_NONE | + PSB_2D_COPYORDER_TL2BR | + PSB_2D_DSTCK_DISABLE | + PSB_2D_SRCCK_DISABLE | PSB_2D_USE_FILL | PSB_2D_ROP3_PATCOPY; + + *buf++ = fill << PSB_2D_FILLCOLOUR_SHIFT; + *buf++ = + (dst_x << PSB_2D_DST_XSTART_SHIFT) | (dst_y << + PSB_2D_DST_YSTART_SHIFT); + *buf++ = + (size_x << PSB_2D_DST_XSIZE_SHIFT) | (size_y << + PSB_2D_DST_YSIZE_SHIFT); + *buf++ = PSB_2D_FLUSH_BH; + + return psbfb_2d_submit(dev_priv, buffer, buf - buffer); +} + +static void psbfb_fillrect_accel(struct fb_info *info, + const struct fb_fillrect *r) +{ + struct psbfb_par *par = info->par; + struct psb_framebuffer *psbfb = par->psbfb; + struct drm_framebuffer *fb = &psbfb->base; + struct drm_psb_private *dev_priv = par->dev->dev_private; + uint32_t offset; + uint32_t stride; + uint32_t format; + + if (!fb) + return; + + offset = psbfb->offset; + stride = fb->pitch; + + switch (fb->depth) { + case 8: + format = PSB_2D_DST_332RGB; + break; + case 15: + format = PSB_2D_DST_555RGB; + break; + case 16: + format = PSB_2D_DST_565RGB; + break; + case 24: + case 32: + /* this is wrong but since we don't do blending its okay */ + format = PSB_2D_DST_8888ARGB; + break; + default: + /* software fallback */ + cfb_fillrect(info, r); + return; + } + + psb_accel_2d_fillrect(dev_priv, + offset, stride, format, + r->dx, r->dy, r->width, r->height, r->color); +} + +static void psbfb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct psbfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (unlikely(info->state != FBINFO_STATE_RUNNING)) + return; + + if (info->flags & FBINFO_HWACCEL_DISABLED) + return cfb_fillrect(info, rect); + + if (psb_2d_trylock(dev_priv)) { + psb_check_power_state(dev, PSB_DEVICE_SGX); + psbfb_fillrect_accel(info, rect); + psb_2d_unlock(dev_priv); + if (drm_psb_ospm && IS_MRST(dev)) + schedule_delayed_work(&dev_priv->scheduler.wq, 1); + } else + cfb_fillrect(info, rect); +} + +uint32_t psb_accel_2d_copy_direction(int xdir, int ydir) +{ + if (xdir < 0) + return (ydir < + 0) ? PSB_2D_COPYORDER_BR2TL : + PSB_2D_COPYORDER_TR2BL; + else + return (ydir < + 0) ? PSB_2D_COPYORDER_BL2TR : + PSB_2D_COPYORDER_TL2BR; +} + +/* + * @srcOffset in bytes + * @srcStride in bytes + * @srcFormat psb 2D format defines + * @dstOffset in bytes + * @dstStride in bytes + * @dstFormat psb 2D format defines + * @srcX offset in pixels + * @srcY offset in pixels + * @dstX offset in pixels + * @dstY offset in pixels + * @sizeX of the copied area + * @sizeY of the copied area + */ +static int psb_accel_2d_copy(struct drm_psb_private *dev_priv, + uint32_t src_offset, uint32_t src_stride, + uint32_t src_format, uint32_t dst_offset, + uint32_t dst_stride, uint32_t dst_format, + uint16_t src_x, uint16_t src_y, + uint16_t dst_x, uint16_t dst_y, + uint16_t size_x, uint16_t size_y) +{ + uint32_t blit_cmd; + uint32_t buffer[10]; + uint32_t *buf; + uint32_t direction; + + buf = buffer; + + direction = + psb_accel_2d_copy_direction(src_x - dst_x, src_y - dst_y); + + if (direction == PSB_2D_COPYORDER_BR2TL || + direction == PSB_2D_COPYORDER_TR2BL) { + src_x += size_x - 1; + dst_x += size_x - 1; + } + if (direction == PSB_2D_COPYORDER_BR2TL || + direction == PSB_2D_COPYORDER_BL2TR) { + src_y += size_y - 1; + dst_y += size_y - 1; + } + + blit_cmd = + PSB_2D_BLIT_BH | + PSB_2D_ROT_NONE | + PSB_2D_DSTCK_DISABLE | + PSB_2D_SRCCK_DISABLE | + PSB_2D_USE_PAT | PSB_2D_ROP3_SRCCOPY | direction; + + *buf++ = PSB_2D_FENCE_BH; + *buf++ = + PSB_2D_DST_SURF_BH | dst_format | (dst_stride << + PSB_2D_DST_STRIDE_SHIFT); + *buf++ = dst_offset; + *buf++ = + PSB_2D_SRC_SURF_BH | src_format | (src_stride << + PSB_2D_SRC_STRIDE_SHIFT); + *buf++ = src_offset; + *buf++ = + PSB_2D_SRC_OFF_BH | (src_x << PSB_2D_SRCOFF_XSTART_SHIFT) | + (src_y << PSB_2D_SRCOFF_YSTART_SHIFT); + *buf++ = blit_cmd; + *buf++ = + (dst_x << PSB_2D_DST_XSTART_SHIFT) | (dst_y << + PSB_2D_DST_YSTART_SHIFT); + *buf++ = + (size_x << PSB_2D_DST_XSIZE_SHIFT) | (size_y << + PSB_2D_DST_YSIZE_SHIFT); + *buf++ = PSB_2D_FLUSH_BH; + + return psbfb_2d_submit(dev_priv, buffer, buf - buffer); +} + +static void psbfb_copyarea_accel(struct fb_info *info, + const struct fb_copyarea *a) +{ + struct psbfb_par *par = info->par; + struct psb_framebuffer *psbfb = par->psbfb; + struct drm_framebuffer *fb = &psbfb->base; + struct drm_psb_private *dev_priv = par->dev->dev_private; + uint32_t offset; + uint32_t stride; + uint32_t src_format; + uint32_t dst_format; + + if (!fb) + return; + + offset = psbfb->offset; + stride = fb->pitch; + + switch (fb->depth) { + case 8: + src_format = PSB_2D_SRC_332RGB; + dst_format = PSB_2D_DST_332RGB; + break; + case 15: + src_format = PSB_2D_SRC_555RGB; + dst_format = PSB_2D_DST_555RGB; + break; + case 16: + src_format = PSB_2D_SRC_565RGB; + dst_format = PSB_2D_DST_565RGB; + break; + case 24: + case 32: + /* this is wrong but since we don't do blending its okay */ + src_format = PSB_2D_SRC_8888ARGB; + dst_format = PSB_2D_DST_8888ARGB; + break; + default: + /* software fallback */ + cfb_copyarea(info, a); + return; + } + + psb_accel_2d_copy(dev_priv, + offset, stride, src_format, + offset, stride, dst_format, + a->sx, a->sy, a->dx, a->dy, a->width, a->height); +} + +static void psbfb_copyarea(struct fb_info *info, + const struct fb_copyarea *region) +{ + struct psbfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (unlikely(info->state != FBINFO_STATE_RUNNING)) + return; + + if (info->flags & FBINFO_HWACCEL_DISABLED) + return cfb_copyarea(info, region); + + if (psb_2d_trylock(dev_priv)) { + psb_check_power_state(dev, PSB_DEVICE_SGX); + psbfb_copyarea_accel(info, region); + psb_2d_unlock(dev_priv); + if (drm_psb_ospm && IS_MRST(dev)) + schedule_delayed_work(&dev_priv->scheduler.wq, 1); + } else + cfb_copyarea(info, region); +} + +void psbfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + if (unlikely(info->state != FBINFO_STATE_RUNNING)) + return; + + cfb_imageblit(info, image); +} + +static void psbfb_onoff(struct fb_info *info, int dpms_mode) +{ + struct psbfb_par *par = info->par; + struct drm_device *dev = par->dev; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + int i; + + /* + * For each CRTC in this fb, find all associated encoders + * and turn them off, then turn off the CRTC. + */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = + crtc->helper_private; + + for (i = 0; i < par->crtc_count; i++) + if (crtc->base.id == par->crtc_ids[i]) + break; + + if (i == par->crtc_count) + continue; + + if (dpms_mode == DRM_MODE_DPMS_ON) + crtc_funcs->dpms(crtc, dpms_mode); + + /* Found a CRTC on this fb, now find encoders */ + list_for_each_entry(encoder, + &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) { + struct drm_encoder_helper_funcs + *encoder_funcs; + encoder_funcs = encoder->helper_private; + encoder_funcs->dpms(encoder, dpms_mode); + } + } + + if (dpms_mode == DRM_MODE_DPMS_OFF) + crtc_funcs->dpms(crtc, dpms_mode); + } +} + +static int psbfb_blank(int blank_mode, struct fb_info *info) +{ + struct psbfb_par *par = info->par; + + par->dpms_state = blank_mode; + PSB_DEBUG_PM("psbfb_blank \n"); + switch (blank_mode) { + case FB_BLANK_UNBLANK: + psbfb_onoff(info, DRM_MODE_DPMS_ON); + break; + case FB_BLANK_NORMAL: + psbfb_onoff(info, DRM_MODE_DPMS_STANDBY); + break; + case FB_BLANK_HSYNC_SUSPEND: + psbfb_onoff(info, DRM_MODE_DPMS_STANDBY); + break; + case FB_BLANK_VSYNC_SUSPEND: + psbfb_onoff(info, DRM_MODE_DPMS_SUSPEND); + break; + case FB_BLANK_POWERDOWN: + psbfb_onoff(info, DRM_MODE_DPMS_OFF); + break; + } + + return 0; +} + + +static int psbfb_kms_off(struct drm_device *dev, int suspend) +{ + struct drm_framebuffer *fb = 0; + DRM_DEBUG("psbfb_kms_off_ioctl\n"); + + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + struct fb_info *info = fb->fbdev; + + if (suspend) + fb_set_suspend(info, 1); + } + mutex_unlock(&dev->mode_config.mutex); + + return 0; +} + +int psbfb_kms_off_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + int ret; + + if (drm_psb_no_fb) + return 0; + acquire_console_sem(); + ret = psbfb_kms_off(dev, 0); + release_console_sem(); + + return ret; +} + +static int psbfb_kms_on(struct drm_device *dev, int resume) +{ + struct drm_framebuffer *fb = 0; + + DRM_DEBUG("psbfb_kms_on_ioctl\n"); + + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(fb, &dev->mode_config.fb_list, head) { + struct fb_info *info = fb->fbdev; + + if (resume) + fb_set_suspend(info, 0); + + } + mutex_unlock(&dev->mode_config.mutex); + + return 0; +} + +int psbfb_kms_on_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + int ret; + + if (drm_psb_no_fb) + return 0; + acquire_console_sem(); + ret = psbfb_kms_on(dev, 0); + release_console_sem(); + drm_helper_disable_unused_functions(dev); + return ret; +} + +void psbfb_suspend(struct drm_device *dev) +{ + acquire_console_sem(); + psbfb_kms_off(dev, 1); + release_console_sem(); +} + +void psbfb_resume(struct drm_device *dev) +{ + acquire_console_sem(); + psbfb_kms_on(dev, 1); + release_console_sem(); + drm_helper_disable_unused_functions(dev); +} + +static int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct psbfb_par *par = info->par; + struct psb_framebuffer *psbfb = par->psbfb; + struct ttm_buffer_object *bo = psbfb->bo; + unsigned long size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + unsigned long offset = vma->vm_pgoff; + + if (vma->vm_pgoff != 0) + return -EINVAL; + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + if (offset + size > bo->num_pages) + return -EINVAL; + + mutex_lock(&bo->mutex); + if (!psbfb->addr_space) + psbfb->addr_space = vma->vm_file->f_mapping; + mutex_unlock(&bo->mutex); + + return ttm_fbdev_mmap(vma, bo); +} + +int psbfb_sync(struct fb_info *info) +{ + struct psbfb_par *par = info->par; + struct drm_psb_private *dev_priv = par->dev->dev_private; + + if (psb_2d_trylock(dev_priv)) { + if (dev_priv->graphics_state == PSB_PWR_STATE_D0i0) + psb_idle_2d(par->dev); + psb_2d_unlock(dev_priv); + } else + udelay(5); + + return 0; +} + +static struct fb_ops psbfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = psbfb_check_var, + .fb_set_par = psbfb_set_par, + .fb_setcolreg = psbfb_setcolreg, + .fb_fillrect = psbfb_fillrect, + .fb_copyarea = psbfb_copyarea, + .fb_imageblit = psbfb_imageblit, + .fb_mmap = psbfb_mmap, + .fb_sync = psbfb_sync, + .fb_blank = psbfb_blank, +}; + +static struct drm_mode_set panic_mode; + +int psbfb_panic(struct notifier_block *n, unsigned long ununsed, + void *panic_str) +{ + DRM_ERROR("panic occurred, switching back to text console\n"); + drm_crtc_helper_set_config(&panic_mode); + + return 0; +} +EXPORT_SYMBOL(psbfb_panic); + +static struct notifier_block paniced = { + .notifier_call = psbfb_panic, +}; + + +static struct drm_framebuffer *psb_framebuffer_create + (struct drm_device *dev, struct drm_mode_fb_cmd *r, + void *mm_private) +{ + struct psb_framebuffer *fb; + int ret; + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) + return NULL; + + ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); + + if (ret) + goto err; + + drm_helper_mode_fill_fb_struct(&fb->base, r); + + fb->bo = mm_private; + + return &fb->base; + +err: + kfree(fb); + return NULL; +} + +static struct drm_framebuffer *psb_user_framebuffer_create + (struct drm_device *dev, struct drm_file *filp, + struct drm_mode_fb_cmd *r) +{ + struct ttm_buffer_object *bo = NULL; + uint64_t size; + + bo = ttm_buffer_object_lookup(psb_fpriv(filp)->tfile, r->handle); + if (!bo) + return NULL; + + /* JB: TODO not drop, make smarter */ + size = ((uint64_t) bo->num_pages) << PAGE_SHIFT; + if (size < r->width * r->height * 4) + return NULL; + + /* JB: TODO not drop, refcount buffer */ + return psb_framebuffer_create(dev, r, bo); +} + +int psbfb_create(struct drm_device *dev, uint32_t fb_width, + uint32_t fb_height, uint32_t surface_width, + uint32_t surface_height, struct psb_framebuffer **psbfb_p) +{ + struct fb_info *info; + struct psbfb_par *par; + struct drm_framebuffer *fb; + struct psb_framebuffer *psbfb; + struct ttm_bo_kmap_obj tmp_kmap; + struct drm_mode_fb_cmd mode_cmd; + struct device *device = &dev->pdev->dev; + struct ttm_bo_device *bdev = &psb_priv(dev)->bdev; + int size, aligned_size, ret; + struct ttm_buffer_object *fbo = NULL; + bool is_iomem; + + mode_cmd.width = surface_width; /* crtc->desired_mode->hdisplay; */ + mode_cmd.height = surface_height; /* crtc->desired_mode->vdisplay; */ + + mode_cmd.bpp = 32; + mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 1) / 8); + mode_cmd.depth = 24; + + size = mode_cmd.pitch * mode_cmd.height; + aligned_size = ALIGN(size, PAGE_SIZE); + ret = ttm_buffer_object_create(bdev, + aligned_size, + ttm_bo_type_kernel, + TTM_PL_FLAG_TT | + TTM_PL_FLAG_VRAM | + TTM_PL_FLAG_NO_EVICT, + 0, 0, 0, NULL, &fbo); + + if (unlikely(ret != 0)) { + DRM_ERROR("failed to allocate framebuffer.\n"); + return -ENOMEM; + } + + mutex_lock(&dev->struct_mutex); + fb = psb_framebuffer_create(dev, &mode_cmd, fbo); + if (!fb) { + DRM_ERROR("failed to allocate fb.\n"); + ret = -ENOMEM; + goto out_err0; + } + psbfb = to_psb_fb(fb); + psbfb->bo = fbo; + + list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); + info = framebuffer_alloc(sizeof(struct psbfb_par), device); + if (!info) { + ret = -ENOMEM; + goto out_err1; + } + + par = info->par; + par->psbfb = psbfb; + + strcpy(info->fix.id, "psbfb"); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.type_aux = 0; + info->fix.xpanstep = 1; /* doing it in hw */ + info->fix.ypanstep = 1; /* doing it in hw */ + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_I830; + info->fix.type_aux = 0; + + info->flags = FBINFO_DEFAULT; + + info->fbops = &psbfb_ops; + + info->fix.line_length = fb->pitch; + info->fix.smem_start = + dev->mode_config.fb_base + psbfb->bo->offset; + info->fix.smem_len = size; + + info->flags = FBINFO_DEFAULT; + + ret = ttm_bo_kmap(psbfb->bo, 0, psbfb->bo->num_pages, &tmp_kmap); + if (ret) { + DRM_ERROR("error mapping fb: %d\n", ret); + goto out_err2; + } + + + info->screen_base = ttm_kmap_obj_virtual(&tmp_kmap, &is_iomem); + info->screen_size = size; + + if (is_iomem) + memset_io(info->screen_base, 0, size); + else + memset(info->screen_base, 0, size); + + info->pseudo_palette = fb->pseudo_palette; + info->var.xres_virtual = fb->width; + info->var.yres_virtual = fb->height; + info->var.bits_per_pixel = fb->bits_per_pixel; + info->var.xoffset = 0; + info->var.yoffset = 0; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + + info->var.xres = fb_width; + info->var.yres = fb_height; + + info->fix.mmio_start = pci_resource_start(dev->pdev, 0); + info->fix.mmio_len = pci_resource_len(dev->pdev, 0); + + 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("fb depth is %d\n", fb->depth); + DRM_DEBUG(" pitch is %d\n", fb->pitch); + fill_fb_bitfield(&info->var, fb->depth); + + fb->fbdev = info; + + par->dev = dev; + + /* To allow resizing without swapping buffers */ + printk(KERN_INFO"allocated %dx%d fb: 0x%08lx, bo %p\n", + psbfb->base.width, + psbfb->base.height, psbfb->bo->offset, psbfb->bo); + + if (psbfb_p) + *psbfb_p = psbfb; + + mutex_unlock(&dev->struct_mutex); + + return 0; +out_err2: + unregister_framebuffer(info); +out_err1: + fb->funcs->destroy(fb); +out_err0: + mutex_unlock(&dev->struct_mutex); + ttm_bo_unref(&fbo); + return ret; +} + +static int psbfb_multi_fb_probe_crtc(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_framebuffer *fb = crtc->fb; + struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); + struct drm_connector *connector; + struct fb_info *info; + struct psbfb_par *par; + struct drm_mode_set *modeset; + unsigned int width, height; + int new_fb = 0; + int ret, i, conn_count; + + if (!drm_helper_crtc_in_use(crtc)) + return 0; + + if (!crtc->desired_mode) + return 0; + + width = crtc->desired_mode->hdisplay; + height = crtc->desired_mode->vdisplay; + + /* is there an fb bound to this crtc already */ + if (!intel_crtc->mode_set.fb) { + ret = + psbfb_create(dev, width, height, width, height, + &psbfb); + if (ret) + return -EINVAL; + new_fb = 1; + } else { + fb = intel_crtc->mode_set.fb; + if ((fb->width < width) || (fb->height < height)) + return -EINVAL; + } + + info = fb->fbdev; + par = info->par; + + modeset = &intel_crtc->mode_set; + modeset->fb = fb; + conn_count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + if (connector->encoder) + if (connector->encoder->crtc == modeset->crtc) { + modeset->connectors[conn_count] = + connector; + conn_count++; + if (conn_count > INTELFB_CONN_LIMIT) + BUG(); + } + } + + for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) + modeset->connectors[i] = NULL; + + par->crtc_ids[0] = crtc->base.id; + + modeset->num_connectors = conn_count; + if (modeset->mode != modeset->crtc->desired_mode) + modeset->mode = modeset->crtc->desired_mode; + + par->crtc_count = 1; + + if (new_fb) { + info->var.pixclock = -1; + if (register_framebuffer(info) < 0) + return -EINVAL; + } else + psbfb_set_par(info); + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + /* Switch back to kernel console on panic */ + panic_mode = *modeset; + atomic_notifier_chain_register(&panic_notifier_list, &paniced); + printk(KERN_INFO "registered panic notifier\n"); + + return 0; +} + +static int psbfb_multi_fb_probe(struct drm_device *dev) +{ + + struct drm_crtc *crtc; + int ret = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + ret = psbfb_multi_fb_probe_crtc(dev, crtc); + if (ret) + return ret; + } + return ret; +} + +static int psbfb_single_fb_probe(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_connector *connector; + unsigned int fb_width = (unsigned) -1, fb_height = (unsigned) -1; + unsigned int surface_width = 0, surface_height = 0; + int new_fb = 0; + int crtc_count = 0; + int ret, i, conn_count = 0; + struct fb_info *info; + struct psbfb_par *par; + struct drm_mode_set *modeset = NULL; + struct drm_framebuffer *fb = NULL; + struct psb_framebuffer *psbfb = NULL; + + /* first up get a count of crtcs now in use and + * new min/maxes width/heights */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (drm_helper_crtc_in_use(crtc)) { + if (crtc->desired_mode) { + fb = crtc->fb; + if (crtc->desired_mode->hdisplay < + fb_width) + fb_width = + crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay < + fb_height) + fb_height = + crtc->desired_mode->vdisplay; + + if (crtc->desired_mode->hdisplay > + surface_width) + surface_width = + crtc->desired_mode->hdisplay; + + if (crtc->desired_mode->vdisplay > + surface_height) + surface_height = + crtc->desired_mode->vdisplay; + + } + crtc_count++; + } + } + + if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { + /* hmm everyone went away - assume VGA cable just fell out + and will come back later. */ + return 0; + } + + /* do we have an fb already? */ + if (list_empty(&dev->mode_config.fb_kernel_list)) { + /* create an fb if we don't have one */ + ret = + psbfb_create(dev, fb_width, fb_height, surface_width, + surface_height, &psbfb); + if (ret) + return -EINVAL; + new_fb = 1; + fb = &psbfb->base; + } else { + fb = list_first_entry(&dev->mode_config.fb_kernel_list, + struct drm_framebuffer, filp_head); + + /* if someone hotplugs something bigger than we have already + * allocated, we are pwned. As really we can't resize an + * fbdev that is in the wild currently due to fbdev not really + * being designed for the lower layers moving stuff around + * under it. - so in the grand style of things - punt. */ + if ((fb->width < surface_width) + || (fb->height < surface_height)) { + DRM_ERROR + ("Framebuffer not large enough to scale" + " console onto.\n"); + return -EINVAL; + } + } + + info = fb->fbdev; + par = info->par; + + crtc_count = 0; + /* okay we need to setup new connector sets in the crtcs */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + modeset = &intel_crtc->mode_set; + modeset->fb = fb; + conn_count = 0; + list_for_each_entry(connector, + &dev->mode_config.connector_list, + head) { + if (connector->encoder) + if (connector->encoder->crtc == + modeset->crtc) { + modeset->connectors[conn_count] = + connector; + conn_count++; + if (conn_count > + INTELFB_CONN_LIMIT) + BUG(); + } + } + + for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) + modeset->connectors[i] = NULL; + + par->crtc_ids[crtc_count++] = crtc->base.id; + + modeset->num_connectors = conn_count; + if (modeset->mode != modeset->crtc->desired_mode) + modeset->mode = modeset->crtc->desired_mode; + } + par->crtc_count = crtc_count; + + if (new_fb) { + info->var.pixclock = -1; + if (register_framebuffer(info) < 0) + return -EINVAL; + } else + psbfb_set_par(info); + + printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, + info->fix.id); + + /* Switch back to kernel console on panic */ + panic_mode = *modeset; + atomic_notifier_chain_register(&panic_notifier_list, &paniced); + printk(KERN_INFO "registered panic notifier\n"); + + return 0; +} + +int psbfb_probe(struct drm_device *dev) +{ + int ret = 0; + + DRM_DEBUG("\n"); + + /* something has changed in the lower levels of hell - deal with it + here */ + + /* two modes : a) 1 fb to rule all crtcs. + b) one fb per crtc. + two actions 1) new connected device + 2) device removed. + case a/1 : if the fb surface isn't big enough - + resize the surface fb. + if the fb size isn't big enough - resize fb into surface. + if everything big enough configure the new crtc/etc. + case a/2 : undo the configuration + possibly resize down the fb to fit the new configuration. + case b/1 : see if it is on a new crtc - setup a new fb and add it. + case b/2 : teardown the new fb. + */ + + /* mode a first */ + /* search for an fb */ + if (0 /*i915_fbpercrtc == 1 */) + ret = psbfb_multi_fb_probe(dev); + else + ret = psbfb_single_fb_probe(dev); + + return ret; +} +EXPORT_SYMBOL(psbfb_probe); + +int psbfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) +{ + struct fb_info *info; + struct psb_framebuffer *psbfb = to_psb_fb(fb); + + if (drm_psb_no_fb) + return 0; + + info = fb->fbdev; + + if (info) { + unregister_framebuffer(info); + ttm_bo_kunmap(&psbfb->kmap); + ttm_bo_unref(&psbfb->bo); + framebuffer_release(info); + } + + atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); + memset(&panic_mode, 0, sizeof(struct drm_mode_set)); + return 0; +} +EXPORT_SYMBOL(psbfb_remove); + +static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + /* JB: TODO currently we can't go from a bo to a handle with ttm */ + (void) file_priv; + *handle = 0; + return 0; +} + +static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct drm_device *dev = fb->dev; + if (fb->fbdev) + psbfb_remove(dev, fb); + + /* JB: TODO not drop, refcount buffer */ + drm_framebuffer_cleanup(fb); + + kfree(fb); +} + +static const struct drm_mode_config_funcs psb_mode_funcs = { + .fb_create = psb_user_framebuffer_create, + .fb_changed = psbfb_probe, +}; + +static void psb_setup_outputs(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + struct drm_connector *connector; + + if (IS_MRST(dev)) { + if (dev_priv->iLVDS_enable) + /* Set up integrated LVDS for MRST */ + mrst_lvds_init(dev, &dev_priv->mode_dev); + else { + /* Set up integrated MIPI for MRST */ + mrst_dsi_init(dev, &dev_priv->mode_dev); + } + } else { + intel_lvds_init(dev, &dev_priv->mode_dev); + /* intel_sdvo_init(dev, SDVOB); */ + } + + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + struct intel_output *intel_output = + to_intel_output(connector); + struct drm_encoder *encoder = &intel_output->enc; + int crtc_mask = 0, clone_mask = 0; + + /* valid crtcs */ + switch (intel_output->type) { + case INTEL_OUTPUT_SDVO: + crtc_mask = ((1 << 0) | (1 << 1)); + clone_mask = (1 << INTEL_OUTPUT_SDVO); + break; + case INTEL_OUTPUT_LVDS: + if (IS_MRST(dev)) + crtc_mask = (1 << 0); + else + crtc_mask = (1 << 1); + + clone_mask = (1 << INTEL_OUTPUT_LVDS); + break; + case INTEL_OUTPUT_MIPI: + crtc_mask = (1 << 0); + clone_mask = (1 << INTEL_OUTPUT_MIPI); + break; + } + encoder->possible_crtcs = crtc_mask; + encoder->possible_clones = + intel_connector_clones(dev, clone_mask); + } +} + +static void *psb_bo_from_handle(struct drm_device *dev, + struct drm_file *file_priv, + unsigned int handle) +{ + return ttm_buffer_object_lookup(psb_fpriv(file_priv)->tfile, + handle); +} + +static size_t psb_bo_size(struct drm_device *dev, void *bof) +{ + struct ttm_buffer_object *bo = bof; + return bo->num_pages << PAGE_SHIFT; +} + +static size_t psb_bo_offset(struct drm_device *dev, void *bof) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + struct ttm_buffer_object *bo = bof; + + size_t offset = bo->offset - dev_priv->pg->gatt_start; + DRM_DEBUG("Offset %u\n", offset); + return offset; +} + +static int psb_bo_pin_for_scanout(struct drm_device *dev, void *bo) +{ +#if 0 /* JB: Not used for the drop */ + struct ttm_buffer_object *bo = bof; + We should do things like check if + the buffer is in a scanout : able + place.And make sure that its pinned. +#endif + return 0; + } + + static int psb_bo_unpin_for_scanout(struct drm_device *dev, + void *bo) { +#if 0 /* JB: Not used for the drop */ + struct ttm_buffer_object *bo = bof; +#endif + return 0; + } + + void psb_modeset_init(struct drm_device *dev) + { + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + struct intel_mode_device *mode_dev = &dev_priv->mode_dev; + int i; + int num_pipe; + + /* Init mm functions */ + mode_dev->bo_from_handle = psb_bo_from_handle; + mode_dev->bo_size = psb_bo_size; + mode_dev->bo_offset = psb_bo_offset; + mode_dev->bo_pin_for_scanout = psb_bo_pin_for_scanout; + mode_dev->bo_unpin_for_scanout = psb_bo_unpin_for_scanout; + + drm_mode_config_init(dev); + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + dev->mode_config.funcs = (void *) &psb_mode_funcs; + + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + + /* set memory base */ + dev->mode_config.fb_base = + pci_resource_start(dev->pdev, 0); + + if (IS_MRST(dev)) + num_pipe = 1; + else + num_pipe = 2; + + + for (i = 0; i < num_pipe; i++) + intel_crtc_init(dev, i, mode_dev); + + psb_setup_outputs(dev); + + /* setup fbs */ + /* drm_initial_config(dev, false); */ + } + + void psb_modeset_cleanup(struct drm_device *dev) + { + drm_mode_config_cleanup(dev); + } |