aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915')
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c10
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h23
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c189
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c6
-rw-r--r--drivers/gpu/drm/i915/intel_display.c43
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c7
6 files changed, 263 insertions, 15 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 62a4bf7b49d..bbadf1c0414 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -177,6 +177,14 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
+ master_priv->sarea = drm_getsarea(dev);
+ if (master_priv->sarea) {
+ master_priv->sarea_priv = (drm_i915_sarea_t *)
+ ((u8 *)master_priv->sarea->handle + init->sarea_priv_offset);
+ } else {
+ DRM_DEBUG("sarea not found assuming DRI2 userspace\n");
+ }
+
if (init->ring_size != 0) {
if (dev_priv->ring.ring_obj != NULL) {
i915_dma_cleanup(dev);
@@ -1152,6 +1160,8 @@ int i915_driver_unload(struct drm_device *dev)
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
intel_modeset_cleanup(dev);
+ i915_gem_free_all_phys_object(dev);
+
mutex_lock(&dev->struct_mutex);
i915_gem_cleanup_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 563de18063f..e1351825200 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -72,6 +72,18 @@ enum pipe {
#define WATCH_INACTIVE 0
#define WATCH_PWRITE 0
+#define I915_GEM_PHYS_CURSOR_0 1
+#define I915_GEM_PHYS_CURSOR_1 2
+#define I915_GEM_PHYS_OVERLAY_REGS 3
+#define I915_MAX_PHYS_OBJECT (I915_GEM_PHYS_OVERLAY_REGS)
+
+struct drm_i915_gem_phys_object {
+ int id;
+ struct page **page_list;
+ drm_dma_handle_t *handle;
+ struct drm_gem_object *cur_obj;
+};
+
typedef struct _drm_i915_ring_buffer {
int tail_mask;
unsigned long Size;
@@ -358,6 +370,9 @@ typedef struct drm_i915_private {
uint32_t bit_6_swizzle_x;
/** Bit 6 swizzling required for Y tiling */
uint32_t bit_6_swizzle_y;
+
+ /* storage for physical objects */
+ struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT];
} mm;
} drm_i915_private_t;
@@ -436,6 +451,9 @@ struct drm_i915_gem_object {
/** User space pin count and filp owning the pin */
uint32_t user_pin_count;
struct drm_file *pin_filp;
+
+ /** for phy allocated objects */
+ struct drm_i915_gem_phys_object *phys_obj;
};
/**
@@ -598,6 +616,11 @@ int i915_gem_do_init(struct drm_device *dev, unsigned long start,
int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
int write);
+int i915_gem_attach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj, int id);
+void i915_gem_detach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj);
+void i915_gem_free_all_phys_object(struct drm_device *dev);
/* i915_gem_tiling.c */
void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 1384d668655..96316fd4723 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -55,6 +55,9 @@ static int i915_gem_object_bind_to_gtt(struct drm_gem_object *obj,
static void i915_gem_object_get_fence_reg(struct drm_gem_object *obj);
static void i915_gem_clear_fence_reg(struct drm_gem_object *obj);
static int i915_gem_evict_something(struct drm_device *dev);
+static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv);
int i915_gem_do_init(struct drm_device *dev, unsigned long start,
unsigned long end)
@@ -386,8 +389,10 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
* pread/pwrite currently are reading and writing from the CPU
* perspective, requiring manual detiling by the client.
*/
- if (obj_priv->tiling_mode == I915_TILING_NONE &&
- dev->gtt_total != 0)
+ if (obj_priv->phys_obj)
+ ret = i915_gem_phys_pwrite(dev, obj, args, file_priv);
+ else if (obj_priv->tiling_mode == I915_TILING_NONE &&
+ dev->gtt_total != 0)
ret = i915_gem_gtt_pwrite(dev, obj, args, file_priv);
else
ret = i915_gem_shmem_pwrite(dev, obj, args, file_priv);
@@ -2858,6 +2863,9 @@ void i915_gem_free_object(struct drm_gem_object *obj)
while (obj_priv->pin_count > 0)
i915_gem_object_unpin(obj);
+ if (obj_priv->phys_obj)
+ i915_gem_detach_phys_object(dev, obj);
+
i915_gem_object_unbind(obj);
list = &obj->map_list;
@@ -3293,3 +3301,180 @@ i915_gem_load(struct drm_device *dev)
i915_gem_detect_bit_6_swizzle(dev);
}
+
+/*
+ * Create a physically contiguous memory object for this object
+ * e.g. for cursor + overlay regs
+ */
+int i915_gem_init_phys_object(struct drm_device *dev,
+ int id, int size)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_phys_object *phys_obj;
+ int ret;
+
+ if (dev_priv->mm.phys_objs[id - 1] || !size)
+ return 0;
+
+ phys_obj = drm_calloc(1, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER);
+ if (!phys_obj)
+ return -ENOMEM;
+
+ phys_obj->id = id;
+
+ phys_obj->handle = drm_pci_alloc(dev, size, 0, 0xffffffff);
+ if (!phys_obj->handle) {
+ ret = -ENOMEM;
+ goto kfree_obj;
+ }
+#ifdef CONFIG_X86
+ set_memory_wc((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE);
+#endif
+
+ dev_priv->mm.phys_objs[id - 1] = phys_obj;
+
+ return 0;
+kfree_obj:
+ drm_free(phys_obj, sizeof(struct drm_i915_gem_phys_object), DRM_MEM_DRIVER);
+ return ret;
+}
+
+void i915_gem_free_phys_object(struct drm_device *dev, int id)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_phys_object *phys_obj;
+
+ if (!dev_priv->mm.phys_objs[id - 1])
+ return;
+
+ phys_obj = dev_priv->mm.phys_objs[id - 1];
+ if (phys_obj->cur_obj) {
+ i915_gem_detach_phys_object(dev, phys_obj->cur_obj);
+ }
+
+#ifdef CONFIG_X86
+ set_memory_wb((unsigned long)phys_obj->handle->vaddr, phys_obj->handle->size / PAGE_SIZE);
+#endif
+ drm_pci_free(dev, phys_obj->handle);
+ kfree(phys_obj);
+ dev_priv->mm.phys_objs[id - 1] = NULL;
+}
+
+void i915_gem_free_all_phys_object(struct drm_device *dev)
+{
+ int i;
+
+ for (i = 0; i < I915_MAX_PHYS_OBJECT; i++)
+ i915_gem_free_phys_object(dev, i);
+}
+
+void i915_gem_detach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj)
+{
+ struct drm_i915_gem_object *obj_priv;
+ int i;
+ int ret;
+ int page_count;
+
+ obj_priv = obj->driver_private;
+ if (!obj_priv->phys_obj)
+ return;
+
+ ret = i915_gem_object_get_page_list(obj);
+ if (ret)
+ goto out;
+
+ page_count = obj->size / PAGE_SIZE;
+
+ for (i = 0; i < page_count; i++) {
+ char *dst = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+ char *src = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
+
+ memcpy(dst, src, PAGE_SIZE);
+ kunmap_atomic(dst, KM_USER0);
+ }
+ drm_clflush_pages(obj_priv->page_list, page_count);
+ drm_agp_chipset_flush(dev);
+out:
+ obj_priv->phys_obj->cur_obj = NULL;
+ obj_priv->phys_obj = NULL;
+}
+
+int
+i915_gem_attach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj, int id)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct drm_i915_gem_object *obj_priv;
+ int ret = 0;
+ int page_count;
+ int i;
+
+ if (id > I915_MAX_PHYS_OBJECT)
+ return -EINVAL;
+
+ obj_priv = obj->driver_private;
+
+ if (obj_priv->phys_obj) {
+ if (obj_priv->phys_obj->id == id)
+ return 0;
+ i915_gem_detach_phys_object(dev, obj);
+ }
+
+
+ /* create a new object */
+ if (!dev_priv->mm.phys_objs[id - 1]) {
+ ret = i915_gem_init_phys_object(dev, id,
+ obj->size);
+ if (ret) {
+ DRM_ERROR("failed to init phys object %d size: %d\n", id, obj->size);
+ goto out;
+ }
+ }
+
+ /* bind to the object */
+ obj_priv->phys_obj = dev_priv->mm.phys_objs[id - 1];
+ obj_priv->phys_obj->cur_obj = obj;
+
+ ret = i915_gem_object_get_page_list(obj);
+ if (ret) {
+ DRM_ERROR("failed to get page list\n");
+ goto out;
+ }
+
+ page_count = obj->size / PAGE_SIZE;
+
+ for (i = 0; i < page_count; i++) {
+ char *src = kmap_atomic(obj_priv->page_list[i], KM_USER0);
+ char *dst = obj_priv->phys_obj->handle->vaddr + (i * PAGE_SIZE);
+
+ memcpy(dst, src, PAGE_SIZE);
+ kunmap_atomic(src, KM_USER0);
+ }
+
+ return 0;
+out:
+ return ret;
+}
+
+static int
+i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
+ struct drm_i915_gem_pwrite *args,
+ struct drm_file *file_priv)
+{
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ void *obj_addr;
+ int ret;
+ char __user *user_data;
+
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
+ obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
+
+ DRM_ERROR("obj_addr %p, %lld\n", obj_addr, args->size);
+ ret = copy_from_user(obj_addr, user_data, args->size);
+ if (ret)
+ return -EFAULT;
+
+ drm_agp_chipset_flush(dev);
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 0cadafbef41..6290219de6c 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -411,6 +411,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags;
+ int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+ u32 pipeconf;
+
+ pipeconf = I915_READ(pipeconf_reg);
+ if (!(pipeconf & PIPEACONF_ENABLE))
+ return -EINVAL;
spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
if (IS_I965G(dev))
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 8ccb9c3ab86..31c3732b7a6 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -401,6 +401,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
I915_WRITE(dspstride, crtc->fb->pitch);
dspcntr = I915_READ(dspcntr_reg);
+ /* Mask out pixel format bits in case we change it */
+ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
switch (crtc->fb->bits_per_pixel) {
case 8:
dspcntr |= DISPPLANE_8BPP;
@@ -1014,21 +1016,25 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
if (bo->size < width * height * 4) {
DRM_ERROR("buffer is to small\n");
- drm_gem_object_unreference(bo);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail;
}
- if (dev_priv->cursor_needs_physical) {
- addr = dev->agp->base + obj_priv->gtt_offset;
- } else {
+ /* we only need to pin inside GTT if cursor is non-phy */
+ if (!dev_priv->cursor_needs_physical) {
+ ret = i915_gem_object_pin(bo, PAGE_SIZE);
+ if (ret) {
+ DRM_ERROR("failed to pin cursor bo\n");
+ goto fail;
+ }
addr = obj_priv->gtt_offset;
- }
-
- ret = i915_gem_object_pin(bo, PAGE_SIZE);
- if (ret) {
- DRM_ERROR("failed to pin cursor bo\n");
- drm_gem_object_unreference(bo);
- return ret;
+ } else {
+ ret = i915_gem_attach_phys_object(dev, bo, (pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1);
+ if (ret) {
+ DRM_ERROR("failed to attach phys object\n");
+ goto fail;
+ }
+ addr = obj_priv->phys_obj->handle->busaddr;
}
temp = 0;
@@ -1041,14 +1047,25 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
I915_WRITE(base, addr);
if (intel_crtc->cursor_bo) {
- i915_gem_object_unpin(intel_crtc->cursor_bo);
+ if (dev_priv->cursor_needs_physical) {
+ if (intel_crtc->cursor_bo != bo)
+ i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
+ } else
+ i915_gem_object_unpin(intel_crtc->cursor_bo);
+ mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(intel_crtc->cursor_bo);
+ mutex_unlock(&dev->struct_mutex);
}
intel_crtc->cursor_addr = addr;
intel_crtc->cursor_bo = bo;
return 0;
+fail:
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(bo);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
}
static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index ccecfaf6307..2fafdcc108f 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -456,6 +456,13 @@ void intel_lvds_init(struct drm_device *dev)
dev_priv->panel_fixed_mode =
drm_mode_duplicate(dev, dev_priv->vbt_mode);
mutex_unlock(&dev->mode_config.mutex);
+ if (dev_priv->panel_fixed_mode) {
+ dev_priv->panel_fixed_mode->type |=
+ DRM_MODE_TYPE_PREFERRED;
+ drm_mode_probed_add(connector,
+ dev_priv->panel_fixed_mode);
+ goto out;
+ }
}
/*