Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt/drm-intel

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/anholt/drm-intel:
  drm/i915: hold ref on flip object until it completes
  drm/i915: Fix crash while aborting hibernation
  drm/i915: Correctly return -ENOMEM on allocation failure in cmdbuf ioctls.
  drm/i915: fix pipe source image setting in flip command
  drm/i915: fix flip done interrupt on Ironlake
  drm/i915: untangle page flip completion
  drm/i915: handle FBC and self-refresh better
  drm/i915: Increase fb alignment to 64k
  drm/i915: Update write_domains on active list after flush.
  drm/i915: Rework DPLL calculation parameters for Ironlake
This commit is contained in:
Linus Torvalds 2010-02-12 09:32:10 -08:00
commit 26b23ace8b
8 changed files with 339 additions and 149 deletions

View file

@ -735,8 +735,10 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
if (cmdbuf->num_cliprects) {
cliprects = kcalloc(cmdbuf->num_cliprects,
sizeof(struct drm_clip_rect), GFP_KERNEL);
if (cliprects == NULL)
if (cliprects == NULL) {
ret = -ENOMEM;
goto fail_batch_free;
}
ret = copy_from_user(cliprects, cmdbuf->cliprects,
cmdbuf->num_cliprects *

View file

@ -174,12 +174,42 @@ const static struct pci_device_id pciidlist[] = {
MODULE_DEVICE_TABLE(pci, pciidlist);
#endif
static int i915_suspend(struct drm_device *dev, pm_message_t state)
static int i915_drm_freeze(struct drm_device *dev)
{
pci_save_state(dev->pdev);
/* If KMS is active, we do the leavevt stuff here */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
int error = i915_gem_idle(dev);
if (error) {
dev_err(&dev->pdev->dev,
"GEM idle failed, resume might fail\n");
return error;
}
drm_irq_uninstall(dev);
}
i915_save_state(dev);
return 0;
}
static void i915_drm_suspend(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (!dev || !dev_priv) {
DRM_ERROR("dev: %p, dev_priv: %p\n", dev, dev_priv);
intel_opregion_free(dev, 1);
/* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0;
}
static int i915_suspend(struct drm_device *dev, pm_message_t state)
{
int error;
if (!dev || !dev->dev_private) {
DRM_ERROR("dev: %p\n", dev);
DRM_ERROR("DRM not initialized, aborting suspend.\n");
return -ENODEV;
}
@ -187,19 +217,11 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
if (state.event == PM_EVENT_PRETHAW)
return 0;
pci_save_state(dev->pdev);
error = i915_drm_freeze(dev);
if (error)
return error;
/* If KMS is active, we do the leavevt stuff here */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
if (i915_gem_idle(dev))
dev_err(&dev->pdev->dev,
"GEM idle failed, resume may fail\n");
drm_irq_uninstall(dev);
}
i915_save_state(dev);
intel_opregion_free(dev, 1);
i915_drm_suspend(dev);
if (state.event == PM_EVENT_SUSPEND) {
/* Shut down the device */
@ -207,45 +229,45 @@ static int i915_suspend(struct drm_device *dev, pm_message_t state)
pci_set_power_state(dev->pdev, PCI_D3hot);
}
/* Modeset on resume, not lid events */
dev_priv->modeset_on_lid = 0;
return 0;
}
static int i915_resume(struct drm_device *dev)
static int i915_drm_thaw(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int ret = 0;
if (pci_enable_device(dev->pdev))
return -1;
pci_set_master(dev->pdev);
i915_restore_state(dev);
intel_opregion_init(dev, 1);
int error = 0;
/* KMS EnterVT equivalent */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
mutex_lock(&dev->struct_mutex);
dev_priv->mm.suspended = 0;
ret = i915_gem_init_ringbuffer(dev);
if (ret != 0)
ret = -1;
error = i915_gem_init_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
drm_irq_install(dev);
}
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
/* Resume the modeset for every activated CRTC */
drm_helper_resume_force_mode(dev);
}
dev_priv->modeset_on_lid = 0;
return ret;
return error;
}
static int i915_resume(struct drm_device *dev)
{
if (pci_enable_device(dev->pdev))
return -EIO;
pci_set_master(dev->pdev);
i915_restore_state(dev);
intel_opregion_init(dev, 1);
return i915_drm_thaw(dev);
}
/**
@ -386,57 +408,69 @@ i915_pci_remove(struct pci_dev *pdev)
drm_put_dev(dev);
}
static int
i915_pci_suspend(struct pci_dev *pdev, pm_message_t state)
static int i915_pm_suspend(struct device *dev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
int error;
return i915_suspend(dev, state);
}
if (!drm_dev || !drm_dev->dev_private) {
dev_err(dev, "DRM not initialized, aborting suspend.\n");
return -ENODEV;
}
static int
i915_pci_resume(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
error = i915_drm_freeze(drm_dev);
if (error)
return error;
return i915_resume(dev);
}
i915_drm_suspend(drm_dev);
static int
i915_pm_suspend(struct device *dev)
{
return i915_pci_suspend(to_pci_dev(dev), PMSG_SUSPEND);
}
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
static int
i915_pm_resume(struct device *dev)
{
return i915_pci_resume(to_pci_dev(dev));
}
static int
i915_pm_freeze(struct device *dev)
{
return i915_pci_suspend(to_pci_dev(dev), PMSG_FREEZE);
}
static int
i915_pm_thaw(struct device *dev)
{
/* thaw during hibernate, do nothing! */
return 0;
}
static int
i915_pm_poweroff(struct device *dev)
static int i915_pm_resume(struct device *dev)
{
return i915_pci_suspend(to_pci_dev(dev), PMSG_HIBERNATE);
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
return i915_resume(drm_dev);
}
static int
i915_pm_restore(struct device *dev)
static int i915_pm_freeze(struct device *dev)
{
return i915_pci_resume(to_pci_dev(dev));
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
if (!drm_dev || !drm_dev->dev_private) {
dev_err(dev, "DRM not initialized, aborting suspend.\n");
return -ENODEV;
}
return i915_drm_freeze(drm_dev);
}
static int i915_pm_thaw(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
return i915_drm_thaw(drm_dev);
}
static int i915_pm_poweroff(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
int error;
error = i915_drm_freeze(drm_dev);
if (!error)
i915_drm_suspend(drm_dev);
return error;
}
const struct dev_pm_ops i915_pm_ops = {
@ -445,7 +479,7 @@ const struct dev_pm_ops i915_pm_ops = {
.freeze = i915_pm_freeze,
.thaw = i915_pm_thaw,
.poweroff = i915_pm_poweroff,
.restore = i915_pm_restore,
.restore = i915_pm_resume,
};
static struct vm_operations_struct i915_gem_vm_ops = {

View file

@ -492,6 +492,15 @@ typedef struct drm_i915_private {
*/
struct list_head flushing_list;
/**
* List of objects currently pending a GPU write flush.
*
* All elements on this list will belong to either the
* active_list or flushing_list, last_rendering_seqno can
* be used to differentiate between the two elements.
*/
struct list_head gpu_write_list;
/**
* LRU list of objects which are not in the ringbuffer and
* are ready to unbind, but are still in the GTT.
@ -592,6 +601,8 @@ struct drm_i915_gem_object {
/** This object's place on the active/flushing/inactive lists */
struct list_head list;
/** This object's place on GPU write list */
struct list_head gpu_write_list;
/** This object's place on the fenced object LRU */
struct list_head fence_list;

View file

@ -1552,6 +1552,8 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
else
list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
BUG_ON(!list_empty(&obj_priv->gpu_write_list));
obj_priv->last_rendering_seqno = 0;
if (obj_priv->active) {
obj_priv->active = 0;
@ -1622,7 +1624,8 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
struct drm_i915_gem_object *obj_priv, *next;
list_for_each_entry_safe(obj_priv, next,
&dev_priv->mm.flushing_list, list) {
&dev_priv->mm.gpu_write_list,
gpu_write_list) {
struct drm_gem_object *obj = obj_priv->obj;
if ((obj->write_domain & flush_domains) ==
@ -1630,6 +1633,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
uint32_t old_write_domain = obj->write_domain;
obj->write_domain = 0;
list_del_init(&obj_priv->gpu_write_list);
i915_gem_object_move_to_active(obj, seqno);
trace_i915_gem_object_change_domain(obj,
@ -2084,8 +2088,8 @@ static int
i915_gem_evict_everything(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t seqno;
int ret;
uint32_t seqno;
bool lists_empty;
spin_lock(&dev_priv->mm.active_list_lock);
@ -2107,6 +2111,8 @@ i915_gem_evict_everything(struct drm_device *dev)
if (ret)
return ret;
BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
ret = i915_gem_evict_from_inactive_list(dev);
if (ret)
return ret;
@ -2701,7 +2707,7 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
old_write_domain = obj->write_domain;
i915_gem_flush(dev, 0, obj->write_domain);
seqno = i915_add_request(dev, NULL, obj->write_domain);
obj->write_domain = 0;
BUG_ON(obj->write_domain);
i915_gem_object_move_to_active(obj, seqno);
trace_i915_gem_object_change_domain(obj,
@ -3682,8 +3688,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
if (args->num_cliprects != 0) {
cliprects = kcalloc(args->num_cliprects, sizeof(*cliprects),
GFP_KERNEL);
if (cliprects == NULL)
if (cliprects == NULL) {
ret = -ENOMEM;
goto pre_mutex_err;
}
ret = copy_from_user(cliprects,
(struct drm_clip_rect __user *)
@ -3850,16 +3858,23 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
i915_gem_flush(dev,
dev->invalidate_domains,
dev->flush_domains);
if (dev->flush_domains)
if (dev->flush_domains & I915_GEM_GPU_DOMAINS)
(void)i915_add_request(dev, file_priv,
dev->flush_domains);
}
for (i = 0; i < args->buffer_count; i++) {
struct drm_gem_object *obj = object_list[i];
struct drm_i915_gem_object *obj_priv = obj->driver_private;
uint32_t old_write_domain = obj->write_domain;
obj->write_domain = obj->pending_write_domain;
if (obj->write_domain)
list_move_tail(&obj_priv->gpu_write_list,
&dev_priv->mm.gpu_write_list);
else
list_del_init(&obj_priv->gpu_write_list);
trace_i915_gem_object_change_domain(obj,
obj->read_domains,
old_write_domain);
@ -4370,6 +4385,7 @@ int i915_gem_init_object(struct drm_gem_object *obj)
obj_priv->obj = obj;
obj_priv->fence_reg = I915_FENCE_REG_NONE;
INIT_LIST_HEAD(&obj_priv->list);
INIT_LIST_HEAD(&obj_priv->gpu_write_list);
INIT_LIST_HEAD(&obj_priv->fence_list);
obj_priv->madv = I915_MADV_WILLNEED;
@ -4821,6 +4837,7 @@ i915_gem_load(struct drm_device *dev)
spin_lock_init(&dev_priv->mm.active_list_lock);
INIT_LIST_HEAD(&dev_priv->mm.active_list);
INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list);
INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
INIT_LIST_HEAD(&dev_priv->mm.request_list);
INIT_LIST_HEAD(&dev_priv->mm.fence_list);

View file

@ -309,22 +309,22 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
if (de_iir & DE_GSE)
ironlake_opregion_gse_intr(dev);
if (de_iir & DE_PLANEA_FLIP_DONE)
if (de_iir & DE_PLANEA_FLIP_DONE) {
intel_prepare_page_flip(dev, 0);
if (de_iir & DE_PLANEB_FLIP_DONE)
intel_prepare_page_flip(dev, 1);
if (de_iir & DE_PIPEA_VBLANK) {
drm_handle_vblank(dev, 0);
intel_finish_page_flip(dev, 0);
}
if (de_iir & DE_PIPEB_VBLANK) {
drm_handle_vblank(dev, 1);
if (de_iir & DE_PLANEB_FLIP_DONE) {
intel_prepare_page_flip(dev, 1);
intel_finish_page_flip(dev, 1);
}
if (de_iir & DE_PIPEA_VBLANK)
drm_handle_vblank(dev, 0);
if (de_iir & DE_PIPEB_VBLANK)
drm_handle_vblank(dev, 1);
/* check event from PCH */
if ((de_iir & DE_PCH_EVENT) &&
(pch_iir & SDE_HOTPLUG_MASK)) {

View file

@ -338,6 +338,7 @@
#define FBC_CTL_PERIODIC (1<<30)
#define FBC_CTL_INTERVAL_SHIFT (16)
#define FBC_CTL_UNCOMPRESSIBLE (1<<14)
#define FBC_C3_IDLE (1<<13)
#define FBC_CTL_STRIDE_SHIFT (5)
#define FBC_CTL_FENCENO (1<<0)
#define FBC_COMMAND 0x0320c

View file

@ -240,33 +240,86 @@ struct intel_limit {
#define IRONLAKE_DOT_MAX 350000
#define IRONLAKE_VCO_MIN 1760000
#define IRONLAKE_VCO_MAX 3510000
#define IRONLAKE_N_MIN 1
#define IRONLAKE_N_MAX 6
#define IRONLAKE_M_MIN 79
#define IRONLAKE_M_MAX 127
#define IRONLAKE_M1_MIN 12
#define IRONLAKE_M1_MAX 22
#define IRONLAKE_M2_MIN 5
#define IRONLAKE_M2_MAX 9
#define IRONLAKE_P_SDVO_DAC_MIN 5
#define IRONLAKE_P_SDVO_DAC_MAX 80
#define IRONLAKE_P_LVDS_MIN 28
#define IRONLAKE_P_LVDS_MAX 112
#define IRONLAKE_P1_MIN 1
#define IRONLAKE_P1_MAX 8
#define IRONLAKE_P2_SDVO_DAC_SLOW 10
#define IRONLAKE_P2_SDVO_DAC_FAST 5
#define IRONLAKE_P2_LVDS_SLOW 14 /* single channel */
#define IRONLAKE_P2_LVDS_FAST 7 /* double channel */
#define IRONLAKE_P2_DOT_LIMIT 225000 /* 225Mhz */
#define IRONLAKE_P_DISPLAY_PORT_MIN 10
#define IRONLAKE_P_DISPLAY_PORT_MAX 20
#define IRONLAKE_P2_DISPLAY_PORT_FAST 10
#define IRONLAKE_P2_DISPLAY_PORT_SLOW 10
#define IRONLAKE_P2_DISPLAY_PORT_LIMIT 0
#define IRONLAKE_P1_DISPLAY_PORT_MIN 1
#define IRONLAKE_P1_DISPLAY_PORT_MAX 2
/* We have parameter ranges for different type of outputs. */
/* DAC & HDMI Refclk 120Mhz */
#define IRONLAKE_DAC_N_MIN 1
#define IRONLAKE_DAC_N_MAX 5
#define IRONLAKE_DAC_M_MIN 79
#define IRONLAKE_DAC_M_MAX 127
#define IRONLAKE_DAC_P_MIN 5
#define IRONLAKE_DAC_P_MAX 80
#define IRONLAKE_DAC_P1_MIN 1
#define IRONLAKE_DAC_P1_MAX 8
#define IRONLAKE_DAC_P2_SLOW 10
#define IRONLAKE_DAC_P2_FAST 5
/* LVDS single-channel 120Mhz refclk */
#define IRONLAKE_LVDS_S_N_MIN 1
#define IRONLAKE_LVDS_S_N_MAX 3
#define IRONLAKE_LVDS_S_M_MIN 79
#define IRONLAKE_LVDS_S_M_MAX 118
#define IRONLAKE_LVDS_S_P_MIN 28
#define IRONLAKE_LVDS_S_P_MAX 112
#define IRONLAKE_LVDS_S_P1_MIN 2
#define IRONLAKE_LVDS_S_P1_MAX 8
#define IRONLAKE_LVDS_S_P2_SLOW 14
#define IRONLAKE_LVDS_S_P2_FAST 14
/* LVDS dual-channel 120Mhz refclk */
#define IRONLAKE_LVDS_D_N_MIN 1
#define IRONLAKE_LVDS_D_N_MAX 3
#define IRONLAKE_LVDS_D_M_MIN 79
#define IRONLAKE_LVDS_D_M_MAX 127
#define IRONLAKE_LVDS_D_P_MIN 14
#define IRONLAKE_LVDS_D_P_MAX 56
#define IRONLAKE_LVDS_D_P1_MIN 2
#define IRONLAKE_LVDS_D_P1_MAX 8
#define IRONLAKE_LVDS_D_P2_SLOW 7
#define IRONLAKE_LVDS_D_P2_FAST 7
/* LVDS single-channel 100Mhz refclk */
#define IRONLAKE_LVDS_S_SSC_N_MIN 1
#define IRONLAKE_LVDS_S_SSC_N_MAX 2
#define IRONLAKE_LVDS_S_SSC_M_MIN 79
#define IRONLAKE_LVDS_S_SSC_M_MAX 126
#define IRONLAKE_LVDS_S_SSC_P_MIN 28
#define IRONLAKE_LVDS_S_SSC_P_MAX 112
#define IRONLAKE_LVDS_S_SSC_P1_MIN 2
#define IRONLAKE_LVDS_S_SSC_P1_MAX 8
#define IRONLAKE_LVDS_S_SSC_P2_SLOW 14
#define IRONLAKE_LVDS_S_SSC_P2_FAST 14
/* LVDS dual-channel 100Mhz refclk */
#define IRONLAKE_LVDS_D_SSC_N_MIN 1
#define IRONLAKE_LVDS_D_SSC_N_MAX 3
#define IRONLAKE_LVDS_D_SSC_M_MIN 79
#define IRONLAKE_LVDS_D_SSC_M_MAX 126
#define IRONLAKE_LVDS_D_SSC_P_MIN 14
#define IRONLAKE_LVDS_D_SSC_P_MAX 42
#define IRONLAKE_LVDS_D_SSC_P1_MIN 2
#define IRONLAKE_LVDS_D_SSC_P1_MAX 6
#define IRONLAKE_LVDS_D_SSC_P2_SLOW 7
#define IRONLAKE_LVDS_D_SSC_P2_FAST 7
/* DisplayPort */
#define IRONLAKE_DP_N_MIN 1
#define IRONLAKE_DP_N_MAX 2
#define IRONLAKE_DP_M_MIN 81
#define IRONLAKE_DP_M_MAX 90
#define IRONLAKE_DP_P_MIN 10
#define IRONLAKE_DP_P_MAX 20
#define IRONLAKE_DP_P2_FAST 10
#define IRONLAKE_DP_P2_SLOW 10
#define IRONLAKE_DP_P2_LIMIT 0
#define IRONLAKE_DP_P1_MIN 1
#define IRONLAKE_DP_P1_MAX 2
static bool
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
@ -474,33 +527,78 @@ static const intel_limit_t intel_limits_pineview_lvds = {
.find_pll = intel_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_sdvo = {
static const intel_limit_t intel_limits_ironlake_dac = {
.dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
.vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
.n = { .min = IRONLAKE_N_MIN, .max = IRONLAKE_N_MAX },
.m = { .min = IRONLAKE_M_MIN, .max = IRONLAKE_M_MAX },
.n = { .min = IRONLAKE_DAC_N_MIN, .max = IRONLAKE_DAC_N_MAX },
.m = { .min = IRONLAKE_DAC_M_MIN, .max = IRONLAKE_DAC_M_MAX },
.m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
.m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
.p = { .min = IRONLAKE_P_SDVO_DAC_MIN, .max = IRONLAKE_P_SDVO_DAC_MAX },
.p1 = { .min = IRONLAKE_P1_MIN, .max = IRONLAKE_P1_MAX },
.p = { .min = IRONLAKE_DAC_P_MIN, .max = IRONLAKE_DAC_P_MAX },
.p1 = { .min = IRONLAKE_DAC_P1_MIN, .max = IRONLAKE_DAC_P1_MAX },
.p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
.p2_slow = IRONLAKE_P2_SDVO_DAC_SLOW,
.p2_fast = IRONLAKE_P2_SDVO_DAC_FAST },
.p2_slow = IRONLAKE_DAC_P2_SLOW,
.p2_fast = IRONLAKE_DAC_P2_FAST },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_lvds = {
static const intel_limit_t intel_limits_ironlake_single_lvds = {
.dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
.vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
.n = { .min = IRONLAKE_N_MIN, .max = IRONLAKE_N_MAX },
.m = { .min = IRONLAKE_M_MIN, .max = IRONLAKE_M_MAX },
.n = { .min = IRONLAKE_LVDS_S_N_MIN, .max = IRONLAKE_LVDS_S_N_MAX },
.m = { .min = IRONLAKE_LVDS_S_M_MIN, .max = IRONLAKE_LVDS_S_M_MAX },
.m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
.m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
.p = { .min = IRONLAKE_P_LVDS_MIN, .max = IRONLAKE_P_LVDS_MAX },
.p1 = { .min = IRONLAKE_P1_MIN, .max = IRONLAKE_P1_MAX },
.p = { .min = IRONLAKE_LVDS_S_P_MIN, .max = IRONLAKE_LVDS_S_P_MAX },
.p1 = { .min = IRONLAKE_LVDS_S_P1_MIN, .max = IRONLAKE_LVDS_S_P1_MAX },
.p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
.p2_slow = IRONLAKE_P2_LVDS_SLOW,
.p2_fast = IRONLAKE_P2_LVDS_FAST },
.p2_slow = IRONLAKE_LVDS_S_P2_SLOW,
.p2_fast = IRONLAKE_LVDS_S_P2_FAST },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_dual_lvds = {
.dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
.vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
.n = { .min = IRONLAKE_LVDS_D_N_MIN, .max = IRONLAKE_LVDS_D_N_MAX },
.m = { .min = IRONLAKE_LVDS_D_M_MIN, .max = IRONLAKE_LVDS_D_M_MAX },
.m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
.m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
.p = { .min = IRONLAKE_LVDS_D_P_MIN, .max = IRONLAKE_LVDS_D_P_MAX },
.p1 = { .min = IRONLAKE_LVDS_D_P1_MIN, .max = IRONLAKE_LVDS_D_P1_MAX },
.p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
.p2_slow = IRONLAKE_LVDS_D_P2_SLOW,
.p2_fast = IRONLAKE_LVDS_D_P2_FAST },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
.dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
.vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
.n = { .min = IRONLAKE_LVDS_S_SSC_N_MIN, .max = IRONLAKE_LVDS_S_SSC_N_MAX },
.m = { .min = IRONLAKE_LVDS_S_SSC_M_MIN, .max = IRONLAKE_LVDS_S_SSC_M_MAX },
.m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
.m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
.p = { .min = IRONLAKE_LVDS_S_SSC_P_MIN, .max = IRONLAKE_LVDS_S_SSC_P_MAX },
.p1 = { .min = IRONLAKE_LVDS_S_SSC_P1_MIN,.max = IRONLAKE_LVDS_S_SSC_P1_MAX },
.p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
.p2_slow = IRONLAKE_LVDS_S_SSC_P2_SLOW,
.p2_fast = IRONLAKE_LVDS_S_SSC_P2_FAST },
.find_pll = intel_g4x_find_best_PLL,
};
static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
.dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
.vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
.n = { .min = IRONLAKE_LVDS_D_SSC_N_MIN, .max = IRONLAKE_LVDS_D_SSC_N_MAX },
.m = { .min = IRONLAKE_LVDS_D_SSC_M_MIN, .max = IRONLAKE_LVDS_D_SSC_M_MAX },
.m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
.m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
.p = { .min = IRONLAKE_LVDS_D_SSC_P_MIN, .max = IRONLAKE_LVDS_D_SSC_P_MAX },
.p1 = { .min = IRONLAKE_LVDS_D_SSC_P1_MIN,.max = IRONLAKE_LVDS_D_SSC_P1_MAX },
.p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
.p2_slow = IRONLAKE_LVDS_D_SSC_P2_SLOW,
.p2_fast = IRONLAKE_LVDS_D_SSC_P2_FAST },
.find_pll = intel_g4x_find_best_PLL,
};
@ -509,34 +607,53 @@ static const intel_limit_t intel_limits_ironlake_display_port = {
.max = IRONLAKE_DOT_MAX },
.vco = { .min = IRONLAKE_VCO_MIN,
.max = IRONLAKE_VCO_MAX},
.n = { .min = IRONLAKE_N_MIN,
.max = IRONLAKE_N_MAX },
.m = { .min = IRONLAKE_M_MIN,
.max = IRONLAKE_M_MAX },
.n = { .min = IRONLAKE_DP_N_MIN,
.max = IRONLAKE_DP_N_MAX },
.m = { .min = IRONLAKE_DP_M_MIN,
.max = IRONLAKE_DP_M_MAX },
.m1 = { .min = IRONLAKE_M1_MIN,
.max = IRONLAKE_M1_MAX },
.m2 = { .min = IRONLAKE_M2_MIN,
.max = IRONLAKE_M2_MAX },
.p = { .min = IRONLAKE_P_DISPLAY_PORT_MIN,
.max = IRONLAKE_P_DISPLAY_PORT_MAX },
.p1 = { .min = IRONLAKE_P1_DISPLAY_PORT_MIN,
.max = IRONLAKE_P1_DISPLAY_PORT_MAX},
.p2 = { .dot_limit = IRONLAKE_P2_DISPLAY_PORT_LIMIT,
.p2_slow = IRONLAKE_P2_DISPLAY_PORT_SLOW,
.p2_fast = IRONLAKE_P2_DISPLAY_PORT_FAST },
.p = { .min = IRONLAKE_DP_P_MIN,
.max = IRONLAKE_DP_P_MAX },
.p1 = { .min = IRONLAKE_DP_P1_MIN,
.max = IRONLAKE_DP_P1_MAX},
.p2 = { .dot_limit = IRONLAKE_DP_P2_LIMIT,
.p2_slow = IRONLAKE_DP_P2_SLOW,
.p2_fast = IRONLAKE_DP_P2_FAST },
.find_pll = intel_find_pll_ironlake_dp,
};
static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
const intel_limit_t *limit;
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
limit = &intel_limits_ironlake_lvds;
else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
int refclk = 120;
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
if (dev_priv->lvds_use_ssc && dev_priv->lvds_ssc_freq == 100)
refclk = 100;
if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
LVDS_CLKB_POWER_UP) {
/* LVDS dual channel */
if (refclk == 100)
limit = &intel_limits_ironlake_dual_lvds_100m;
else
limit = &intel_limits_ironlake_dual_lvds;
} else {
if (refclk == 100)
limit = &intel_limits_ironlake_single_lvds_100m;
else
limit = &intel_limits_ironlake_single_lvds;
}
} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
HAS_eDP)
limit = &intel_limits_ironlake_display_port;
else
limit = &intel_limits_ironlake_sdvo;
limit = &intel_limits_ironlake_dac;
return limit;
}
@ -914,6 +1031,8 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
/* enable it... */
fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
if (IS_I945GM(dev))
fbc_ctl |= FBC_C3_IDLE; /* 945 needs special SR handling */
fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
if (obj_priv->tiling_mode != I915_TILING_NONE)
@ -3962,7 +4081,8 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
struct intel_unpin_work {
struct work_struct work;
struct drm_device *dev;
struct drm_gem_object *obj;
struct drm_gem_object *old_fb_obj;
struct drm_gem_object *pending_flip_obj;
struct drm_pending_vblank_event *event;
int pending;
};
@ -3973,8 +4093,9 @@ static void intel_unpin_work_fn(struct work_struct *__work)
container_of(__work, struct intel_unpin_work, work);
mutex_lock(&work->dev->struct_mutex);
i915_gem_object_unpin(work->obj);
drm_gem_object_unreference(work->obj);
i915_gem_object_unpin(work->old_fb_obj);
drm_gem_object_unreference(work->pending_flip_obj);
drm_gem_object_unreference(work->old_fb_obj);
mutex_unlock(&work->dev->struct_mutex);
kfree(work);
}
@ -3998,7 +4119,7 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe)
work = intel_crtc->unpin_work;
if (work == NULL || !work->pending) {
if (work && !work->pending) {
obj_priv = work->obj->driver_private;
obj_priv = work->pending_flip_obj->driver_private;
DRM_DEBUG_DRIVER("flip finish: %p (%d) not pending?\n",
obj_priv,
atomic_read(&obj_priv->pending_flip));
@ -4023,7 +4144,7 @@ void intel_finish_page_flip(struct drm_device *dev, int pipe)
spin_unlock_irqrestore(&dev->event_lock, flags);
obj_priv = work->obj->driver_private;
obj_priv = work->pending_flip_obj->driver_private;
/* Initial scanout buffer will have a 0 pending flip count */
if ((atomic_read(&obj_priv->pending_flip) == 0) ||
@ -4060,7 +4181,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_unpin_work *work;
unsigned long flags;
int ret;
int pipesrc_reg = (intel_crtc->pipe == 0) ? PIPEASRC : PIPEBSRC;
int ret, pipesrc;
RING_LOCALS;
work = kzalloc(sizeof *work, GFP_KERNEL);
@ -4072,7 +4194,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
work->event = event;
work->dev = crtc->dev;
intel_fb = to_intel_framebuffer(crtc->fb);
work->obj = intel_fb->obj;
work->old_fb_obj = intel_fb->obj;
INIT_WORK(&work->work, intel_unpin_work_fn);
/* We borrow the event spin lock for protecting unpin_work */
@ -4100,14 +4222,16 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
return ret;
}
/* Reference the old fb object for the scheduled work. */
drm_gem_object_reference(work->obj);
/* Reference the objects for the scheduled work. */
drm_gem_object_reference(work->old_fb_obj);
drm_gem_object_reference(obj);
crtc->fb = fb;
i915_gem_object_flush_write_domain(obj);
drm_vblank_get(dev, intel_crtc->pipe);
obj_priv = obj->driver_private;
atomic_inc(&obj_priv->pending_flip);
work->pending_flip_obj = obj;
BEGIN_LP_RING(4);
OUT_RING(MI_DISPLAY_FLIP |
@ -4115,7 +4239,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
OUT_RING(fb->pitch);
if (IS_I965G(dev)) {
OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode);
OUT_RING((fb->width << 16) | fb->height);
pipesrc = I915_READ(pipesrc_reg);
OUT_RING(pipesrc & 0x0fff0fff);
} else {
OUT_RING(obj_priv->gtt_offset);
OUT_RING(MI_NOOP);

View file

@ -148,7 +148,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
mutex_lock(&dev->struct_mutex);
ret = i915_gem_object_pin(fbo, PAGE_SIZE);
ret = i915_gem_object_pin(fbo, 64*1024);
if (ret) {
DRM_ERROR("failed to pin fb: %d\n", ret);
goto out_unref;