Commit 4d9c55e4 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'drm-patches' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6

* 'drm-patches' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6:
  drm: _end is shadowing real _end, just rename it.
  drm/vbl rework: rework how the drm deals with vblank.
  drm: reorganise minor number handling using backported modesetting code.
  drm/i915: Handle tiled buffers in vblank tasklet
  drm/i965: On I965, use correct 3DSTATE_DRAWING_RECTANGLE command in vblank
  drm: Remove unneeded dma sync in ATI pcigart alloc
  drm: Fix mismerge of non-coherent DMA patch
parents 50704516 b74e2082
...@@ -167,13 +167,6 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga ...@@ -167,13 +167,6 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga
page_base += ATI_PCIGART_PAGE_SIZE; page_base += ATI_PCIGART_PAGE_SIZE;
} }
} }
if (gart_info->gart_table_location == DRM_ATI_GART_MAIN)
dma_sync_single_for_device(&dev->pdev->dev,
bus_address,
max_pages * sizeof(u32),
PCI_DMA_TODEVICE);
ret = 1; ret = 1;
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
......
...@@ -471,6 +471,7 @@ struct drm_irq_busid { ...@@ -471,6 +471,7 @@ struct drm_irq_busid {
enum drm_vblank_seq_type { enum drm_vblank_seq_type {
_DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */ _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */
_DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */ _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */
_DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */
_DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */ _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */
_DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */ _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */
_DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */ _DRM_VBLANK_SIGNAL = 0x40000000 /**< Send signal instead of blocking */
...@@ -503,6 +504,21 @@ union drm_wait_vblank { ...@@ -503,6 +504,21 @@ union drm_wait_vblank {
struct drm_wait_vblank_reply reply; struct drm_wait_vblank_reply reply;
}; };
enum drm_modeset_ctl_cmd {
_DRM_PRE_MODESET = 1,
_DRM_POST_MODESET = 2,
};
/**
* DRM_IOCTL_MODESET_CTL ioctl argument type
*
* \sa drmModesetCtl().
*/
struct drm_modeset_ctl {
unsigned long arg;
enum drm_modeset_ctl_cmd cmd;
};
/** /**
* DRM_IOCTL_AGP_ENABLE ioctl argument type. * DRM_IOCTL_AGP_ENABLE ioctl argument type.
* *
...@@ -587,6 +603,7 @@ struct drm_set_version { ...@@ -587,6 +603,7 @@ struct drm_set_version {
#define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client) #define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, struct drm_client)
#define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats) #define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, struct drm_stats)
#define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version) #define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, struct drm_set_version)
#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, struct drm_modeset_ctl)
#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique) #define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, struct drm_unique)
#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth) #define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, struct drm_auth)
......
...@@ -100,10 +100,8 @@ struct drm_device; ...@@ -100,10 +100,8 @@ struct drm_device;
#define DRIVER_HAVE_DMA 0x20 #define DRIVER_HAVE_DMA 0x20
#define DRIVER_HAVE_IRQ 0x40 #define DRIVER_HAVE_IRQ 0x40
#define DRIVER_IRQ_SHARED 0x80 #define DRIVER_IRQ_SHARED 0x80
#define DRIVER_IRQ_VBL 0x100
#define DRIVER_DMA_QUEUE 0x200 #define DRIVER_DMA_QUEUE 0x200
#define DRIVER_FB_DMA 0x400 #define DRIVER_FB_DMA 0x400
#define DRIVER_IRQ_VBL2 0x800
/***********************************************************************/ /***********************************************************************/
/** \name Begin the DRM... */ /** \name Begin the DRM... */
...@@ -379,13 +377,12 @@ struct drm_buf_entry { ...@@ -379,13 +377,12 @@ struct drm_buf_entry {
struct drm_file { struct drm_file {
int authenticated; int authenticated;
int master; int master;
int minor;
pid_t pid; pid_t pid;
uid_t uid; uid_t uid;
drm_magic_t magic; drm_magic_t magic;
unsigned long ioctl_count; unsigned long ioctl_count;
struct list_head lhead; struct list_head lhead;
struct drm_head *head; struct drm_minor *minor;
int remove_auth_on_close; int remove_auth_on_close;
unsigned long lock_count; unsigned long lock_count;
struct file *filp; struct file *filp;
...@@ -580,10 +577,52 @@ struct drm_driver { ...@@ -580,10 +577,52 @@ struct drm_driver {
int (*context_dtor) (struct drm_device *dev, int context); int (*context_dtor) (struct drm_device *dev, int context);
int (*kernel_context_switch) (struct drm_device *dev, int old, int (*kernel_context_switch) (struct drm_device *dev, int old,
int new); int new);
void (*kernel_context_switch_unlock) (struct drm_device *dev); void (*kernel_context_switch_unlock) (struct drm_device * dev);
int (*vblank_wait) (struct drm_device *dev, unsigned int *sequence); /**
int (*vblank_wait2) (struct drm_device *dev, unsigned int *sequence); * get_vblank_counter - get raw hardware vblank counter
int (*dri_library_name) (struct drm_device *dev, char *buf); * @dev: DRM device
* @crtc: counter to fetch
*
* Driver callback for fetching a raw hardware vblank counter
* for @crtc. If a device doesn't have a hardware counter, the
* driver can simply return the value of drm_vblank_count and
* make the enable_vblank() and disable_vblank() hooks into no-ops,
* leaving interrupts enabled at all times.
*
* Wraparound handling and loss of events due to modesetting is dealt
* with in the DRM core code.
*
* RETURNS
* Raw vblank counter value.
*/
u32 (*get_vblank_counter) (struct drm_device *dev, int crtc);
/**
* enable_vblank - enable vblank interrupt events
* @dev: DRM device
* @crtc: which irq to enable
*
* Enable vblank interrupts for @crtc. If the device doesn't have
* a hardware vblank counter, this routine should be a no-op, since
* interrupts will have to stay on to keep the count accurate.
*
* RETURNS
* Zero on success, appropriate errno if the given @crtc's vblank
* interrupt cannot be enabled.
*/
int (*enable_vblank) (struct drm_device *dev, int crtc);
/**
* disable_vblank - disable vblank interrupt events
* @dev: DRM device
* @crtc: which irq to enable
*
* Disable vblank interrupts for @crtc. If the device doesn't have
* a hardware vblank counter, this routine should be a no-op, since
* interrupts will have to stay on to keep the count accurate.
*/
void (*disable_vblank) (struct drm_device *dev, int crtc);
int (*dri_library_name) (struct drm_device *dev, char * buf);
/** /**
* Called by \c drm_device_is_agp. Typically used to determine if a * Called by \c drm_device_is_agp. Typically used to determine if a
...@@ -602,7 +641,7 @@ struct drm_driver { ...@@ -602,7 +641,7 @@ struct drm_driver {
irqreturn_t(*irq_handler) (DRM_IRQ_ARGS); irqreturn_t(*irq_handler) (DRM_IRQ_ARGS);
void (*irq_preinstall) (struct drm_device *dev); void (*irq_preinstall) (struct drm_device *dev);
void (*irq_postinstall) (struct drm_device *dev); int (*irq_postinstall) (struct drm_device *dev);
void (*irq_uninstall) (struct drm_device *dev); void (*irq_uninstall) (struct drm_device *dev);
void (*reclaim_buffers) (struct drm_device *dev, void (*reclaim_buffers) (struct drm_device *dev,
struct drm_file * file_priv); struct drm_file * file_priv);
...@@ -630,16 +669,19 @@ struct drm_driver { ...@@ -630,16 +669,19 @@ struct drm_driver {
struct pci_driver pci_driver; struct pci_driver pci_driver;
}; };
#define DRM_MINOR_UNASSIGNED 0
#define DRM_MINOR_LEGACY 1
/** /**
* DRM head structure. This structure represent a video head on a card * DRM minor structure. This structure represents a drm minor number.
* that may contain multiple heads. Embed one per head of these in the
* private drm_device structure.
*/ */
struct drm_head { struct drm_minor {
int minor; /**< Minor device number */ int index; /**< Minor device number */
int type; /**< Control or render */
dev_t device; /**< Device number for mknod */
struct device kdev; /**< Linux device */
struct drm_device *dev; struct drm_device *dev;
struct proc_dir_entry *dev_root; /**< proc directory entry */ struct proc_dir_entry *dev_root; /**< proc directory entry */
dev_t device; /**< Device number for mknod */
}; };
/** /**
...@@ -647,7 +689,6 @@ struct drm_head { ...@@ -647,7 +689,6 @@ struct drm_head {
* may contain multiple heads. * may contain multiple heads.
*/ */
struct drm_device { struct drm_device {
struct device dev; /**< Linux device */
char *unique; /**< Unique identifier: e.g., busid */ char *unique; /**< Unique identifier: e.g., busid */
int unique_len; /**< Length of unique field */ int unique_len; /**< Length of unique field */
char *devname; /**< For /proc/interrupts */ char *devname; /**< For /proc/interrupts */
...@@ -729,13 +770,21 @@ struct drm_device { ...@@ -729,13 +770,21 @@ struct drm_device {
/** \name VBLANK IRQ support */ /** \name VBLANK IRQ support */
/*@{ */ /*@{ */
wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ wait_queue_head_t *vbl_queue; /**< VBLANK wait queue */
atomic_t vbl_received; atomic_t *_vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */
atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */
spinlock_t vbl_lock; spinlock_t vbl_lock;
struct list_head vbl_sigs; /**< signal list to send on VBLANK */ struct list_head *vbl_sigs; /**< signal list to send on VBLANK */
struct list_head vbl_sigs2; /**< signals to send on secondary VBLANK */ atomic_t vbl_signal_pending; /* number of signals pending on all crtcs*/
unsigned int vbl_pending; atomic_t *vblank_refcount; /* number of users of vblank interrupts per crtc */
u32 *last_vblank; /* protected by dev->vbl_lock, used */
/* for wraparound handling */
u32 *vblank_offset; /* used to track how many vblanks */
int *vblank_enabled; /* so we don't call enable more than
once per disable */
u32 *vblank_premodeset; /* were lost during modeset */
struct timer_list vblank_disable_timer;
unsigned long max_vblank_count; /**< size of vblank counter register */
spinlock_t tasklet_lock; /**< For drm_locked_tasklet */ spinlock_t tasklet_lock; /**< For drm_locked_tasklet */
void (*locked_tasklet_func)(struct drm_device *dev); void (*locked_tasklet_func)(struct drm_device *dev);
...@@ -755,6 +804,7 @@ struct drm_device { ...@@ -755,6 +804,7 @@ struct drm_device {
#ifdef __alpha__ #ifdef __alpha__
struct pci_controller *hose; struct pci_controller *hose;
#endif #endif
int num_crtcs; /**< Number of CRTCs on this device */
struct drm_sg_mem *sg; /**< Scatter gather memory */ struct drm_sg_mem *sg; /**< Scatter gather memory */
void *dev_private; /**< device private data */ void *dev_private; /**< device private data */
struct drm_sigdata sigdata; /**< For block_all_signals */ struct drm_sigdata sigdata; /**< For block_all_signals */
...@@ -763,7 +813,7 @@ struct drm_device { ...@@ -763,7 +813,7 @@ struct drm_device {
struct drm_driver *driver; struct drm_driver *driver;
drm_local_map_t *agp_buffer_map; drm_local_map_t *agp_buffer_map;
unsigned int agp_buffer_token; unsigned int agp_buffer_token;
struct drm_head primary; /**< primary screen head */ struct drm_minor *primary; /**< render type primary screen head */
/** \name Drawable information */ /** \name Drawable information */
/*@{ */ /*@{ */
...@@ -989,11 +1039,19 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev); ...@@ -989,11 +1039,19 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev);
extern void drm_driver_irq_postinstall(struct drm_device *dev); extern void drm_driver_irq_postinstall(struct drm_device *dev);
extern void drm_driver_irq_uninstall(struct drm_device *dev); extern void drm_driver_irq_uninstall(struct drm_device *dev);
extern int drm_wait_vblank(struct drm_device *dev, void *data, extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);
struct drm_file *file_priv); extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp);
extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq);
extern void drm_vbl_send_signals(struct drm_device *dev);
extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*)); extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*));
extern u32 drm_vblank_count(struct drm_device *dev, int crtc);
extern void drm_update_vblank_count(struct drm_device *dev, int crtc);
extern void drm_handle_vblank(struct drm_device *dev, int crtc);
extern int drm_vblank_get(struct drm_device *dev, int crtc);
extern void drm_vblank_put(struct drm_device *dev, int crtc);
/* Modesetting support */
extern int drm_modeset_ctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
/* AGP/GART support (drm_agpsupport.h) */ /* AGP/GART support (drm_agpsupport.h) */
extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); extern struct drm_agp_head *drm_agp_init(struct drm_device *dev);
...@@ -1030,23 +1088,20 @@ extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle); ...@@ -1030,23 +1088,20 @@ extern int drm_agp_unbind_memory(DRM_AGP_MEM * handle);
extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver); struct drm_driver *driver);
extern int drm_put_dev(struct drm_device *dev); extern int drm_put_dev(struct drm_device *dev);
extern int drm_put_head(struct drm_head *head); extern int drm_put_minor(struct drm_minor **minor);
extern unsigned int drm_debug; extern unsigned int drm_debug;
extern unsigned int drm_cards_limit;
extern struct drm_head **drm_heads;
extern struct class *drm_class; extern struct class *drm_class;
extern struct proc_dir_entry *drm_proc_root; extern struct proc_dir_entry *drm_proc_root;
extern struct idr drm_minors_idr;
extern drm_local_map_t *drm_getsarea(struct drm_device *dev); extern drm_local_map_t *drm_getsarea(struct drm_device *dev);
/* Proc support (drm_proc.h) */ /* Proc support (drm_proc.h) */
extern int drm_proc_init(struct drm_device *dev, extern int drm_proc_init(struct drm_minor *minor, int minor_id,
int minor, struct proc_dir_entry *root);
struct proc_dir_entry *root, extern int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root);
struct proc_dir_entry **dev_root);
extern int drm_proc_cleanup(int minor,
struct proc_dir_entry *root,
struct proc_dir_entry *dev_root);
/* Scatter Gather Support (drm_scatter.h) */ /* Scatter Gather Support (drm_scatter.h) */
extern void drm_sg_cleanup(struct drm_sg_mem * entry); extern void drm_sg_cleanup(struct drm_sg_mem * entry);
...@@ -1071,8 +1126,8 @@ extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); ...@@ -1071,8 +1126,8 @@ extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah);
struct drm_sysfs_class; struct drm_sysfs_class;
extern struct class *drm_sysfs_create(struct module *owner, char *name); extern struct class *drm_sysfs_create(struct module *owner, char *name);
extern void drm_sysfs_destroy(void); extern void drm_sysfs_destroy(void);
extern int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head); extern int drm_sysfs_device_add(struct drm_minor *minor);
extern void drm_sysfs_device_remove(struct drm_device *dev); extern void drm_sysfs_device_remove(struct drm_minor *minor);
/* /*
* Basic memory manager support (drm_mm.c) * Basic memory manager support (drm_mm.c)
......
...@@ -122,7 +122,7 @@ EXPORT_SYMBOL(drm_agp_acquire); ...@@ -122,7 +122,7 @@ EXPORT_SYMBOL(drm_agp_acquire);
int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, int drm_agp_acquire_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
return drm_agp_acquire((struct drm_device *) file_priv->head->dev); return drm_agp_acquire((struct drm_device *) file_priv->minor->dev);
} }
/** /**
......
...@@ -313,35 +313,36 @@ static void drm_cleanup(struct drm_device * dev) ...@@ -313,35 +313,36 @@ static void drm_cleanup(struct drm_device * dev)
drm_ht_remove(&dev->map_hash); drm_ht_remove(&dev->map_hash);
drm_ctxbitmap_cleanup(dev); drm_ctxbitmap_cleanup(dev);
drm_put_head(&dev->primary); drm_put_minor(&dev->primary);
if (drm_put_dev(dev)) if (drm_put_dev(dev))
DRM_ERROR("Cannot unload module\n"); DRM_ERROR("Cannot unload module\n");
} }
void drm_exit(struct drm_driver *driver) int drm_minors_cleanup(int id, void *ptr, void *data)
{ {
int i; struct drm_minor *minor = ptr;
struct drm_device *dev = NULL; struct drm_device *dev;
struct drm_head *head; struct drm_driver *driver = data;
dev = minor->dev;
if (minor->dev->driver != driver)
return 0;
if (minor->type != DRM_MINOR_LEGACY)
return 0;
if (dev)
pci_dev_put(dev->pdev);
drm_cleanup(dev);
return 1;
}
void drm_exit(struct drm_driver *driver)
{
DRM_DEBUG("\n"); DRM_DEBUG("\n");
for (i = 0; i < drm_cards_limit; i++) { idr_for_each(&drm_minors_idr, &drm_minors_cleanup, driver);
head = drm_heads[i];
if (!head)
continue;
if (!head->dev)
continue;
if (head->dev->driver != driver)
continue;
dev = head->dev;
if (dev) {
/* release the pci driver */
if (dev->pdev)
pci_dev_put(dev->pdev);
drm_cleanup(dev);
}
}
DRM_INFO("Module unloaded\n"); DRM_INFO("Module unloaded\n");
} }
...@@ -357,13 +358,7 @@ static int __init drm_core_init(void) ...@@ -357,13 +358,7 @@ static int __init drm_core_init(void)
{ {
int ret = -ENOMEM; int ret = -ENOMEM;
drm_cards_limit = idr_init(&drm_minors_idr);
(drm_cards_limit <
DRM_MAX_MINOR + 1 ? drm_cards_limit : DRM_MAX_MINOR + 1);
drm_heads =
drm_calloc(drm_cards_limit, sizeof(*drm_heads), DRM_MEM_STUB);
if (!drm_heads)
goto err_p1;
if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops)) if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
goto err_p1; goto err_p1;
...@@ -391,7 +386,8 @@ static int __init drm_core_init(void) ...@@ -391,7 +386,8 @@ static int __init drm_core_init(void)
drm_sysfs_destroy(); drm_sysfs_destroy();
err_p2: err_p2:
unregister_chrdev(DRM_MAJOR, "drm"); unregister_chrdev(DRM_MAJOR, "drm");
drm_free(drm_heads, sizeof(*drm_heads) * drm_cards_limit, DRM_MEM_STUB);
idr_destroy(&drm_minors_idr);
err_p1: err_p1:
return ret; return ret;
} }
...@@ -403,7 +399,7 @@ static void __exit drm_core_exit(void) ...@@ -403,7 +399,7 @@ static void __exit drm_core_exit(void)
unregister_chrdev(DRM_MAJOR, "drm"); unregister_chrdev(DRM_MAJOR, "drm");
drm_free(drm_heads, sizeof(*drm_heads) * drm_cards_limit, DRM_MEM_STUB); idr_destroy(&drm_minors_idr);
} }
module_init(drm_core_init); module_init(drm_core_init);
...@@ -452,7 +448,7 @@ int drm_ioctl(struct inode *inode, struct file *filp, ...@@ -452,7 +448,7 @@ int drm_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct drm_file *file_priv = filp->private_data; struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->head->dev; struct drm_device *dev = file_priv->minor->dev;
struct drm_ioctl_desc *ioctl; struct drm_ioctl_desc *ioctl;
drm_ioctl_t *func; drm_ioctl_t *func;
unsigned int nr = DRM_IOCTL_NR(cmd); unsigned int nr = DRM_IOCTL_NR(cmd);
...@@ -465,7 +461,7 @@ int drm_ioctl(struct inode *inode, struct file *filp, ...@@ -465,7 +461,7 @@ int drm_ioctl(struct inode *inode, struct file *filp,
DRM_DEBUG("pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n", DRM_DEBUG("pid=%d, cmd=0x%02x, nr=0x%02x, dev 0x%lx, auth=%d\n",
task_pid_nr(current), cmd, nr, task_pid_nr(current), cmd, nr,
(long)old_encode_dev(file_priv->head->device), (long)old_encode_dev(file_priv->minor->device),
file_priv->authenticated); file_priv->authenticated);
if ((nr >= DRM_CORE_IOCTL_COUNT) && if ((nr >= DRM_CORE_IOCTL_COUNT) &&
......
...@@ -129,16 +129,15 @@ static int drm_setup(struct drm_device * dev) ...@@ -129,16 +129,15 @@ static int drm_setup(struct drm_device * dev)
int drm_open(struct inode *inode, struct file *filp) int drm_open(struct inode *inode, struct file *filp)
{ {
struct drm_device *dev = NULL; struct drm_device *dev = NULL;
int minor = iminor(inode); int minor_id = iminor(inode);
struct drm_minor *minor;
int retcode = 0; int retcode = 0;
if (!((minor >= 0) && (minor < drm_cards_limit))) minor = idr_find(&drm_minors_idr, minor_id);
if (!minor)
return -ENODEV; return -ENODEV;
if (!drm_heads[minor]) if (!(dev = minor->dev))
return -ENODEV;
if (!(dev = drm_heads[minor]->dev))
return -ENODEV; return -ENODEV;
retcode = drm_open_helper(inode, filp, dev); retcode = drm_open_helper(inode, filp, dev);
...@@ -168,19 +167,18 @@ EXPORT_SYMBOL(drm_open); ...@@ -168,19 +167,18 @@ EXPORT_SYMBOL(drm_open);
int drm_stub_open(struct inode *inode, struct file *filp) int drm_stub_open(struct inode *inode, struct file *filp)
{ {
struct drm_device *dev = NULL; struct drm_device *dev = NULL;
int minor = iminor(inode); struct drm_minor *minor;
int minor_id = iminor(inode);
int err = -ENODEV; int err = -ENODEV;
const struct file_operations *old_fops; const struct file_operations *old_fops;
DRM_DEBUG("\n"); DRM_DEBUG("\n");
if (!((minor >= 0) && (minor < drm_cards_limit))) minor = idr_find(&drm_minors_idr, minor_id);
return -ENODEV; if (!minor)
if (!drm_heads[minor])
return -ENODEV; return -ENODEV;
if (!(dev = drm_heads[minor]->dev)) if (!(dev = minor->dev))
return -ENODEV; return -ENODEV;
old_fops = filp->f_op; old_fops = filp->f_op;
...@@ -225,7 +223,7 @@ static int drm_cpu_valid(void) ...@@ -225,7 +223,7 @@ static int drm_cpu_valid(void)
static int drm_open_helper(struct inode *inode, struct file *filp, static int drm_open_helper(struct inode *inode, struct file *filp,
struct drm_device * dev) struct drm_device * dev)
{ {
int minor = iminor(inode); int minor_id = iminor(inode);
struct drm_file *priv; struct drm_file *priv;
int ret; int ret;
...@@ -234,7 +232,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, ...@@ -234,7 +232,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
if (!drm_cpu_valid()) if (!drm_cpu_valid())
return -EINVAL; return -EINVAL;
DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor); DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id);
priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
if (!priv) if (!priv)
...@@ -245,8 +243,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, ...@@ -245,8 +243,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->filp = filp; priv->filp = filp;
priv->uid = current->euid; priv->uid = current->euid;
priv->pid = task_pid_nr(current); priv->pid = task_pid_nr(current);
priv->minor = minor; priv->minor = idr_find(&drm_minors_idr, minor_id);
priv->head = drm_heads[minor];
priv->ioctl_count = 0; priv->ioctl_count = 0;
/* for compatibility root is always authenticated */ /* for compatibility root is always authenticated */
priv->authenticated = capable(CAP_SYS_ADMIN); priv->authenticated = capable(CAP_SYS_ADMIN);
...@@ -297,11 +294,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp, ...@@ -297,11 +294,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
int drm_fasync(int fd, struct file *filp, int on) int drm_fasync(int fd, struct file *filp, int on)
{ {
struct drm_file *priv = filp->private_data; struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
int retcode; int retcode;
DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, DRM_DEBUG("fd = %d, device = 0x%lx\n", fd,
(long)old_encode_dev(priv->head->device)); (long)old_encode_dev(priv->minor->device));
retcode = fasync_helper(fd, filp, on, &dev->buf_async); retcode = fasync_helper(fd, filp, on, &dev->buf_async);
if (retcode < 0) if (retcode < 0)
return retcode; return retcode;
...@@ -324,7 +321,7 @@ EXPORT_SYMBOL(drm_fasync); ...@@ -324,7 +321,7 @@ EXPORT_SYMBOL(drm_fasync);
int drm_release(struct inode *inode, struct file *filp) int drm_release(struct inode *inode, struct file *filp)
{ {
struct drm_file *file_priv = filp->private_data; struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->head->dev; struct drm_device *dev = file_priv->minor->dev;
int retcode = 0; int retcode = 0;
unsigned long irqflags; unsigned long irqflags;
...@@ -341,14 +338,14 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -341,14 +338,14 @@ int drm_release(struct inode *inode, struct file *filp)
DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
task_pid_nr(current), task_pid_nr(current),
(long)old_encode_dev(file_priv->head->device), (long)old_encode_dev(file_priv->minor->device),
dev->open_count); dev->open_count);
if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) { if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) {
if (drm_i_have_hw_lock(dev, file_priv)) { if (drm_i_have_hw_lock(dev, file_priv)) {
dev->driver->reclaim_buffers_locked(dev, file_priv); dev->driver->reclaim_buffers_locked(dev, file_priv);
} else { } else {
unsigned long _end=jiffies + 3*DRM_HZ; unsigned long endtime = jiffies + 3 * DRM_HZ;
int locked = 0; int locked = 0;
drm_idlelock_take(&dev->lock); drm_idlelock_take(&dev->lock);
...@@ -366,7 +363,7 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -366,7 +363,7 @@ int drm_release(struct inode *inode, struct file *filp)
if (locked) if (locked)
break; break;
schedule(); schedule();
} while (!time_after_eq(jiffies, _end)); } while (!time_after_eq(jiffies, endtime));
if (!locked) { if (!locked) {
DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
......
...@@ -71,6 +71,117 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, ...@@ -71,6 +71,117 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
return 0; return 0;
} }
static void vblank_disable_fn(unsigned long arg)
{
struct drm_device *dev = (struct drm_device *)arg;
unsigned long irqflags;
int i;
for (i = 0; i < dev->num_crtcs; i++) {
spin_lock_irqsave(&dev->vbl_lock, irqflags);
if (atomic_read(&dev->vblank_refcount[i]) == 0 &&
dev->vblank_enabled[i]) {
dev->driver->disable_vblank(dev, i);
dev->vblank_enabled[i] = 0;
}
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
}
}
static void drm_vblank_cleanup(struct drm_device *dev)
{
/* Bail if the driver didn't call drm_vblank_init() */
if (dev->num_crtcs == 0)
return;
del_timer(&dev->vblank_disable_timer);
vblank_disable_fn((unsigned long)dev);
drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs,
DRM_MEM_DRIVER);
drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs,
DRM_MEM_DRIVER);
drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) *
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) *
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) *
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs,
DRM_MEM_DRIVER);
drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) *
dev->num_crtcs, DRM_MEM_DRIVER);
drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs,
DRM_MEM_DRIVER);
dev->num_crtcs = 0;
}
int drm_vblank_init(struct drm_device *dev, int num_crtcs)
{
int i, ret = -ENOMEM;
setup_timer(&dev->vblank_disable_timer, vblank_disable_fn,
(unsigned long)dev);
spin_lock_init(&dev->vbl_lock);
atomic_set(&dev->vbl_signal_pending, 0);
dev->num_crtcs = num_crtcs;
dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs,
DRM_MEM_DRIVER);
if (!dev->vbl_queue)
goto err;
dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs,
DRM_MEM_DRIVER);
if (!dev->vbl_sigs)
goto err;
dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs,
DRM_MEM_DRIVER);
if (!dev->_vblank_count)
goto err;
dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs,
DRM_MEM_DRIVER);
if (!dev->vblank_refcount)
goto err;
dev->vblank_enabled = drm_calloc(num_crtcs, sizeof(int),
DRM_MEM_DRIVER);
if (!dev->vblank_enabled)
goto err;
dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER);
if (!dev->last_vblank)
goto err;
dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32),
DRM_MEM_DRIVER);
if (!dev->vblank_premodeset)
goto err;
dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER);
if (!dev->vblank_offset)
goto err;
/* Zero per-crtc vblank stuff */
for (i = 0; i < num_crtcs; i++) {
init_waitqueue_head(&dev->vbl_queue[i]);
INIT_LIST_HEAD(&dev->vbl_sigs[i]);
atomic_set(&dev->_vblank_count[i], 0);
atomic_set(&dev->vblank_refcount[i], 0);
}
return 0;
err:
drm_vblank_cleanup(dev);
return ret;
}
EXPORT_SYMBOL(drm_vblank_init);
/** /**
* Install IRQ handler. * Install IRQ handler.
* *
...@@ -109,17 +220,6 @@ static int drm_irq_install(struct drm_device * dev) ...@@ -109,17 +220,6 @@ static int drm_irq_install(struct drm_device * dev)
DRM_DEBUG("irq=%d\n", dev->irq); DRM_DEBUG("irq=%d\n", dev->irq);
if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) {
init_waitqueue_head(&dev->vbl_queue);
spin_lock_init(&dev->vbl_lock);
INIT_LIST_HEAD(&dev->vbl_sigs);
INIT_LIST_HEAD(&dev->vbl_sigs2);
dev->vbl_pending = 0;
}
/* Before installing handler */ /* Before installing handler */
dev->driver->irq_preinstall(dev); dev->driver->irq_preinstall(dev);
...@@ -137,9 +237,14 @@ static int drm_irq_install(struct drm_device * dev) ...@@ -137,9 +237,14 @@ static int drm_irq_install(struct drm_device * dev)
} }
/* After installing handler */ /* After installing handler */
dev->driver->irq_postinstall(dev); ret = dev->driver->irq_postinstall(dev);
if (ret < 0) {
mutex_lock(&dev->struct_mutex);
dev->irq_enabled = 0;
mutex_unlock(&dev->struct_mutex);
}
return 0; return ret;
} }
/** /**
...@@ -170,6 +275,8 @@ int drm_irq_uninstall(struct drm_device * dev) ...@@ -170,6 +275,8 @@ int drm_irq_uninstall(struct drm_device * dev)
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
drm_vblank_cleanup(dev);
dev->locked_tasklet_func = NULL; dev->locked_tasklet_func = NULL;
return 0; return 0;
...@@ -213,6 +320,148 @@ int drm_control(struct drm_device *dev, void *data, ...@@ -213,6 +320,148 @@ int drm_control(struct drm_device *dev, void *data,
} }
} }
/**
* drm_vblank_count - retrieve "cooked" vblank counter value
* @dev: DRM device
* @crtc: which counter to retrieve
*
* Fetches the "cooked" vblank count value that represents the number of
* vblank events since the system was booted, including lost events due to
* modesetting activity.
*/
u32 drm_vblank_count(struct drm_device *dev, int crtc)
{
return atomic_read(&dev->_vblank_count[crtc]) +
dev->vblank_offset[crtc];
}
EXPORT_SYMBOL(drm_vblank_count);
/**
* drm_update_vblank_count - update the master vblank counter
* @dev: DRM device
* @crtc: counter to update
*
* Call back into the driver to update the appropriate vblank counter
* (specified by @crtc). Deal with wraparound, if it occurred, and
* update the last read value so we can deal with wraparound on the next
* call if necessary.
*/
void drm_update_vblank_count(struct drm_device *dev, int crtc)
{
unsigned long irqflags;
u32 cur_vblank, diff;
/*
* Interrupts were disabled prior to this call, so deal with counter
* wrap if needed.
* NOTE! It's possible we lost a full dev->max_vblank_count events
* here if the register is small or we had vblank interrupts off for
* a long time.
*/
cur_vblank = dev->driver->get_vblank_counter(dev, crtc);
spin_lock_irqsave(&dev->vbl_lock, irqflags);
if (cur_vblank < dev->last_vblank[crtc]) {
diff = dev->max_vblank_count -
dev->last_vblank[crtc];
diff += cur_vblank;
} else {
diff = cur_vblank - dev->last_vblank[crtc];
}
dev->last_vblank[crtc] = cur_vblank;
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
atomic_add(diff, &dev->_vblank_count[crtc]);
}
EXPORT_SYMBOL(drm_update_vblank_count);
/**
* drm_vblank_get - get a reference count on vblank events
* @dev: DRM device
* @crtc: which CRTC to own
*
* Acquire a reference count on vblank events to avoid having them disabled
* while in use. Note callers will probably want to update the master counter
* using drm_update_vblank_count() above before calling this routine so that
* wakeups occur on the right vblank event.
*
* RETURNS
* Zero on success, nonzero on failure.
*/
int drm_vblank_get(struct drm_device *dev, int crtc)
{
unsigned long irqflags;
int ret = 0;
spin_lock_irqsave(&dev->vbl_lock, irqflags);
/* Going from 0->1 means we have to enable interrupts again */
if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 &&
!dev->vblank_enabled[crtc]) {
ret = dev->driver->enable_vblank(dev, crtc);
if (ret)
atomic_dec(&dev->vblank_refcount[crtc]);
else
dev->vblank_enabled[crtc] = 1;
}
spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
return ret;
}
EXPORT_SYMBOL(drm_vblank_get);
/**
* drm_vblank_put - give up ownership of vblank events
* @dev: DRM device
* @crtc: which counter to give up
*
* Release ownership of a given vblank counter, turning off interrupts
* if possible.
*/
void drm_vblank_put(struct drm_device *dev, int crtc)
{
/* Last user schedules interrupt disable */
if (atomic_dec_and_test(&dev->vblank_refcount[crtc]))
mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ);
}
EXPORT_SYMBOL(drm_vblank_put);
/**
* drm_modeset_ctl - handle vblank event counter changes across mode switch
* @DRM_IOCTL_ARGS: standard ioctl arguments
*
* Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET
* ioctls around modesetting so that any lost vblank events are accounted for.
*/
int drm_modeset_ctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_modeset_ctl *modeset = data;
int crtc, ret = 0;
u32 new;
crtc = modeset->arg;
if (crtc >= dev->num_crtcs) {
ret = -EINVAL;
goto out;
}
switch (modeset->cmd) {
case _DRM_PRE_MODESET:
dev->vblank_premodeset[crtc] =
dev->driver->get_vblank_counter(dev, crtc);
break;
case _DRM_POST_MODESET:
new = dev->driver->get_vblank_counter(dev, crtc);
dev->vblank_offset[crtc] = dev->vblank_premodeset[crtc] - new;
break;
default:
ret = -EINVAL;
break;
}
out:
return ret;
}
/** /**
* Wait for VBLANK. * Wait for VBLANK.
* *
...@@ -232,12 +481,13 @@ int drm_control(struct drm_device *dev, void *data, ...@@ -232,12 +481,13 @@ int drm_control(struct drm_device *dev, void *data,
* *
* If a signal is not requested, then calls vblank_wait(). * If a signal is not requested, then calls vblank_wait().
*/ */
int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) int drm_wait_vblank(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{ {
union drm_wait_vblank *vblwait = data; union drm_wait_vblank *vblwait = data;
struct timeval now; struct timeval now;
int ret = 0; int ret = 0;
unsigned int flags, seq; unsigned int flags, seq, crtc;
if ((!dev->irq) || (!dev->irq_enabled)) if ((!dev->irq) || (!dev->irq_enabled))
return -EINVAL; return -EINVAL;
...@@ -251,13 +501,13 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr ...@@ -251,13 +501,13 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
} }
flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK;
crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? if (crtc >= dev->num_crtcs)
DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL))
return -EINVAL; return -EINVAL;
seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 drm_update_vblank_count(dev, crtc);
: &dev->vbl_received); seq = drm_vblank_count(dev, crtc);
switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {
case _DRM_VBLANK_RELATIVE: case _DRM_VBLANK_RELATIVE:
...@@ -276,8 +526,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr ...@@ -276,8 +526,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
if (flags & _DRM_VBLANK_SIGNAL) { if (flags & _DRM_VBLANK_SIGNAL) {
unsigned long irqflags; unsigned long irqflags;
struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) struct list_head *vbl_sigs = &dev->vbl_sigs[crtc];
? &dev->vbl_sigs2 : &dev->vbl_sigs;
struct drm_vbl_sig *vbl_sig; struct drm_vbl_sig *vbl_sig;
spin_lock_irqsave(&dev->vbl_lock, irqflags); spin_lock_irqsave(&dev->vbl_lock, irqflags);
...@@ -298,22 +547,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr ...@@ -298,22 +547,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
} }
} }
if (dev->vbl_pending >= 100) { if (atomic_read(&dev->vbl_signal_pending) >= 100) {
spin_unlock_irqrestore(&dev->vbl_lock, irqflags); spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
return -EBUSY; return -EBUSY;
} }
dev->vbl_pending++;
spin_unlock_irqrestore(&dev->vbl_lock, irqflags); spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
if (! vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig),
(vbl_sig = DRM_MEM_DRIVER);
drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) { if (!vbl_sig)
return -ENOMEM; return -ENOMEM;
ret = drm_vblank_get(dev, crtc);
if (ret) {
drm_free(vbl_sig, sizeof(struct drm_vbl_sig),
DRM_MEM_DRIVER);
return ret;
} }
memset((void *)vbl_sig, 0, sizeof(*vbl_sig)); atomic_inc(&dev->vbl_signal_pending);
vbl_sig->sequence = vblwait->request.sequence; vbl_sig->sequence = vblwait->request.sequence;
vbl_sig->info.si_signo = vblwait->request.signal; vbl_sig->info.si_signo = vblwait->request.signal;
...@@ -327,17 +580,20 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr ...@@ -327,17 +580,20 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
vblwait->reply.sequence = seq; vblwait->reply.sequence = seq;
} else { } else {
if (flags & _DRM_VBLANK_SECONDARY) { unsigned long cur_vblank;
if (dev->driver->vblank_wait2)
ret = dev->driver->vblank_wait2(dev, &vblwait->request.sequence); ret = drm_vblank_get(dev, crtc);
} else if (dev->driver->vblank_wait) if (ret)
ret = return ret;
dev->driver->vblank_wait(dev, DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ,
&vblwait->request.sequence); (((cur_vblank = drm_vblank_count(dev, crtc))
- vblwait->request.sequence) <= (1 << 23)));
drm_vblank_put(dev, crtc);
do_gettimeofday(&now); do_gettimeofday(&now);
vblwait->reply.tval_sec = now.tv_sec; vblwait->reply.tval_sec = now.tv_sec;
vblwait->reply.tval_usec = now.tv_usec; vblwait->reply.tval_usec = now.tv_usec;
vblwait->reply.sequence = cur_vblank;
} }
done: done:
...@@ -348,44 +604,57 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr ...@@ -348,44 +604,57 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr
* Send the VBLANK signals. * Send the VBLANK signals.
* *
* \param dev DRM device. * \param dev DRM device.
* \param crtc CRTC where the vblank event occurred
* *
* Sends a signal for each task in drm_device::vbl_sigs and empties the list. * Sends a signal for each task in drm_device::vbl_sigs and empties the list.
* *
* If a signal is not requested, then calls vblank_wait(). * If a signal is not requested, then calls vblank_wait().
*/ */
void drm_vbl_send_signals(struct drm_device * dev) static void drm_vbl_send_signals(struct drm_device * dev, int crtc)
{ {
struct drm_vbl_sig *vbl_sig, *tmp;
struct list_head *vbl_sigs;
unsigned int vbl_seq;
unsigned long flags; unsigned long flags;
int i;
spin_lock_irqsave(&dev->vbl_lock, flags); spin_lock_irqsave(&dev->vbl_lock, flags);
for (i = 0; i < 2; i++) { vbl_sigs = &dev->vbl_sigs[crtc];
struct drm_vbl_sig *vbl_sig, *tmp; vbl_seq = drm_vblank_count(dev, crtc);
struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs;
unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 :
&dev->vbl_received);
list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {
if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) {
vbl_sig->info.si_code = vbl_seq; vbl_sig->info.si_code = vbl_seq;
send_sig_info(vbl_sig->info.si_signo, send_sig_info(vbl_sig->info.si_signo,
&vbl_sig->info, vbl_sig->task); &vbl_sig->info, vbl_sig->task);
list_del(&vbl_sig->head); list_del(&vbl_sig->head);
drm_free(vbl_sig, sizeof(*vbl_sig), drm_free(vbl_sig, sizeof(*vbl_sig),
DRM_MEM_DRIVER); DRM_MEM_DRIVER);
atomic_dec(&dev->vbl_signal_pending);
dev->vbl_pending--; drm_vblank_put(dev, crtc);
} }
}
} }
spin_unlock_irqrestore(&dev->vbl_lock, flags); spin_unlock_irqrestore(&dev->vbl_lock, flags);
} }
EXPORT_SYMBOL(drm_vbl_send_signals); /**
* drm_handle_vblank - handle a vblank event
* @dev: DRM device
* @crtc: where this event occurred
*
* Drivers should call this routine in their vblank interrupt handlers to
* update the vblank counter and send any signals that may be pending.
*/
void drm_handle_vblank(struct drm_device *dev, int crtc)
{
drm_update_vblank_count(dev, crtc);
DRM_WAKEUP(&dev->vbl_queue[crtc]);
drm_vbl_send_signals(dev, crtc);
}
EXPORT_SYMBOL(drm_handle_vblank);
/** /**
* Tasklet wrapper function. * Tasklet wrapper function.
......
...@@ -87,34 +87,35 @@ static struct drm_proc_list { ...@@ -87,34 +87,35 @@ static struct drm_proc_list {
* "/proc/dri/%minor%/", and each entry in proc_list as * "/proc/dri/%minor%/", and each entry in proc_list as
* "/proc/dri/%minor%/%name%". * "/proc/dri/%minor%/%name%".
*/ */
int drm_proc_init(struct drm_device * dev, int minor, int drm_proc_init(struct drm_minor *minor, int minor_id,
struct proc_dir_entry *root, struct proc_dir_entry **dev_root) struct proc_dir_entry *root)
{ {
struct proc_dir_entry *ent; struct proc_dir_entry *ent;
int i, j; int i, j;
char name[64]; char name[64];
sprintf(name, "%d", minor); sprintf(name, "%d", minor_id);
*dev_root = proc_mkdir(name, root); minor->dev_root = proc_mkdir(name, root);
if (!*dev_root) { if (!minor->dev_root) {
DRM_ERROR("Cannot create /proc/dri/%s\n", name); DRM_ERROR("Cannot create /proc/dri/%s\n", name);
return -1; return -1;
} }
for (i = 0; i < DRM_PROC_ENTRIES; i++) { for (i = 0; i < DRM_PROC_ENTRIES; i++) {
ent = create_proc_entry(drm_proc_list[i].name, ent = create_proc_entry(drm_proc_list[i].name,
S_IFREG | S_IRUGO, *dev_root); S_IFREG | S_IRUGO, minor->dev_root);
if (!ent) { if (!ent) {
DRM_ERROR("Cannot create /proc/dri/%s/%s\n", DRM_ERROR("Cannot create /proc/dri/%s/%s\n",
name, drm_proc_list[i].name); name, drm_proc_list[i].name);
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
remove_proc_entry(drm_proc_list[i].name, remove_proc_entry(drm_proc_list[i].name,
*dev_root); minor->dev_root);
remove_proc_entry(name, root); remove_proc_entry(name, root);
minor->dev_root = NULL;
return -1; return -1;
} }
ent->read_proc = drm_proc_list[i].f; ent->read_proc = drm_proc_list[i].f;
ent->data = dev; ent->data = minor;
} }
return 0; return 0;
...@@ -130,18 +131,17 @@ int drm_proc_init(struct drm_device * dev, int minor, ...@@ -130,18 +131,17 @@ int drm_proc_init(struct drm_device * dev, int minor,
* *
* Remove all proc entries created by proc_init(). * Remove all proc entries created by proc_init().
*/ */
int drm_proc_cleanup(int minor, struct proc_dir_entry *root, int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root)
struct proc_dir_entry *dev_root)
{ {
int i; int i;
char name[64]; char name[64];
if (!root || !dev_root) if (!root || !minor->dev_root)
return 0; return 0;
for (i = 0; i < DRM_PROC_ENTRIES; i++) for (i = 0; i < DRM_PROC_ENTRIES; i++)
remove_proc_entry(drm_proc_list[i].name, dev_root); remove_proc_entry(drm_proc_list[i].name, minor->dev_root);
sprintf(name, "%d", minor); sprintf(name, "%d", minor->index);
remove_proc_entry(name, root); remove_proc_entry(name, root);
return 0; return 0;
...@@ -163,7 +163,8 @@ int drm_proc_cleanup(int minor, struct proc_dir_entry *root, ...@@ -163,7 +163,8 @@ int drm_proc_cleanup(int minor, struct proc_dir_entry *root,
static int drm_name_info(char *buf, char **start, off_t offset, int request, static int drm_name_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int len = 0; int len = 0;
if (offset > DRM_PROC_LIMIT) { if (offset > DRM_PROC_LIMIT) {
...@@ -205,7 +206,8 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request, ...@@ -205,7 +206,8 @@ static int drm_name_info(char *buf, char **start, off_t offset, int request,
static int drm__vm_info(char *buf, char **start, off_t offset, int request, static int drm__vm_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int len = 0; int len = 0;
struct drm_map *map; struct drm_map *map;
struct drm_map_list *r_list; struct drm_map_list *r_list;
...@@ -261,7 +263,8 @@ static int drm__vm_info(char *buf, char **start, off_t offset, int request, ...@@ -261,7 +263,8 @@ static int drm__vm_info(char *buf, char **start, off_t offset, int request,
static int drm_vm_info(char *buf, char **start, off_t offset, int request, static int drm_vm_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int ret; int ret;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
...@@ -284,7 +287,8 @@ static int drm_vm_info(char *buf, char **start, off_t offset, int request, ...@@ -284,7 +287,8 @@ static int drm_vm_info(char *buf, char **start, off_t offset, int request,
static int drm__queues_info(char *buf, char **start, off_t offset, static int drm__queues_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data) int request, int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int len = 0; int len = 0;
int i; int i;
struct drm_queue *q; struct drm_queue *q;
...@@ -334,7 +338,8 @@ static int drm__queues_info(char *buf, char **start, off_t offset, ...@@ -334,7 +338,8 @@ static int drm__queues_info(char *buf, char **start, off_t offset,
static int drm_queues_info(char *buf, char **start, off_t offset, int request, static int drm_queues_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int ret; int ret;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
...@@ -357,7 +362,8 @@ static int drm_queues_info(char *buf, char **start, off_t offset, int request, ...@@ -357,7 +362,8 @@ static int drm_queues_info(char *buf, char **start, off_t offset, int request,
static int drm__bufs_info(char *buf, char **start, off_t offset, int request, static int drm__bufs_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int len = 0; int len = 0;
struct drm_device_dma *dma = dev->dma; struct drm_device_dma *dma = dev->dma;
int i; int i;
...@@ -406,7 +412,8 @@ static int drm__bufs_info(char *buf, char **start, off_t offset, int request, ...@@ -406,7 +412,8 @@ static int drm__bufs_info(char *buf, char **start, off_t offset, int request,
static int drm_bufs_info(char *buf, char **start, off_t offset, int request, static int drm_bufs_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int ret; int ret;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
...@@ -429,7 +436,8 @@ static int drm_bufs_info(char *buf, char **start, off_t offset, int request, ...@@ -429,7 +436,8 @@ static int drm_bufs_info(char *buf, char **start, off_t offset, int request,
static int drm__clients_info(char *buf, char **start, off_t offset, static int drm__clients_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data) int request, int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int len = 0; int len = 0;
struct drm_file *priv; struct drm_file *priv;
...@@ -445,7 +453,7 @@ static int drm__clients_info(char *buf, char **start, off_t offset, ...@@ -445,7 +453,7 @@ static int drm__clients_info(char *buf, char **start, off_t offset,
list_for_each_entry(priv, &dev->filelist, lhead) { list_for_each_entry(priv, &dev->filelist, lhead) {
DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n", DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
priv->authenticated ? 'y' : 'n', priv->authenticated ? 'y' : 'n',
priv->minor, priv->minor->index,
priv->pid, priv->pid,
priv->uid, priv->magic, priv->ioctl_count); priv->uid, priv->magic, priv->ioctl_count);
} }
...@@ -462,7 +470,8 @@ static int drm__clients_info(char *buf, char **start, off_t offset, ...@@ -462,7 +470,8 @@ static int drm__clients_info(char *buf, char **start, off_t offset,
static int drm_clients_info(char *buf, char **start, off_t offset, static int drm_clients_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data) int request, int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int ret; int ret;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
...@@ -476,7 +485,8 @@ static int drm_clients_info(char *buf, char **start, off_t offset, ...@@ -476,7 +485,8 @@ static int drm_clients_info(char *buf, char **start, off_t offset,
static int drm__vma_info(char *buf, char **start, off_t offset, int request, static int drm__vma_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int len = 0; int len = 0;
struct drm_vma_entry *pt; struct drm_vma_entry *pt;
struct vm_area_struct *vma; struct vm_area_struct *vma;
...@@ -535,7 +545,8 @@ static int drm__vma_info(char *buf, char **start, off_t offset, int request, ...@@ -535,7 +545,8 @@ static int drm__vma_info(char *buf, char **start, off_t offset, int request,
static int drm_vma_info(char *buf, char **start, off_t offset, int request, static int drm_vma_info(char *buf, char **start, off_t offset, int request,
int *eof, void *data) int *eof, void *data)
{ {
struct drm_device *dev = (struct drm_device *) data; struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
int ret; int ret;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
......
...@@ -36,23 +36,49 @@ ...@@ -36,23 +36,49 @@
#include "drmP.h" #include "drmP.h"
#include "drm_core.h" #include "drm_core.h"
unsigned int drm_cards_limit = 16; /* Enough for one machine */
unsigned int drm_debug = 0; /* 1 to enable debug output */ unsigned int drm_debug = 0; /* 1 to enable debug output */
EXPORT_SYMBOL(drm_debug); EXPORT_SYMBOL(drm_debug);
MODULE_AUTHOR(CORE_AUTHOR); MODULE_AUTHOR(CORE_AUTHOR);
MODULE_DESCRIPTION(CORE_DESC); MODULE_DESCRIPTION(CORE_DESC);
MODULE_LICENSE("GPL and additional rights"); MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(cards_limit, "Maximum number of graphics cards");
MODULE_PARM_DESC(debug, "Enable debug output"); MODULE_PARM_DESC(debug, "Enable debug output");
module_param_named(cards_limit, drm_cards_limit, int, 0444);
module_param_named(debug, drm_debug, int, 0600); module_param_named(debug, drm_debug, int, 0600);
struct drm_head **drm_heads; struct idr drm_minors_idr;
struct class *drm_class; struct class *drm_class;
struct proc_dir_entry *drm_proc_root; struct proc_dir_entry *drm_proc_root;
static int drm_minor_get_id(struct drm_device *dev, int type)
{
int new_id;
int ret;
int base = 0, limit = 63;
again:
if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {
DRM_ERROR("Out of memory expanding drawable idr\n");
return -ENOMEM;
}
mutex_lock(&dev->struct_mutex);
ret = idr_get_new_above(&drm_minors_idr, NULL,
base, &new_id);
mutex_unlock(&dev->struct_mutex);
if (ret == -EAGAIN) {
goto again;
} else if (ret) {
return ret;
}
if (new_id >= limit) {
idr_remove(&drm_minors_idr, new_id);
return -EINVAL;
}
return new_id;
}
static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
const struct pci_device_id *ent, const struct pci_device_id *ent,
struct drm_driver *driver) struct drm_driver *driver)
...@@ -145,48 +171,60 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev, ...@@ -145,48 +171,60 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
* create the proc init entry via proc_init(). This routines assigns * create the proc init entry via proc_init(). This routines assigns
* minor numbers to secondary heads of multi-headed cards * minor numbers to secondary heads of multi-headed cards
*/ */
static int drm_get_head(struct drm_device * dev, struct drm_head * head) static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
{ {
struct drm_head **heads = drm_heads; struct drm_minor *new_minor;
int ret; int ret;
int minor; int minor_id;
DRM_DEBUG("\n"); DRM_DEBUG("\n");
for (minor = 0; minor < drm_cards_limit; minor++, heads++) { minor_id = drm_minor_get_id(dev, type);
if (!*heads) { if (minor_id < 0)
return minor_id;
*head = (struct drm_head) {
.dev = dev,.device = new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
MKDEV(DRM_MAJOR, minor),.minor = minor,}; if (!new_minor) {
ret = -ENOMEM;
if ((ret = goto err_idr;
drm_proc_init(dev, minor, drm_proc_root, }
&head->dev_root))) {
printk(KERN_ERR new_minor->type = type;
"DRM: Failed to initialize /proc/dri.\n"); new_minor->device = MKDEV(DRM_MAJOR, minor_id);
goto err_g1; new_minor->dev = dev;
} new_minor->index = minor_id;
ret = drm_sysfs_device_add(dev, head); idr_replace(&drm_minors_idr, new_minor, minor_id);
if (ret) {
printk(KERN_ERR if (type == DRM_MINOR_LEGACY) {
"DRM: Error sysfs_device_add.\n"); ret = drm_proc_init(new_minor, minor_id, drm_proc_root);
goto err_g2; if (ret) {
} DRM_ERROR("DRM: Failed to initialize /proc/dri.\n");
*heads = head; goto err_mem;
DRM_DEBUG("new minor assigned %d\n", minor);
return 0;
} }
} else
new_minor->dev_root = NULL;
ret = drm_sysfs_device_add(new_minor);
if (ret) {
printk(KERN_ERR
"DRM: Error sysfs_device_add.\n");
goto err_g2;
} }
DRM_ERROR("out of minors\n"); *minor = new_minor;
return -ENOMEM;
err_g2: DRM_DEBUG("new minor assigned %d\n", minor_id);
drm_proc_cleanup(minor, drm_proc_root, head->dev_root); return 0;
err_g1:
*head = (struct drm_head) {
.dev = NULL}; err_g2:
if (new_minor->type == DRM_MINOR_LEGACY)
drm_proc_cleanup(new_minor, drm_proc_root);
err_mem:
kfree(new_minor);
err_idr:
idr_remove(&drm_minors_idr, minor_id);
*minor = NULL;
return ret; return ret;
} }
...@@ -222,12 +260,12 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, ...@@ -222,12 +260,12 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
printk(KERN_ERR "DRM: Fill_in_dev failed.\n"); printk(KERN_ERR "DRM: Fill_in_dev failed.\n");
goto err_g2; goto err_g2;
} }
if ((ret = drm_get_head(dev, &dev->primary))) if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY)))
goto err_g2; goto err_g2;
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
driver->name, driver->major, driver->minor, driver->patchlevel, driver->name, driver->major, driver->minor, driver->patchlevel,
driver->date, dev->primary.minor); driver->date, dev->primary->index);
return 0; return 0;
...@@ -276,18 +314,18 @@ int drm_put_dev(struct drm_device * dev) ...@@ -276,18 +314,18 @@ int drm_put_dev(struct drm_device * dev)
* last minor released. * last minor released.
* *
*/ */
int drm_put_head(struct drm_head * head) int drm_put_minor(struct drm_minor **minor_p)
{ {
int minor = head->minor; struct drm_minor *minor = *minor_p;
DRM_DEBUG("release secondary minor %d\n", minor->index);
DRM_DEBUG("release secondary minor %d\n", minor);
drm_proc_cleanup(minor, drm_proc_root, head->dev_root);
drm_sysfs_device_remove(head->dev);
*head = (struct drm_head) {.dev = NULL}; if (minor->type == DRM_MINOR_LEGACY)
drm_proc_cleanup(minor, drm_proc_root);
drm_sysfs_device_remove(minor);
drm_heads[minor] = NULL; idr_remove(&drm_minors_idr, minor->index);
kfree(minor);
*minor_p = NULL;
return 0; return 0;
} }
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
#include "drm_core.h" #include "drm_core.h"
#include "drmP.h" #include "drmP.h"
#define to_drm_device(d) container_of(d, struct drm_device, dev) #define to_drm_minor(d) container_of(d, struct drm_minor, kdev)
/** /**
* drm_sysfs_suspend - DRM class suspend hook * drm_sysfs_suspend - DRM class suspend hook
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
*/ */
static int drm_sysfs_suspend(struct device *dev, pm_message_t state) static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
{ {
struct drm_device *drm_dev = to_drm_device(dev); struct drm_minor *drm_minor = to_drm_minor(dev);
struct drm_device *drm_dev = drm_minor->dev;
printk(KERN_ERR "%s\n", __FUNCTION__); printk(KERN_ERR "%s\n", __FUNCTION__);
...@@ -50,7 +51,8 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state) ...@@ -50,7 +51,8 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state)
*/ */
static int drm_sysfs_resume(struct device *dev) static int drm_sysfs_resume(struct device *dev)
{ {
struct drm_device *drm_dev = to_drm_device(dev); struct drm_minor *drm_minor = to_drm_minor(dev);
struct drm_device *drm_dev = drm_minor->dev;
if (drm_dev->driver->resume) if (drm_dev->driver->resume)
return drm_dev->driver->resume(drm_dev); return drm_dev->driver->resume(drm_dev);
...@@ -120,10 +122,11 @@ void drm_sysfs_destroy(void) ...@@ -120,10 +122,11 @@ void drm_sysfs_destroy(void)
static ssize_t show_dri(struct device *device, struct device_attribute *attr, static ssize_t show_dri(struct device *device, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct drm_device *dev = to_drm_device(device); struct drm_minor *drm_minor = to_drm_minor(device);
if (dev->driver->dri_library_name) struct drm_device *drm_dev = drm_minor->dev;
return dev->driver->dri_library_name(dev, buf); if (drm_dev->driver->dri_library_name)
return snprintf(buf, PAGE_SIZE, "%s\n", dev->driver->pci_driver.name); return drm_dev->driver->dri_library_name(drm_dev, buf);
return snprintf(buf, PAGE_SIZE, "%s\n", drm_dev->driver->pci_driver.name);
} }
static struct device_attribute device_attrs[] = { static struct device_attribute device_attrs[] = {
...@@ -152,25 +155,28 @@ static void drm_sysfs_device_release(struct device *dev) ...@@ -152,25 +155,28 @@ static void drm_sysfs_device_release(struct device *dev)
* as the parent for the Linux device, and make sure it has a file containing * as the parent for the Linux device, and make sure it has a file containing
* the driver we're using (for userspace compatibility). * the driver we're using (for userspace compatibility).
*/ */
int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head) int drm_sysfs_device_add(struct drm_minor *minor)
{ {
int err; int err;
int i, j; int i, j;
char *minor_str;
dev->dev.parent = &dev->pdev->dev; minor->kdev.parent = &minor->dev->pdev->dev;
dev->dev.class = drm_class; minor->kdev.class = drm_class;
dev->dev.release = drm_sysfs_device_release; minor->kdev.release = drm_sysfs_device_release;
dev->dev.devt = head->device; minor->kdev.devt = minor->device;
snprintf(dev->dev.bus_id, BUS_ID_SIZE, "card%d", head->minor); minor_str = "card%d";
err = device_register(&dev->dev); snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index);
err = device_register(&minor->kdev);
if (err) { if (err) {
DRM_ERROR("device add failed: %d\n", err); DRM_ERROR("device add failed: %d\n", err);
goto err_out; goto err_out;
} }
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
err = device_create_file(&dev->dev, &device_attrs[i]); err = device_create_file(&minor->kdev, &device_attrs[i]);
if (err) if (err)
goto err_out_files; goto err_out_files;
} }
...@@ -180,8 +186,8 @@ int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head) ...@@ -180,8 +186,8 @@ int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head)
err_out_files: err_out_files:
if (i > 0) if (i > 0)
for (j = 0; j < i; j++) for (j = 0; j < i; j++)
device_remove_file(&dev->dev, &device_attrs[i]); device_remove_file(&minor->kdev, &device_attrs[i]);
device_unregister(&dev->dev); device_unregister(&minor->kdev);
err_out: err_out:
return err; return err;
...@@ -194,11 +200,11 @@ int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head) ...@@ -194,11 +200,11 @@ int drm_sysfs_device_add(struct drm_device *dev, struct drm_head *head)
* This call unregisters and cleans up a class device that was created with a * This call unregisters and cleans up a class device that was created with a
* call to drm_sysfs_device_add() * call to drm_sysfs_device_add()
*/ */
void drm_sysfs_device_remove(struct drm_device *dev) void drm_sysfs_device_remove(struct drm_minor *minor)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
device_remove_file(&dev->dev, &device_attrs[i]); device_remove_file(&minor->kdev, &device_attrs[i]);
device_unregister(&dev->dev); device_unregister(&minor->kdev);
} }
...@@ -90,7 +90,7 @@ static pgprot_t drm_dma_prot(uint32_t map_type, struct vm_area_struct *vma) ...@@ -90,7 +90,7 @@ static pgprot_t drm_dma_prot(uint32_t map_type, struct vm_area_struct *vma)
static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) static int drm_do_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{ {
struct drm_file *priv = vma->vm_file->private_data; struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
struct drm_map *map = NULL; struct drm_map *map = NULL;
struct drm_map_list *r_list; struct drm_map_list *r_list;
struct drm_hash_item *hash; struct drm_hash_item *hash;
...@@ -207,7 +207,7 @@ static int drm_do_vm_shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ...@@ -207,7 +207,7 @@ static int drm_do_vm_shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
static void drm_vm_shm_close(struct vm_area_struct *vma) static void drm_vm_shm_close(struct vm_area_struct *vma)
{ {
struct drm_file *priv = vma->vm_file->private_data; struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
struct drm_vma_entry *pt, *temp; struct drm_vma_entry *pt, *temp;
struct drm_map *map; struct drm_map *map;
struct drm_map_list *r_list; struct drm_map_list *r_list;
...@@ -286,7 +286,7 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) ...@@ -286,7 +286,7 @@ static void drm_vm_shm_close(struct vm_area_struct *vma)
static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{ {
struct drm_file *priv = vma->vm_file->private_data; struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
struct drm_device_dma *dma = dev->dma; struct drm_device_dma *dma = dev->dma;
unsigned long offset; unsigned long offset;
unsigned long page_nr; unsigned long page_nr;
...@@ -321,7 +321,7 @@ static int drm_do_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ...@@ -321,7 +321,7 @@ static int drm_do_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{ {
struct drm_map *map = (struct drm_map *) vma->vm_private_data; struct drm_map *map = (struct drm_map *) vma->vm_private_data;
struct drm_file *priv = vma->vm_file->private_data; struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
struct drm_sg_mem *entry = dev->sg; struct drm_sg_mem *entry = dev->sg;
unsigned long offset; unsigned long offset;
unsigned long map_offset; unsigned long map_offset;
...@@ -402,7 +402,7 @@ static struct vm_operations_struct drm_vm_sg_ops = { ...@@ -402,7 +402,7 @@ static struct vm_operations_struct drm_vm_sg_ops = {
static void drm_vm_open_locked(struct vm_area_struct *vma) static void drm_vm_open_locked(struct vm_area_struct *vma)
{ {
struct drm_file *priv = vma->vm_file->private_data; struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
struct drm_vma_entry *vma_entry; struct drm_vma_entry *vma_entry;
DRM_DEBUG("0x%08lx,0x%08lx\n", DRM_DEBUG("0x%08lx,0x%08lx\n",
...@@ -420,7 +420,7 @@ static void drm_vm_open_locked(struct vm_area_struct *vma) ...@@ -420,7 +420,7 @@ static void drm_vm_open_locked(struct vm_area_struct *vma)
static void drm_vm_open(struct vm_area_struct *vma) static void drm_vm_open(struct vm_area_struct *vma)
{ {
struct drm_file *priv = vma->vm_file->private_data; struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
drm_vm_open_locked(vma); drm_vm_open_locked(vma);
...@@ -438,7 +438,7 @@ static void drm_vm_open(struct vm_area_struct *vma) ...@@ -438,7 +438,7 @@ static void drm_vm_open(struct vm_area_struct *vma)
static void drm_vm_close(struct vm_area_struct *vma) static void drm_vm_close(struct vm_area_struct *vma)
{ {
struct drm_file *priv = vma->vm_file->private_data; struct drm_file *priv = vma->vm_file->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
struct drm_vma_entry *pt, *temp; struct drm_vma_entry *pt, *temp;
DRM_DEBUG("0x%08lx,0x%08lx\n", DRM_DEBUG("0x%08lx,0x%08lx\n",
...@@ -473,7 +473,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) ...@@ -473,7 +473,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
struct drm_device_dma *dma; struct drm_device_dma *dma;
unsigned long length = vma->vm_end - vma->vm_start; unsigned long length = vma->vm_end - vma->vm_start;
dev = priv->head->dev; dev = priv->minor->dev;
dma = dev->dma; dma = dev->dma;
DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n", DRM_DEBUG("start = 0x%lx, end = 0x%lx, page offset = 0x%lx\n",
vma->vm_start, vma->vm_end, vma->vm_pgoff); vma->vm_start, vma->vm_end, vma->vm_pgoff);
...@@ -543,7 +543,7 @@ EXPORT_SYMBOL(drm_core_get_reg_ofs); ...@@ -543,7 +543,7 @@ EXPORT_SYMBOL(drm_core_get_reg_ofs);
static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
{ {
struct drm_file *priv = filp->private_data; struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
struct drm_map *map = NULL; struct drm_map *map = NULL;
unsigned long offset = 0; unsigned long offset = 0;
struct drm_hash_item *hash; struct drm_hash_item *hash;
...@@ -640,12 +640,12 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) ...@@ -640,12 +640,12 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
/* Don't let this area swap. Change when /* Don't let this area swap. Change when
DRM_KERNEL advisory is supported. */ DRM_KERNEL advisory is supported. */
vma->vm_flags |= VM_RESERVED; vma->vm_flags |= VM_RESERVED;
vma->vm_page_prot = drm_dma_prot(map->type, vma);
break; break;
case _DRM_SCATTER_GATHER: case _DRM_SCATTER_GATHER:
vma->vm_ops = &drm_vm_sg_ops; vma->vm_ops = &drm_vm_sg_ops;
vma->vm_private_data = (void *)map; vma->vm_private_data = (void *)map;
vma->vm_flags |= VM_RESERVED; vma->vm_flags |= VM_RESERVED;
vma->vm_page_prot = drm_dma_prot(map->type, vma);
break; break;
default: default:
return -EINVAL; /* This should never happen. */ return -EINVAL; /* This should never happen. */
...@@ -661,7 +661,7 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) ...@@ -661,7 +661,7 @@ static int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
int drm_mmap(struct file *filp, struct vm_area_struct *vma) int drm_mmap(struct file *filp, struct vm_area_struct *vma)
{ {
struct drm_file *priv = filp->private_data; struct drm_file *priv = filp->private_data;
struct drm_device *dev = priv->head->dev; struct drm_device *dev = priv->minor->dev;
int ret; int ret;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
......
...@@ -94,7 +94,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) ...@@ -94,7 +94,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
drm_i810_buf_priv_t *buf_priv; drm_i810_buf_priv_t *buf_priv;
lock_kernel(); lock_kernel();
dev = priv->head->dev; dev = priv->minor->dev;
dev_priv = dev->dev_private; dev_priv = dev->dev_private;
buf = dev_priv->mmap_buffer; buf = dev_priv->mmap_buffer;
buf_priv = buf->dev_private; buf_priv = buf->dev_private;
...@@ -122,7 +122,7 @@ static const struct file_operations i810_buffer_fops = { ...@@ -122,7 +122,7 @@ static const struct file_operations i810_buffer_fops = {
static int i810_map_buffer(struct drm_buf * buf, struct drm_file *file_priv) static int i810_map_buffer(struct drm_buf * buf, struct drm_file *file_priv)
{ {
struct drm_device *dev = file_priv->head->dev; struct drm_device *dev = file_priv->minor->dev;
drm_i810_buf_priv_t *buf_priv = buf->dev_private; drm_i810_buf_priv_t *buf_priv = buf->dev_private;
drm_i810_private_t *dev_priv = dev->dev_private; drm_i810_private_t *dev_priv = dev->dev_private;
const struct file_operations *old_fops; const struct file_operations *old_fops;
......
...@@ -96,7 +96,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma) ...@@ -96,7 +96,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
drm_i830_buf_priv_t *buf_priv; drm_i830_buf_priv_t *buf_priv;
lock_kernel(); lock_kernel();
dev = priv->head->dev; dev = priv->minor->dev;
dev_priv = dev->dev_private; dev_priv = dev->dev_private;
buf = dev_priv->mmap_buffer; buf = dev_priv->mmap_buffer;
buf_priv = buf->dev_private; buf_priv = buf->dev_private;
...@@ -124,7 +124,7 @@ static const struct file_operations i830_buffer_fops = { ...@@ -124,7 +124,7 @@ static const struct file_operations i830_buffer_fops = {
static int i830_map_buffer(struct drm_buf * buf, struct drm_file *file_priv) static int i830_map_buffer(struct drm_buf * buf, struct drm_file *file_priv)
{ {
struct drm_device *dev = file_priv->head->dev; struct drm_device *dev = file_priv->minor->dev;
drm_i830_buf_priv_t *buf_priv = buf->dev_private; drm_i830_buf_priv_t *buf_priv = buf->dev_private;
drm_i830_private_t *dev_priv = dev->dev_private; drm_i830_private_t *dev_priv = dev->dev_private;
const struct file_operations *old_fops; const struct file_operations *old_fops;
......
...@@ -415,10 +415,13 @@ static void i915_emit_breadcrumb(struct drm_device *dev) ...@@ -415,10 +415,13 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
RING_LOCALS; RING_LOCALS;
dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter; if (++dev_priv->counter > BREADCRUMB_MASK) {
dev_priv->counter = 1;
DRM_DEBUG("Breadcrumb counter wrapped around\n");
}
if (dev_priv->counter > 0x7FFFFFFFUL) if (dev_priv->sarea_priv)
dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1; dev_priv->sarea_priv->last_enqueue = dev_priv->counter;
BEGIN_LP_RING(4); BEGIN_LP_RING(4);
OUT_RING(CMD_STORE_DWORD_IDX); OUT_RING(CMD_STORE_DWORD_IDX);
...@@ -428,6 +431,26 @@ static void i915_emit_breadcrumb(struct drm_device *dev) ...@@ -428,6 +431,26 @@ static void i915_emit_breadcrumb(struct drm_device *dev)
ADVANCE_LP_RING(); ADVANCE_LP_RING();
} }
int i915_emit_mi_flush(struct drm_device *dev, uint32_t flush)
{
drm_i915_private_t *dev_priv = dev->dev_private;
uint32_t flush_cmd = CMD_MI_FLUSH;
RING_LOCALS;
flush_cmd |= flush;
i915_kernel_lost_context(dev);
BEGIN_LP_RING(4);
OUT_RING(flush_cmd);
OUT_RING(0);
OUT_RING(0);
OUT_RING(0);
ADVANCE_LP_RING();
return 0;
}
static int i915_dispatch_cmdbuffer(struct drm_device * dev, static int i915_dispatch_cmdbuffer(struct drm_device * dev,
drm_i915_cmdbuffer_t * cmd) drm_i915_cmdbuffer_t * cmd)
{ {
...@@ -511,52 +534,74 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev, ...@@ -511,52 +534,74 @@ static int i915_dispatch_batchbuffer(struct drm_device * dev,
return 0; return 0;
} }
static int i915_dispatch_flip(struct drm_device * dev) static void i915_do_dispatch_flip(struct drm_device * dev, int plane, int sync)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
u32 num_pages, current_page, next_page, dspbase;
int shift = 2 * plane, x, y;
RING_LOCALS; RING_LOCALS;
DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n", /* Calculate display base offset */
__FUNCTION__, num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2;
dev_priv->current_page, current_page = (dev_priv->sarea_priv->pf_current_page >> shift) & 0x3;
dev_priv->sarea_priv->pf_current_page); next_page = (current_page + 1) % num_pages;
i915_kernel_lost_context(dev); switch (next_page) {
default:
BEGIN_LP_RING(2); case 0:
OUT_RING(INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE); dspbase = dev_priv->sarea_priv->front_offset;
OUT_RING(0); break;
ADVANCE_LP_RING(); case 1:
dspbase = dev_priv->sarea_priv->back_offset;
break;
case 2:
dspbase = dev_priv->sarea_priv->third_offset;
break;
}
BEGIN_LP_RING(6); if (plane == 0) {
OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); x = dev_priv->sarea_priv->planeA_x;
OUT_RING(0); y = dev_priv->sarea_priv->planeA_y;
if (dev_priv->current_page == 0) {
OUT_RING(dev_priv->back_offset);
dev_priv->current_page = 1;
} else { } else {
OUT_RING(dev_priv->front_offset); x = dev_priv->sarea_priv->planeB_x;
dev_priv->current_page = 0; y = dev_priv->sarea_priv->planeB_y;
} }
OUT_RING(0);
ADVANCE_LP_RING();
BEGIN_LP_RING(2); dspbase += (y * dev_priv->sarea_priv->pitch + x) * dev_priv->cpp;
OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP);
OUT_RING(0);
ADVANCE_LP_RING();
dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; DRM_DEBUG("plane=%d current_page=%d dspbase=0x%x\n", plane, current_page,
dspbase);
BEGIN_LP_RING(4); BEGIN_LP_RING(4);
OUT_RING(CMD_STORE_DWORD_IDX); OUT_RING(sync ? 0 :
OUT_RING(20); (MI_WAIT_FOR_EVENT | (plane ? MI_WAIT_FOR_PLANE_B_FLIP :
OUT_RING(dev_priv->counter); MI_WAIT_FOR_PLANE_A_FLIP)));
OUT_RING(0); OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (sync ? 0 : ASYNC_FLIP) |
(plane ? DISPLAY_PLANE_B : DISPLAY_PLANE_A));
OUT_RING(dev_priv->sarea_priv->pitch * dev_priv->cpp);
OUT_RING(dspbase);
ADVANCE_LP_RING(); ADVANCE_LP_RING();
dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; dev_priv->sarea_priv->pf_current_page &= ~(0x3 << shift);
return 0; dev_priv->sarea_priv->pf_current_page |= next_page << shift;
}
void i915_dispatch_flip(struct drm_device * dev, int planes, int sync)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int i;
DRM_DEBUG("planes=0x%x pfCurrentPage=%d\n",
planes, dev_priv->sarea_priv->pf_current_page);
i915_emit_mi_flush(dev, MI_READ_FLUSH | MI_EXE_FLUSH);
for (i = 0; i < 2; i++)
if (planes & (1 << i))
i915_do_dispatch_flip(dev, i, sync);
i915_emit_breadcrumb(dev);
} }
static int i915_quiescent(struct drm_device * dev) static int i915_quiescent(struct drm_device * dev)
...@@ -579,7 +624,6 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, ...@@ -579,7 +624,6 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 *hw_status = dev_priv->hw_status_page;
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
dev_priv->sarea_priv; dev_priv->sarea_priv;
drm_i915_batchbuffer_t *batch = data; drm_i915_batchbuffer_t *batch = data;
...@@ -602,7 +646,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, ...@@ -602,7 +646,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data,
ret = i915_dispatch_batchbuffer(dev, batch); ret = i915_dispatch_batchbuffer(dev, batch);
sarea_priv->last_dispatch = (int)hw_status[5]; sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
return ret; return ret;
} }
...@@ -610,7 +654,6 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, ...@@ -610,7 +654,6 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 *hw_status = dev_priv->hw_status_page;
drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
dev_priv->sarea_priv; dev_priv->sarea_priv;
drm_i915_cmdbuffer_t *cmdbuf = data; drm_i915_cmdbuffer_t *cmdbuf = data;
...@@ -635,18 +678,51 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, ...@@ -635,18 +678,51 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
return ret; return ret;
} }
sarea_priv->last_dispatch = (int)hw_status[5]; sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
return 0;
}
static int i915_do_cleanup_pageflip(struct drm_device * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int i, planes, num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2;
DRM_DEBUG("\n");
for (i = 0, planes = 0; i < 2; i++)
if (dev_priv->sarea_priv->pf_current_page & (0x3 << (2 * i))) {
dev_priv->sarea_priv->pf_current_page =
(dev_priv->sarea_priv->pf_current_page &
~(0x3 << (2 * i))) | ((num_pages - 1) << (2 * i));
planes |= 1 << i;
}
if (planes)
i915_dispatch_flip(dev, planes, 0);
return 0; return 0;
} }
static int i915_flip_bufs(struct drm_device *dev, void *data, static int i915_flip_bufs(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
DRM_DEBUG("%s\n", __FUNCTION__); drm_i915_flip_t *param = data;
DRM_DEBUG("\n");
LOCK_TEST_WITH_RETURN(dev, file_priv); LOCK_TEST_WITH_RETURN(dev, file_priv);
return i915_dispatch_flip(dev); /* This is really planes */
if (param->pipes & ~0x3) {
DRM_ERROR("Invalid planes 0x%x, only <= 0x3 is valid\n",
param->pipes);
return -EINVAL;
}
i915_dispatch_flip(dev, param->pipes, 0);
return 0;
} }
static int i915_getparam(struct drm_device *dev, void *data, static int i915_getparam(struct drm_device *dev, void *data,
...@@ -807,6 +883,8 @@ void i915_driver_lastclose(struct drm_device * dev) ...@@ -807,6 +883,8 @@ void i915_driver_lastclose(struct drm_device * dev)
if (!dev_priv) if (!dev_priv)
return; return;
if (drm_getsarea(dev) && dev_priv->sarea_priv)
i915_do_cleanup_pageflip(dev);
if (dev_priv->agp_heap) if (dev_priv->agp_heap)
i915_mem_takedown(&(dev_priv->agp_heap)); i915_mem_takedown(&(dev_priv->agp_heap));
......
...@@ -105,14 +105,29 @@ typedef struct _drm_i915_sarea { ...@@ -105,14 +105,29 @@ typedef struct _drm_i915_sarea {
unsigned int rotated_tiled; unsigned int rotated_tiled;
unsigned int rotated2_tiled; unsigned int rotated2_tiled;
int pipeA_x; int planeA_x;
int pipeA_y; int planeA_y;
int pipeA_w; int planeA_w;
int pipeA_h; int planeA_h;
int pipeB_x; int planeB_x;
int pipeB_y; int planeB_y;
int pipeB_w; int planeB_w;
int pipeB_h; int planeB_h;
/* Triple buffering */
drm_handle_t third_handle;
int third_offset;
int third_size;
unsigned int third_tiled;
/* buffer object handles for the static buffers. May change
* over the lifetime of the client, though it doesn't in our current
* implementation.
*/
unsigned int front_bo_handle;
unsigned int back_bo_handle;
unsigned int third_bo_handle;
unsigned int depth_bo_handle;
} drm_i915_sarea_t; } drm_i915_sarea_t;
/* Flags for perf_boxes /* Flags for perf_boxes
...@@ -146,7 +161,7 @@ typedef struct _drm_i915_sarea { ...@@ -146,7 +161,7 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
#define DRM_IOCTL_I915_FLIP DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP) #define DRM_IOCTL_I915_FLIP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FLIP, drm_i915_flip_t)
#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t) #define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t)
#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t) #define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t)
#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t) #define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t)
...@@ -161,6 +176,18 @@ typedef struct _drm_i915_sarea { ...@@ -161,6 +176,18 @@ typedef struct _drm_i915_sarea {
#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) #define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) #define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t)
/* Asynchronous page flipping:
*/
typedef struct drm_i915_flip {
/*
* This is really talking about planes, and we could rename it
* except for the fact that some of the duplicated i915_drm.h files
* out there check for HAVE_I915_FLIP and so might pick up this
* version.
*/
int pipes;
} drm_i915_flip_t;
/* Allow drivers to submit batchbuffers directly to hardware, relying /* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware. * on the security mechanisms provided by hardware.
*/ */
......
...@@ -533,8 +533,7 @@ static struct drm_driver driver = { ...@@ -533,8 +533,7 @@ static struct drm_driver driver = {
*/ */
.driver_features = .driver_features =
DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
DRIVER_IRQ_VBL2,
.load = i915_driver_load, .load = i915_driver_load,
.unload = i915_driver_unload, .unload = i915_driver_unload,
.lastclose = i915_driver_lastclose, .lastclose = i915_driver_lastclose,
...@@ -542,8 +541,9 @@ static struct drm_driver driver = { ...@@ -542,8 +541,9 @@ static struct drm_driver driver = {
.suspend = i915_suspend, .suspend = i915_suspend,
.resume = i915_resume, .resume = i915_resume,
.device_is_agp = i915_driver_device_is_agp, .device_is_agp = i915_driver_device_is_agp,
.vblank_wait = i915_driver_vblank_wait, .get_vblank_counter = i915_get_vblank_counter,
.vblank_wait2 = i915_driver_vblank_wait2, .enable_vblank = i915_enable_vblank,
.disable_vblank = i915_disable_vblank,
.irq_preinstall = i915_driver_irq_preinstall, .irq_preinstall = i915_driver_irq_preinstall,
.irq_postinstall = i915_driver_irq_postinstall, .irq_postinstall = i915_driver_irq_postinstall,
.irq_uninstall = i915_driver_irq_uninstall, .irq_uninstall = i915_driver_irq_uninstall,
......
...@@ -76,8 +76,9 @@ struct mem_block { ...@@ -76,8 +76,9 @@ struct mem_block {
typedef struct _drm_i915_vbl_swap { typedef struct _drm_i915_vbl_swap {
struct list_head head; struct list_head head;
drm_drawable_t drw_id; drm_drawable_t drw_id;
unsigned int pipe; unsigned int plane;
unsigned int sequence; unsigned int sequence;
int flip;
} drm_i915_vbl_swap_t; } drm_i915_vbl_swap_t;
typedef struct drm_i915_private { typedef struct drm_i915_private {
...@@ -90,7 +91,7 @@ typedef struct drm_i915_private { ...@@ -90,7 +91,7 @@ typedef struct drm_i915_private {
drm_dma_handle_t *status_page_dmah; drm_dma_handle_t *status_page_dmah;
void *hw_status_page; void *hw_status_page;
dma_addr_t dma_status_page; dma_addr_t dma_status_page;
unsigned long counter; uint32_t counter;
unsigned int status_gfx_addr; unsigned int status_gfx_addr;
drm_local_map_t hws_map; drm_local_map_t hws_map;
...@@ -103,13 +104,18 @@ typedef struct drm_i915_private { ...@@ -103,13 +104,18 @@ typedef struct drm_i915_private {
wait_queue_head_t irq_queue; wait_queue_head_t irq_queue;
atomic_t irq_received; atomic_t irq_received;
atomic_t irq_emitted; atomic_t irq_emited;
int tex_lru_log_granularity; int tex_lru_log_granularity;
int allow_batchbuffer; int allow_batchbuffer;
struct mem_block *agp_heap; struct mem_block *agp_heap;
unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
int vblank_pipe; int vblank_pipe;
spinlock_t user_irq_lock;
int user_irq_refcount;
int fence_irq_on;
uint32_t irq_enable_reg;
int irq_enabled;
spinlock_t swaps_lock; spinlock_t swaps_lock;
drm_i915_vbl_swap_t vbl_swaps; drm_i915_vbl_swap_t vbl_swaps;
...@@ -216,7 +222,7 @@ extern void i915_driver_preclose(struct drm_device *dev, ...@@ -216,7 +222,7 @@ extern void i915_driver_preclose(struct drm_device *dev,
extern int i915_driver_device_is_agp(struct drm_device * dev); extern int i915_driver_device_is_agp(struct drm_device * dev);
extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg); unsigned long arg);
extern void i915_dispatch_flip(struct drm_device * dev, int pipes, int sync);
/* i915_irq.c */ /* i915_irq.c */
extern int i915_irq_emit(struct drm_device *dev, void *data, extern int i915_irq_emit(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
...@@ -227,7 +233,7 @@ extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequenc ...@@ -227,7 +233,7 @@ extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequenc
extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence); extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence);
extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);
extern void i915_driver_irq_preinstall(struct drm_device * dev); extern void i915_driver_irq_preinstall(struct drm_device * dev);
extern void i915_driver_irq_postinstall(struct drm_device * dev); extern int i915_driver_irq_postinstall(struct drm_device * dev);
extern void i915_driver_irq_uninstall(struct drm_device * dev); extern void i915_driver_irq_uninstall(struct drm_device * dev);
extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, extern int i915_vblank_pipe_set(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
...@@ -235,6 +241,9 @@ extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, ...@@ -235,6 +241,9 @@ extern int i915_vblank_pipe_get(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
extern int i915_vblank_swap(struct drm_device *dev, void *data, extern int i915_vblank_swap(struct drm_device *dev, void *data,
struct drm_file *file_priv); struct drm_file *file_priv);
extern int i915_enable_vblank(struct drm_device *dev, int crtc);
extern void i915_disable_vblank(struct drm_device *dev, int crtc);
extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc);
/* i915_mem.c */ /* i915_mem.c */
extern int i915_mem_alloc(struct drm_device *dev, void *data, extern int i915_mem_alloc(struct drm_device *dev, void *data,
...@@ -379,21 +388,91 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); ...@@ -379,21 +388,91 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
/* Interrupt bits: /* Interrupt bits:
*/ */
#define USER_INT_FLAG (1<<1) #define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18)
#define VSYNC_PIPEB_FLAG (1<<5) #define I915_DISPLAY_PORT_INTERRUPT (1<<17)
#define VSYNC_PIPEA_FLAG (1<<7) #define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15)
#define HWB_OOM_FLAG (1<<13) /* binner out of memory */ #define I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT (1<<14)
#define I915_HWB_OOM_INTERRUPT (1<<13) /* binner out of memory */
#define I915_SYNC_STATUS_INTERRUPT (1<<12)
#define I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT (1<<11)
#define I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT (1<<10)
#define I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT (1<<9)
#define I915_DISPLAY_PLANE_C_FLIP_PENDING_INTERRUPT (1<<8)
#define I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT (1<<7)
#define I915_DISPLAY_PIPE_A_EVENT_INTERRUPT (1<<6)
#define I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT (1<<5)
#define I915_DISPLAY_PIPE_B_EVENT_INTERRUPT (1<<4)
#define I915_DEBUG_INTERRUPT (1<<2)
#define I915_USER_INTERRUPT (1<<1)
#define I915REG_HWSTAM 0x02098 #define I915REG_HWSTAM 0x02098
#define I915REG_INT_IDENTITY_R 0x020a4 #define I915REG_INT_IDENTITY_R 0x020a4
#define I915REG_INT_MASK_R 0x020a8 #define I915REG_INT_MASK_R 0x020a8
#define I915REG_INT_ENABLE_R 0x020a0 #define I915REG_INT_ENABLE_R 0x020a0
#define I915REG_INSTPM 0x020c0
#define PIPEADSL 0x70000
#define PIPEBDSL 0x71000
#define I915REG_PIPEASTAT 0x70024 #define I915REG_PIPEASTAT 0x70024
#define I915REG_PIPEBSTAT 0x71024 #define I915REG_PIPEBSTAT 0x71024
/*
* The two pipe frame counter registers are not synchronized, so
* reading a stable value is somewhat tricky. The following code
* should work:
*
* do {
* high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >>
* PIPE_FRAME_HIGH_SHIFT;
* low1 = ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >>
* PIPE_FRAME_LOW_SHIFT);
* high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >>
* PIPE_FRAME_HIGH_SHIFT);
* } while (high1 != high2);
* frame = (high1 << 8) | low1;
*/
#define PIPEAFRAMEHIGH 0x70040
#define PIPEBFRAMEHIGH 0x71040
#define PIPE_FRAME_HIGH_MASK 0x0000ffff
#define PIPE_FRAME_HIGH_SHIFT 0
#define PIPEAFRAMEPIXEL 0x70044
#define PIPEBFRAMEPIXEL 0x71044
#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) #define PIPE_FRAME_LOW_MASK 0xff000000
#define I915_VBLANK_CLEAR (1UL<<1) #define PIPE_FRAME_LOW_SHIFT 24
/*
* Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register
* and is 24 bits wide.
*/
#define PIPE_PIXEL_MASK 0x00ffffff
#define PIPE_PIXEL_SHIFT 0
#define I915_FIFO_UNDERRUN_STATUS (1UL<<31)
#define I915_CRC_ERROR_ENABLE (1UL<<29)
#define I915_CRC_DONE_ENABLE (1UL<<28)
#define I915_GMBUS_EVENT_ENABLE (1UL<<27)
#define I915_VSYNC_INTERRUPT_ENABLE (1UL<<25)
#define I915_DISPLAY_LINE_COMPARE_ENABLE (1UL<<24)
#define I915_DPST_EVENT_ENABLE (1UL<<23)
#define I915_LEGACY_BLC_EVENT_ENABLE (1UL<<22)
#define I915_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21)
#define I915_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20)
#define I915_START_VBLANK_INTERRUPT_ENABLE (1UL<<18) /* 965 or later */
#define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17)
#define I915_OVERLAY_UPDATED_ENABLE (1UL<<16)
#define I915_CRC_ERROR_INTERRUPT_STATUS (1UL<<13)
#define I915_CRC_DONE_INTERRUPT_STATUS (1UL<<12)
#define I915_GMBUS_INTERRUPT_STATUS (1UL<<11)
#define I915_VSYNC_INTERRUPT_STATUS (1UL<<9)
#define I915_DISPLAY_LINE_COMPARE_STATUS (1UL<<8)
#define I915_DPST_EVENT_STATUS (1UL<<7)
#define I915_LEGACY_BLC_EVENT_STATUS (1UL<<6)
#define I915_ODD_FIELD_INTERRUPT_STATUS (1UL<<5)
#define I915_EVEN_FIELD_INTERRUPT_STATUS (1UL<<4)
#define I915_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */
#define I915_VBLANK_INTERRUPT_STATUS (1UL<<1)
#define I915_OVERLAY_UPDATED_STATUS (1UL<<0)
#define SRX_INDEX 0x3c4 #define SRX_INDEX 0x3c4
#define SRX_DATA 0x3c5 #define SRX_DATA 0x3c5
...@@ -566,6 +645,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); ...@@ -566,6 +645,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
#define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6) #define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6)
#define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) #define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21)
#define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) #define XY_SRC_COPY_BLT_WRITE_RGB (1<<20)
#define XY_SRC_COPY_BLT_SRC_TILED (1<<15)
#define XY_SRC_COPY_BLT_DST_TILED (1<<11)
#define MI_BATCH_BUFFER ((0x30<<23)|1) #define MI_BATCH_BUFFER ((0x30<<23)|1)
#define MI_BATCH_BUFFER_START (0x31<<23) #define MI_BATCH_BUFFER_START (0x31<<23)
......
...@@ -37,6 +37,109 @@ ...@@ -37,6 +37,109 @@
#define MAX_NOPID ((u32)~0) #define MAX_NOPID ((u32)~0)
/**
* i915_get_pipe - return the the pipe associated with a given plane
* @dev: DRM device
* @plane: plane to look for
*
* The Intel Mesa & 2D drivers call the vblank routines with a plane number
* rather than a pipe number, since they may not always be equal. This routine
* maps the given @plane back to a pipe number.
*/
static int
i915_get_pipe(struct drm_device *dev, int plane)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 dspcntr;
dspcntr = plane ? I915_READ(DSPBCNTR) : I915_READ(DSPACNTR);
return dspcntr & DISPPLANE_SEL_PIPE_MASK ? 1 : 0;
}
/**
* i915_get_plane - return the the plane associated with a given pipe
* @dev: DRM device
* @pipe: pipe to look for
*
* The Intel Mesa & 2D drivers call the vblank routines with a plane number
* rather than a plane number, since they may not always be equal. This routine
* maps the given @pipe back to a plane number.
*/
static int
i915_get_plane(struct drm_device *dev, int pipe)
{
if (i915_get_pipe(dev, 0) == pipe)
return 0;
return 1;
}
/**
* i915_pipe_enabled - check if a pipe is enabled
* @dev: DRM device
* @pipe: pipe to check
*
* Reading certain registers when the pipe is disabled can hang the chip.
* Use this routine to make sure the PLL is running and the pipe is active
* before reading such registers if unsure.
*/
static int
i915_pipe_enabled(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF;
if (I915_READ(pipeconf) & PIPEACONF_ENABLE)
return 1;
return 0;
}
/**
* Emit a synchronous flip.
*
* This function must be called with the drawable spinlock held.
*/
static void
i915_dispatch_vsync_flip(struct drm_device *dev, struct drm_drawable_info *drw,
int plane)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
u16 x1, y1, x2, y2;
int pf_planes = 1 << plane;
/* If the window is visible on the other plane, we have to flip on that
* plane as well.
*/
if (plane == 1) {
x1 = sarea_priv->planeA_x;
y1 = sarea_priv->planeA_y;
x2 = x1 + sarea_priv->planeA_w;
y2 = y1 + sarea_priv->planeA_h;
} else {
x1 = sarea_priv->planeB_x;
y1 = sarea_priv->planeB_y;
x2 = x1 + sarea_priv->planeB_w;
y2 = y1 + sarea_priv->planeB_h;
}
if (x2 > 0 && y2 > 0) {
int i, num_rects = drw->num_rects;
struct drm_clip_rect *rect = drw->rects;
for (i = 0; i < num_rects; i++)
if (!(rect[i].x1 >= x2 || rect[i].y1 >= y2 ||
rect[i].x2 <= x1 || rect[i].y2 <= y1)) {
pf_planes = 0x3;
break;
}
}
i915_dispatch_flip(dev, pf_planes, 1);
}
/** /**
* Emit blits for scheduled buffer swaps. * Emit blits for scheduled buffer swaps.
* *
...@@ -45,40 +148,59 @@ ...@@ -45,40 +148,59 @@
static void i915_vblank_tasklet(struct drm_device *dev) static void i915_vblank_tasklet(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long irqflags;
struct list_head *list, *tmp, hits, *hit; struct list_head *list, *tmp, hits, *hit;
int nhits, nrects, slice[2], upper[2], lower[2], i; int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages;
unsigned counter[2] = { atomic_read(&dev->vbl_received), unsigned counter[2];
atomic_read(&dev->vbl_received2) };
struct drm_drawable_info *drw; struct drm_drawable_info *drw;
drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;
u32 cpp = dev_priv->cpp; u32 cpp = dev_priv->cpp, offsets[3];
u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD |
XY_SRC_COPY_BLT_WRITE_ALPHA | XY_SRC_COPY_BLT_WRITE_ALPHA |
XY_SRC_COPY_BLT_WRITE_RGB) XY_SRC_COPY_BLT_WRITE_RGB)
: XY_SRC_COPY_BLT_CMD; : XY_SRC_COPY_BLT_CMD;
u32 pitchropcpp = (sarea_priv->pitch * cpp) | (0xcc << 16) | u32 src_pitch = sarea_priv->pitch * cpp;
(cpp << 23) | (1 << 24); u32 dst_pitch = sarea_priv->pitch * cpp;
/* COPY rop (0xcc), map cpp to magic color depth constants */
u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24);
RING_LOCALS; RING_LOCALS;
if (sarea_priv->front_tiled) {
cmd |= XY_SRC_COPY_BLT_DST_TILED;
dst_pitch >>= 2;
}
if (sarea_priv->back_tiled) {
cmd |= XY_SRC_COPY_BLT_SRC_TILED;
src_pitch >>= 2;
}
counter[0] = drm_vblank_count(dev, 0);
counter[1] = drm_vblank_count(dev, 1);
DRM_DEBUG("\n"); DRM_DEBUG("\n");
INIT_LIST_HEAD(&hits); INIT_LIST_HEAD(&hits);
nhits = nrects = 0; nhits = nrects = 0;
spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); /* No irqsave/restore necessary. This tasklet may be run in an
* interrupt context or normal context, but we don't have to worry
* about getting interrupted by something acquiring the lock, because
* we are the interrupt context thing that acquires the lock.
*/
spin_lock(&dev_priv->swaps_lock);
/* Find buffer swaps scheduled for this vertical blank */ /* Find buffer swaps scheduled for this vertical blank */
list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {
drm_i915_vbl_swap_t *vbl_swap = drm_i915_vbl_swap_t *vbl_swap =
list_entry(list, drm_i915_vbl_swap_t, head); list_entry(list, drm_i915_vbl_swap_t, head);
int pipe = i915_get_pipe(dev, vbl_swap->plane);
if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23)) if ((counter[pipe] - vbl_swap->sequence) > (1<<23))
continue; continue;
list_del(list); list_del(list);
dev_priv->swaps_pending--; dev_priv->swaps_pending--;
drm_vblank_put(dev, pipe);
spin_unlock(&dev_priv->swaps_lock); spin_unlock(&dev_priv->swaps_lock);
spin_lock(&dev->drw_lock); spin_lock(&dev->drw_lock);
...@@ -116,33 +238,23 @@ static void i915_vblank_tasklet(struct drm_device *dev) ...@@ -116,33 +238,23 @@ static void i915_vblank_tasklet(struct drm_device *dev)
spin_lock(&dev_priv->swaps_lock); spin_lock(&dev_priv->swaps_lock);
} }
if (nhits == 0) {
spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
return;
}
spin_unlock(&dev_priv->swaps_lock); spin_unlock(&dev_priv->swaps_lock);
i915_kernel_lost_context(dev); if (nhits == 0)
return;
BEGIN_LP_RING(6);
OUT_RING(GFX_OP_DRAWRECT_INFO);
OUT_RING(0);
OUT_RING(0);
OUT_RING(sarea_priv->width | sarea_priv->height << 16);
OUT_RING(sarea_priv->width | sarea_priv->height << 16);
OUT_RING(0);
ADVANCE_LP_RING();
sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; i915_kernel_lost_context(dev);
upper[0] = upper[1] = 0; upper[0] = upper[1] = 0;
slice[0] = max(sarea_priv->pipeA_h / nhits, 1); slice[0] = max(sarea_priv->planeA_h / nhits, 1);
slice[1] = max(sarea_priv->pipeB_h / nhits, 1); slice[1] = max(sarea_priv->planeB_h / nhits, 1);
lower[0] = sarea_priv->pipeA_y + slice[0]; lower[0] = sarea_priv->planeA_y + slice[0];
lower[1] = sarea_priv->pipeB_y + slice[0]; lower[1] = sarea_priv->planeB_y + slice[0];
offsets[0] = sarea_priv->front_offset;
offsets[1] = sarea_priv->back_offset;
offsets[2] = sarea_priv->third_offset;
num_pages = sarea_priv->third_handle ? 3 : 2;
spin_lock(&dev->drw_lock); spin_lock(&dev->drw_lock);
...@@ -154,6 +266,8 @@ static void i915_vblank_tasklet(struct drm_device *dev) ...@@ -154,6 +266,8 @@ static void i915_vblank_tasklet(struct drm_device *dev)
for (i = 0; i++ < nhits; for (i = 0; i++ < nhits;
upper[0] = lower[0], lower[0] += slice[0], upper[0] = lower[0], lower[0] += slice[0],
upper[1] = lower[1], lower[1] += slice[1]) { upper[1] = lower[1], lower[1] += slice[1]) {
int init_drawrect = 1;
if (i == nhits) if (i == nhits)
lower[0] = lower[1] = sarea_priv->height; lower[0] = lower[1] = sarea_priv->height;
...@@ -161,7 +275,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) ...@@ -161,7 +275,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)
drm_i915_vbl_swap_t *swap_hit = drm_i915_vbl_swap_t *swap_hit =
list_entry(hit, drm_i915_vbl_swap_t, head); list_entry(hit, drm_i915_vbl_swap_t, head);
struct drm_clip_rect *rect; struct drm_clip_rect *rect;
int num_rects, pipe; int num_rects, plane, front, back;
unsigned short top, bottom; unsigned short top, bottom;
drw = drm_get_drawable_info(dev, swap_hit->drw_id); drw = drm_get_drawable_info(dev, swap_hit->drw_id);
...@@ -169,10 +283,50 @@ static void i915_vblank_tasklet(struct drm_device *dev) ...@@ -169,10 +283,50 @@ static void i915_vblank_tasklet(struct drm_device *dev)
if (!drw) if (!drw)
continue; continue;
plane = swap_hit->plane;
if (swap_hit->flip) {
i915_dispatch_vsync_flip(dev, drw, plane);
continue;
}
if (init_drawrect) {
int width = sarea_priv->width;
int height = sarea_priv->height;
if (IS_I965G(dev)) {
BEGIN_LP_RING(4);
OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
OUT_RING(0);
OUT_RING(((width - 1) & 0xffff) | ((height - 1) << 16));
OUT_RING(0);
ADVANCE_LP_RING();
} else {
BEGIN_LP_RING(6);
OUT_RING(GFX_OP_DRAWRECT_INFO);
OUT_RING(0);
OUT_RING(0);
OUT_RING(((width - 1) & 0xffff) | ((height - 1) << 16));
OUT_RING(0);
OUT_RING(0);
ADVANCE_LP_RING();
}
sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT;
init_drawrect = 0;
}
rect = drw->rects; rect = drw->rects;
pipe = swap_hit->pipe; top = upper[plane];
top = upper[pipe]; bottom = lower[plane];
bottom = lower[pipe];
front = (dev_priv->sarea_priv->pf_current_page >>
(2 * plane)) & 0x3;
back = (front + 1) % num_pages;
for (num_rects = drw->num_rects; num_rects--; rect++) { for (num_rects = drw->num_rects; num_rects--; rect++) {
int y1 = max(rect->y1, top); int y1 = max(rect->y1, top);
...@@ -184,20 +338,20 @@ static void i915_vblank_tasklet(struct drm_device *dev) ...@@ -184,20 +338,20 @@ static void i915_vblank_tasklet(struct drm_device *dev)
BEGIN_LP_RING(8); BEGIN_LP_RING(8);
OUT_RING(cmd); OUT_RING(cmd);
OUT_RING(pitchropcpp); OUT_RING(ropcpp | dst_pitch);
OUT_RING((y1 << 16) | rect->x1); OUT_RING((y1 << 16) | rect->x1);
OUT_RING((y2 << 16) | rect->x2); OUT_RING((y2 << 16) | rect->x2);
OUT_RING(sarea_priv->front_offset); OUT_RING(offsets[front]);
OUT_RING((y1 << 16) | rect->x1); OUT_RING((y1 << 16) | rect->x1);
OUT_RING(pitchropcpp & 0xffff); OUT_RING(src_pitch);
OUT_RING(sarea_priv->back_offset); OUT_RING(offsets[back]);
ADVANCE_LP_RING(); ADVANCE_LP_RING();
} }
} }
} }
spin_unlock_irqrestore(&dev->drw_lock, irqflags); spin_unlock(&dev->drw_lock);
list_for_each_safe(hit, tmp, &hits) { list_for_each_safe(hit, tmp, &hits) {
drm_i915_vbl_swap_t *swap_hit = drm_i915_vbl_swap_t *swap_hit =
...@@ -209,67 +363,112 @@ static void i915_vblank_tasklet(struct drm_device *dev) ...@@ -209,67 +363,112 @@ static void i915_vblank_tasklet(struct drm_device *dev)
} }
} }
u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
unsigned long high_frame;
unsigned long low_frame;
u32 high1, high2, low, count;
int pipe;
pipe = i915_get_pipe(dev, plane);
high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH;
low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
if (!i915_pipe_enabled(dev, pipe)) {
printk(KERN_ERR "trying to get vblank count for disabled "
"pipe %d\n", pipe);
return 0;
}
/*
* High & low register fields aren't synchronized, so make sure
* we get a low value that's stable across two reads of the high
* register.
*/
do {
high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
PIPE_FRAME_HIGH_SHIFT);
low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
PIPE_FRAME_LOW_SHIFT);
high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
PIPE_FRAME_HIGH_SHIFT);
} while (high1 != high2);
count = (high1 << 8) | low;
/* count may be reset by other driver(e.g. 2D driver),
we have no way to know if it is wrapped or resetted
when count is zero. do a rough guess.
*/
if (count == 0 && dev->last_vblank[pipe] < dev->max_vblank_count/2)
dev->last_vblank[pipe] = 0;
return count;
}
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{ {
struct drm_device *dev = (struct drm_device *) arg; struct drm_device *dev = (struct drm_device *) arg;
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u16 temp; u32 iir;
u32 pipea_stats, pipeb_stats; u32 pipea_stats, pipeb_stats;
int vblank = 0;
pipea_stats = I915_READ(I915REG_PIPEASTAT);
pipeb_stats = I915_READ(I915REG_PIPEBSTAT); iir = I915_READ(I915REG_INT_IDENTITY_R);
if (iir == 0) {
temp = I915_READ16(I915REG_INT_IDENTITY_R); DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n",
iir,
temp &= (USER_INT_FLAG | VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG); I915_READ(I915REG_INT_MASK_R),
I915_READ(I915REG_INT_ENABLE_R),
DRM_DEBUG("%s flag=%08x\n", __FUNCTION__, temp); I915_READ(I915REG_PIPEASTAT),
I915_READ(I915REG_PIPEBSTAT));
if (temp == 0)
return IRQ_NONE; return IRQ_NONE;
}
I915_WRITE16(I915REG_INT_IDENTITY_R, temp); /*
(void) I915_READ16(I915REG_INT_IDENTITY_R); * Clear the PIPE(A|B)STAT regs before the IIR otherwise
DRM_READMEMORYBARRIER(); * we may get extra interrupts.
*/
dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
pipea_stats = I915_READ(I915REG_PIPEASTAT);
if (pipea_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
I915_VBLANK_INTERRUPT_STATUS))
{
vblank++;
drm_handle_vblank(dev, i915_get_plane(dev, 0));
}
I915_WRITE(I915REG_PIPEASTAT, pipea_stats);
}
if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
pipeb_stats = I915_READ(I915REG_PIPEBSTAT);
if (pipeb_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
I915_VBLANK_INTERRUPT_STATUS))
{
vblank++;
drm_handle_vblank(dev, i915_get_plane(dev, 1));
}
I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats);
}
if (temp & USER_INT_FLAG) if (dev_priv->sarea_priv)
DRM_WAKEUP(&dev_priv->irq_queue); dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { I915_WRITE(I915REG_INT_IDENTITY_R, iir);
int vblank_pipe = dev_priv->vblank_pipe; (void) I915_READ(I915REG_INT_IDENTITY_R); /* Flush posted write */
if ((vblank_pipe &
(DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B))
== (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) {
if (temp & VSYNC_PIPEA_FLAG)
atomic_inc(&dev->vbl_received);
if (temp & VSYNC_PIPEB_FLAG)
atomic_inc(&dev->vbl_received2);
} else if (((temp & VSYNC_PIPEA_FLAG) &&
(vblank_pipe & DRM_I915_VBLANK_PIPE_A)) ||
((temp & VSYNC_PIPEB_FLAG) &&
(vblank_pipe & DRM_I915_VBLANK_PIPE_B)))
atomic_inc(&dev->vbl_received);
DRM_WAKEUP(&dev->vbl_queue);
drm_vbl_send_signals(dev);
if (iir & I915_USER_INTERRUPT) {
DRM_WAKEUP(&dev_priv->irq_queue);
}
if (vblank) {
if (dev_priv->swaps_pending > 0) if (dev_priv->swaps_pending > 0)
drm_locked_tasklet(dev, i915_vblank_tasklet); drm_locked_tasklet(dev, i915_vblank_tasklet);
I915_WRITE(I915REG_PIPEASTAT,
pipea_stats|I915_VBLANK_INTERRUPT_ENABLE|
I915_VBLANK_CLEAR);
I915_WRITE(I915REG_PIPEBSTAT,
pipeb_stats|I915_VBLANK_INTERRUPT_ENABLE|
I915_VBLANK_CLEAR);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int i915_emit_irq(struct drm_device * dev) static int i915_emit_irq(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
RING_LOCALS; RING_LOCALS;
...@@ -316,42 +515,12 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr) ...@@ -316,42 +515,12 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); READ_BREADCRUMB(dev_priv), (int)dev_priv->counter);
} }
dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); if (dev_priv->sarea_priv)
return ret; dev_priv->sarea_priv->last_dispatch =
} READ_BREADCRUMB(dev_priv);
static int i915_driver_vblank_do_wait(struct drm_device *dev, unsigned int *sequence,
atomic_t *counter)
{
drm_i915_private_t *dev_priv = dev->dev_private;
unsigned int cur_vblank;
int ret = 0;
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
(((cur_vblank = atomic_read(counter))
- *sequence) <= (1<<23)));
*sequence = cur_vblank;
return ret; return ret;
} }
int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence)
{
return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);
}
int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence)
{
return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2);
}
/* Needs the lock as it touches the ring. /* Needs the lock as it touches the ring.
*/ */
int i915_irq_emit(struct drm_device *dev, void *data, int i915_irq_emit(struct drm_device *dev, void *data,
...@@ -394,18 +563,96 @@ int i915_irq_wait(struct drm_device *dev, void *data, ...@@ -394,18 +563,96 @@ int i915_irq_wait(struct drm_device *dev, void *data,
return i915_wait_irq(dev, irqwait->irq_seq); return i915_wait_irq(dev, irqwait->irq_seq);
} }
int i915_enable_vblank(struct drm_device *dev, int plane)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
int pipe = i915_get_pipe(dev, plane);
u32 pipestat_reg = 0;
u32 pipestat;
switch (pipe) {
case 0:
pipestat_reg = I915REG_PIPEASTAT;
dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
break;
case 1:
pipestat_reg = I915REG_PIPEBSTAT;
dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
break;
default:
DRM_ERROR("tried to enable vblank on non-existent pipe %d\n",
pipe);
break;
}
if (pipestat_reg)
{
pipestat = I915_READ (pipestat_reg);
/*
* Older chips didn't have the start vblank interrupt,
* but
*/
if (IS_I965G (dev))
pipestat |= I915_START_VBLANK_INTERRUPT_ENABLE;
else
pipestat |= I915_VBLANK_INTERRUPT_ENABLE;
/*
* Clear any pending status
*/
pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS |
I915_VBLANK_INTERRUPT_STATUS);
I915_WRITE(pipestat_reg, pipestat);
}
I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
return 0;
}
void i915_disable_vblank(struct drm_device *dev, int plane)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
int pipe = i915_get_pipe(dev, plane);
u32 pipestat_reg = 0;
u32 pipestat;
switch (pipe) {
case 0:
pipestat_reg = I915REG_PIPEASTAT;
dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
break;
case 1:
pipestat_reg = I915REG_PIPEBSTAT;
dev_priv->irq_enable_reg &= ~I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
break;
default:
DRM_ERROR("tried to disable vblank on non-existent pipe %d\n",
pipe);
break;
}
I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
if (pipestat_reg)
{
pipestat = I915_READ (pipestat_reg);
pipestat &= ~(I915_START_VBLANK_INTERRUPT_ENABLE |
I915_VBLANK_INTERRUPT_ENABLE);
/*
* Clear any pending status
*/
pipestat |= (I915_START_VBLANK_INTERRUPT_STATUS |
I915_VBLANK_INTERRUPT_STATUS);
I915_WRITE(pipestat_reg, pipestat);
}
}
static void i915_enable_interrupt (struct drm_device *dev) static void i915_enable_interrupt (struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u16 flag;
flag = 0; dev_priv->irq_enable_reg |= I915_USER_INTERRUPT;
if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)
flag |= VSYNC_PIPEA_FLAG;
if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)
flag |= VSYNC_PIPEB_FLAG;
I915_WRITE16(I915REG_INT_ENABLE_R, USER_INT_FLAG | flag); I915_WRITE(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);
dev_priv->irq_enabled = 1;
} }
/* Set the vblank monitor pipe /* Set the vblank monitor pipe
...@@ -428,8 +675,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data, ...@@ -428,8 +675,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,
dev_priv->vblank_pipe = pipe->pipe; dev_priv->vblank_pipe = pipe->pipe;
i915_enable_interrupt (dev);
return 0; return 0;
} }
...@@ -447,9 +692,9 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, ...@@ -447,9 +692,9 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
flag = I915_READ(I915REG_INT_ENABLE_R); flag = I915_READ(I915REG_INT_ENABLE_R);
pipe->pipe = 0; pipe->pipe = 0;
if (flag & VSYNC_PIPEA_FLAG) if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)
pipe->pipe |= DRM_I915_VBLANK_PIPE_A; pipe->pipe |= DRM_I915_VBLANK_PIPE_A;
if (flag & VSYNC_PIPEB_FLAG) if (flag & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
pipe->pipe |= DRM_I915_VBLANK_PIPE_B; pipe->pipe |= DRM_I915_VBLANK_PIPE_B;
return 0; return 0;
...@@ -464,27 +709,30 @@ int i915_vblank_swap(struct drm_device *dev, void *data, ...@@ -464,27 +709,30 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
drm_i915_vblank_swap_t *swap = data; drm_i915_vblank_swap_t *swap = data;
drm_i915_vbl_swap_t *vbl_swap; drm_i915_vbl_swap_t *vbl_swap;
unsigned int pipe, seqtype, curseq; unsigned int pipe, seqtype, curseq, plane;
unsigned long irqflags; unsigned long irqflags;
struct list_head *list; struct list_head *list;
int ret;
if (!dev_priv) { if (!dev_priv) {
DRM_ERROR("%s called with no initialization\n", __func__); DRM_ERROR("%s called with no initialization\n", __func__);
return -EINVAL; return -EINVAL;
} }
if (dev_priv->sarea_priv->rotation) { if (!dev_priv->sarea_priv || dev_priv->sarea_priv->rotation) {
DRM_DEBUG("Rotation not supported\n"); DRM_DEBUG("Rotation not supported\n");
return -EINVAL; return -EINVAL;
} }
if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE |
_DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) { _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS |
_DRM_VBLANK_FLIP)) {
DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype); DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype);
return -EINVAL; return -EINVAL;
} }
pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; plane = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0;
pipe = i915_get_pipe(dev, plane);
seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE);
...@@ -495,6 +743,11 @@ int i915_vblank_swap(struct drm_device *dev, void *data, ...@@ -495,6 +743,11 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
spin_lock_irqsave(&dev->drw_lock, irqflags); spin_lock_irqsave(&dev->drw_lock, irqflags);
/* It makes no sense to schedule a swap for a drawable that doesn't have
* valid information at this point. E.g. this could mean that the X
* server is too old to push drawable information to the DRM, in which
* case all such swaps would become ineffective.
*/
if (!drm_get_drawable_info(dev, swap->drawable)) { if (!drm_get_drawable_info(dev, swap->drawable)) {
spin_unlock_irqrestore(&dev->drw_lock, irqflags); spin_unlock_irqrestore(&dev->drw_lock, irqflags);
DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable); DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable);
...@@ -503,7 +756,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data, ...@@ -503,7 +756,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
spin_unlock_irqrestore(&dev->drw_lock, irqflags); spin_unlock_irqrestore(&dev->drw_lock, irqflags);
curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); drm_update_vblank_count(dev, pipe);
curseq = drm_vblank_count(dev, pipe);
if (seqtype == _DRM_VBLANK_RELATIVE) if (seqtype == _DRM_VBLANK_RELATIVE)
swap->sequence += curseq; swap->sequence += curseq;
...@@ -517,14 +771,43 @@ int i915_vblank_swap(struct drm_device *dev, void *data, ...@@ -517,14 +771,43 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
} }
} }
if (swap->seqtype & _DRM_VBLANK_FLIP) {
swap->sequence--;
if ((curseq - swap->sequence) <= (1<<23)) {
struct drm_drawable_info *drw;
LOCK_TEST_WITH_RETURN(dev, file_priv);
spin_lock_irqsave(&dev->drw_lock, irqflags);
drw = drm_get_drawable_info(dev, swap->drawable);
if (!drw) {
spin_unlock_irqrestore(&dev->drw_lock,
irqflags);
DRM_DEBUG("Invalid drawable ID %d\n",
swap->drawable);
return -EINVAL;
}
i915_dispatch_vsync_flip(dev, drw, plane);
spin_unlock_irqrestore(&dev->drw_lock, irqflags);
return 0;
}
}
spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
list_for_each(list, &dev_priv->vbl_swaps.head) { list_for_each(list, &dev_priv->vbl_swaps.head) {
vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head);
if (vbl_swap->drw_id == swap->drawable && if (vbl_swap->drw_id == swap->drawable &&
vbl_swap->pipe == pipe && vbl_swap->plane == plane &&
vbl_swap->sequence == swap->sequence) { vbl_swap->sequence == swap->sequence) {
vbl_swap->flip = (swap->seqtype & _DRM_VBLANK_FLIP);
spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags);
DRM_DEBUG("Already scheduled\n"); DRM_DEBUG("Already scheduled\n");
return 0; return 0;
...@@ -547,9 +830,19 @@ int i915_vblank_swap(struct drm_device *dev, void *data, ...@@ -547,9 +830,19 @@ int i915_vblank_swap(struct drm_device *dev, void *data,
DRM_DEBUG("\n"); DRM_DEBUG("\n");
ret = drm_vblank_get(dev, pipe);
if (ret) {
drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER);
return ret;
}
vbl_swap->drw_id = swap->drawable; vbl_swap->drw_id = swap->drawable;
vbl_swap->pipe = pipe; vbl_swap->plane = plane;
vbl_swap->sequence = swap->sequence; vbl_swap->sequence = swap->sequence;
vbl_swap->flip = (swap->seqtype & _DRM_VBLANK_FLIP);
if (vbl_swap->flip)
swap->sequence++;
spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); spin_lock_irqsave(&dev_priv->swaps_lock, irqflags);
...@@ -567,37 +860,57 @@ void i915_driver_irq_preinstall(struct drm_device * dev) ...@@ -567,37 +860,57 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
I915_WRITE16(I915REG_HWSTAM, 0xfffe); I915_WRITE16(I915REG_HWSTAM, 0xeffe);
I915_WRITE16(I915REG_INT_MASK_R, 0x0); I915_WRITE16(I915REG_INT_MASK_R, 0x0);
I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);
} }
void i915_driver_irq_postinstall(struct drm_device * dev) int i915_driver_irq_postinstall(struct drm_device * dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
int ret, num_pipes = 2;
spin_lock_init(&dev_priv->swaps_lock); spin_lock_init(&dev_priv->swaps_lock);
INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); INIT_LIST_HEAD(&dev_priv->vbl_swaps.head);
dev_priv->swaps_pending = 0; dev_priv->swaps_pending = 0;
if (!dev_priv->vblank_pipe) dev_priv->user_irq_refcount = 0;
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; dev_priv->irq_enable_reg = 0;
ret = drm_vblank_init(dev, num_pipes);
if (ret)
return ret;
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
i915_enable_interrupt(dev); i915_enable_interrupt(dev);
DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue);
/*
* Initialize the hardware status page IRQ location.
*/
I915_WRITE(I915REG_INSTPM, (1 << 5) | (1 << 21));
return 0;
} }
void i915_driver_irq_uninstall(struct drm_device * dev) void i915_driver_irq_uninstall(struct drm_device * dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u16 temp; u32 temp;
if (!dev_priv) if (!dev_priv)
return; return;
I915_WRITE16(I915REG_HWSTAM, 0xffff); dev_priv->irq_enabled = 0;
I915_WRITE16(I915REG_INT_MASK_R, 0xffff); I915_WRITE(I915REG_HWSTAM, 0xffffffff);
I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); I915_WRITE(I915REG_INT_MASK_R, 0xffffffff);
I915_WRITE(I915REG_INT_ENABLE_R, 0x0);
temp = I915_READ16(I915REG_INT_IDENTITY_R);
I915_WRITE16(I915REG_INT_IDENTITY_R, temp); temp = I915_READ(I915REG_PIPEASTAT);
I915_WRITE(I915REG_PIPEASTAT, temp);
temp = I915_READ(I915REG_PIPEBSTAT);
I915_WRITE(I915REG_PIPEBSTAT, temp);
temp = I915_READ(I915REG_INT_IDENTITY_R);
I915_WRITE(I915REG_INT_IDENTITY_R, temp);
} }
...@@ -45,15 +45,16 @@ static struct pci_device_id pciidlist[] = { ...@@ -45,15 +45,16 @@ static struct pci_device_id pciidlist[] = {
static struct drm_driver driver = { static struct drm_driver driver = {
.driver_features = .driver_features =
DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA |
DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
DRIVER_IRQ_VBL,
.dev_priv_size = sizeof(drm_mga_buf_priv_t), .dev_priv_size = sizeof(drm_mga_buf_priv_t),
.load = mga_driver_load, .load = mga_driver_load,
.unload = mga_driver_unload, .unload = mga_driver_unload,
.lastclose = mga_driver_lastclose, .lastclose = mga_driver_lastclose,
.dma_quiescent = mga_driver_dma_quiescent, .dma_quiescent = mga_driver_dma_quiescent,
.device_is_agp = mga_driver_device_is_agp, .device_is_agp = mga_driver_device_is_agp,
.vblank_wait = mga_driver_vblank_wait, .get_vblank_counter = mga_get_vblank_counter,
.enable_vblank = mga_enable_vblank,
.disable_vblank = mga_disable_vblank,
.irq_preinstall = mga_driver_irq_preinstall, .irq_preinstall = mga_driver_irq_preinstall,
.irq_postinstall = mga_driver_irq_postinstall, .irq_postinstall = mga_driver_irq_postinstall,
.irq_uninstall = mga_driver_irq_uninstall, .irq_uninstall = mga_driver_irq_uninstall,
......
...@@ -120,6 +120,7 @@ typedef struct drm_mga_private { ...@@ -120,6 +120,7 @@ typedef struct drm_mga_private {
u32 clear_cmd; u32 clear_cmd;
u32 maccess; u32 maccess;
atomic_t vbl_received; /**< Number of vblanks received. */
wait_queue_head_t fence_queue; wait_queue_head_t fence_queue;
atomic_t last_fence_retired; atomic_t last_fence_retired;
u32 next_fence_to_post; u32 next_fence_to_post;
...@@ -181,11 +182,14 @@ extern int mga_warp_install_microcode(drm_mga_private_t * dev_priv); ...@@ -181,11 +182,14 @@ extern int mga_warp_install_microcode(drm_mga_private_t * dev_priv);
extern int mga_warp_init(drm_mga_private_t * dev_priv); extern int mga_warp_init(drm_mga_private_t * dev_priv);
/* mga_irq.c */ /* mga_irq.c */
extern int mga_enable_vblank(struct drm_device *dev, int crtc);
extern void mga_disable_vblank(struct drm_device *dev, int crtc);
extern u32 mga_get_vblank_counter(struct drm_device *dev, int crtc);
extern int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence); extern int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence);
extern int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); extern int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence);
extern irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS); extern irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS);
extern void mga_driver_irq_preinstall(struct drm_device * dev); extern void mga_driver_irq_preinstall(struct drm_device * dev);
extern void mga_driver_irq_postinstall(struct drm_device * dev); extern int mga_driver_irq_postinstall(struct drm_device * dev);
extern void mga_driver_irq_uninstall(struct drm_device * dev); extern void mga_driver_irq_uninstall(struct drm_device * dev);
extern long mga_compat_ioctl(struct file *filp, unsigned int cmd, extern long mga_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg); unsigned long arg);
......
...@@ -35,6 +35,20 @@ ...@@ -35,6 +35,20 @@
#include "mga_drm.h" #include "mga_drm.h"
#include "mga_drv.h" #include "mga_drv.h"
u32 mga_get_vblank_counter(struct drm_device *dev, int crtc)
{
const drm_mga_private_t *const dev_priv =
(drm_mga_private_t *) dev->dev_private;
if (crtc != 0) {
return 0;
}
return atomic_read(&dev_priv->vbl_received);
}
irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)
{ {
struct drm_device *dev = (struct drm_device *) arg; struct drm_device *dev = (struct drm_device *) arg;
...@@ -47,9 +61,8 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -47,9 +61,8 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)
/* VBLANK interrupt */ /* VBLANK interrupt */
if (status & MGA_VLINEPEN) { if (status & MGA_VLINEPEN) {
MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR); MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR);
atomic_inc(&dev->vbl_received); atomic_inc(&dev_priv->vbl_received);
DRM_WAKEUP(&dev->vbl_queue); drm_handle_vblank(dev, 0);
drm_vbl_send_signals(dev);
handled = 1; handled = 1;
} }
...@@ -78,22 +91,34 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -78,22 +91,34 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)
return IRQ_NONE; return IRQ_NONE;
} }
int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) int mga_enable_vblank(struct drm_device *dev, int crtc)
{ {
unsigned int cur_vblank; drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
int ret = 0;
/* Assume that the user has missed the current sequence number if (crtc != 0) {
* by about a day rather than she wants to wait for years DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
* using vertical blanks... crtc);
*/ return 0;
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, }
(((cur_vblank = atomic_read(&dev->vbl_received))
- *sequence) <= (1 << 23)));
*sequence = cur_vblank; MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN);
return 0;
}
return ret;
void mga_disable_vblank(struct drm_device *dev, int crtc)
{
if (crtc != 0) {
DRM_ERROR("tried to disable vblank on non-existent crtc %d\n",
crtc);
}
/* Do *NOT* disable the vertical refresh interrupt. MGA doesn't have
* a nice hardware counter that tracks the number of refreshes when
* the interrupt is disabled, and the kernel doesn't know the refresh
* rate to calculate an estimate.
*/
/* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */
} }
int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence) int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence)
...@@ -125,14 +150,22 @@ void mga_driver_irq_preinstall(struct drm_device * dev) ...@@ -125,14 +150,22 @@ void mga_driver_irq_preinstall(struct drm_device * dev)
MGA_WRITE(MGA_ICLEAR, ~0); MGA_WRITE(MGA_ICLEAR, ~0);
} }
void mga_driver_irq_postinstall(struct drm_device * dev) int mga_driver_irq_postinstall(struct drm_device * dev)
{ {
drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
int ret;
ret = drm_vblank_init(dev, 1);
if (ret)
return ret;
DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); DRM_INIT_WAITQUEUE(&dev_priv->fence_queue);
/* Turn on vertical blank interrupt and soft trap interrupt. */ /* Turn on soft trap interrupt. Vertical blank interrupts are enabled
MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); * in mga_enable_vblank.
*/
MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN);
return 0;
} }
void mga_driver_irq_uninstall(struct drm_device * dev) void mga_driver_irq_uninstall(struct drm_device * dev)
......
...@@ -43,12 +43,13 @@ static struct pci_device_id pciidlist[] = { ...@@ -43,12 +43,13 @@ static struct pci_device_id pciidlist[] = {
static struct drm_driver driver = { static struct drm_driver driver = {
.driver_features = .driver_features =
DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG |
DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
DRIVER_IRQ_VBL,
.dev_priv_size = sizeof(drm_r128_buf_priv_t), .dev_priv_size = sizeof(drm_r128_buf_priv_t),
.preclose = r128_driver_preclose, .preclose = r128_driver_preclose,
.lastclose = r128_driver_lastclose, .lastclose = r128_driver_lastclose,
.vblank_wait = r128_driver_vblank_wait, .get_vblank_counter = r128_get_vblank_counter,
.enable_vblank = r128_enable_vblank,
.disable_vblank = r128_disable_vblank,
.irq_preinstall = r128_driver_irq_preinstall, .irq_preinstall = r128_driver_irq_preinstall,
.irq_postinstall = r128_driver_irq_postinstall, .irq_postinstall = r128_driver_irq_postinstall,
.irq_uninstall = r128_driver_irq_uninstall, .irq_uninstall = r128_driver_irq_uninstall,
......
...@@ -97,6 +97,8 @@ typedef struct drm_r128_private { ...@@ -97,6 +97,8 @@ typedef struct drm_r128_private {
u32 crtc_offset; u32 crtc_offset;
u32 crtc_offset_cntl; u32 crtc_offset_cntl;
atomic_t vbl_received;
u32 color_fmt; u32 color_fmt;
unsigned int front_offset; unsigned int front_offset;
unsigned int front_pitch; unsigned int front_pitch;
...@@ -149,11 +151,12 @@ extern int r128_wait_ring(drm_r128_private_t * dev_priv, int n); ...@@ -149,11 +151,12 @@ extern int r128_wait_ring(drm_r128_private_t * dev_priv, int n);
extern int r128_do_cce_idle(drm_r128_private_t * dev_priv); extern int r128_do_cce_idle(drm_r128_private_t * dev_priv);
extern int r128_do_cleanup_cce(struct drm_device * dev); extern int r128_do_cleanup_cce(struct drm_device * dev);
extern int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); extern int r128_enable_vblank(struct drm_device *dev, int crtc);
extern void r128_disable_vblank(struct drm_device *dev, int crtc);
extern u32 r128_get_vblank_counter(struct drm_device *dev, int crtc);
extern irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS); extern irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS);
extern void r128_driver_irq_preinstall(struct drm_device * dev); extern void r128_driver_irq_preinstall(struct drm_device * dev);
extern void r128_driver_irq_postinstall(struct drm_device * dev); extern int r128_driver_irq_postinstall(struct drm_device * dev);
extern void r128_driver_irq_uninstall(struct drm_device * dev); extern void r128_driver_irq_uninstall(struct drm_device * dev);
extern void r128_driver_lastclose(struct drm_device * dev); extern void r128_driver_lastclose(struct drm_device * dev);
extern void r128_driver_preclose(struct drm_device * dev, extern void r128_driver_preclose(struct drm_device * dev,
......
...@@ -35,6 +35,16 @@ ...@@ -35,6 +35,16 @@
#include "r128_drm.h" #include "r128_drm.h"
#include "r128_drv.h" #include "r128_drv.h"
u32 r128_get_vblank_counter(struct drm_device *dev, int crtc)
{
const drm_r128_private_t *dev_priv = dev->dev_private;
if (crtc != 0)
return 0;
return atomic_read(&dev_priv->vbl_received);
}
irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS) irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS)
{ {
struct drm_device *dev = (struct drm_device *) arg; struct drm_device *dev = (struct drm_device *) arg;
...@@ -46,30 +56,38 @@ irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -46,30 +56,38 @@ irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS)
/* VBLANK interrupt */ /* VBLANK interrupt */
if (status & R128_CRTC_VBLANK_INT) { if (status & R128_CRTC_VBLANK_INT) {
R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK); R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK);
atomic_inc(&dev->vbl_received); atomic_inc(&dev_priv->vbl_received);
DRM_WAKEUP(&dev->vbl_queue); drm_handle_vblank(dev, 0);
drm_vbl_send_signals(dev);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
return IRQ_NONE; return IRQ_NONE;
} }
int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) int r128_enable_vblank(struct drm_device *dev, int crtc)
{ {
unsigned int cur_vblank; drm_r128_private_t *dev_priv = dev->dev_private;
int ret = 0;
/* Assume that the user has missed the current sequence number if (crtc != 0) {
* by about a day rather than she wants to wait for years DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc);
* using vertical blanks... return -EINVAL;
*/ }
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
(((cur_vblank = atomic_read(&dev->vbl_received))
- *sequence) <= (1 << 23)));
*sequence = cur_vblank; R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN);
return 0;
}
void r128_disable_vblank(struct drm_device *dev, int crtc)
{
if (crtc != 0)
DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc);
return ret; /*
* FIXME: implement proper interrupt disable by using the vblank
* counter register (if available)
*
* R128_WRITE(R128_GEN_INT_CNTL,
* R128_READ(R128_GEN_INT_CNTL) & ~R128_CRTC_VBLANK_INT_EN);
*/
} }
void r128_driver_irq_preinstall(struct drm_device * dev) void r128_driver_irq_preinstall(struct drm_device * dev)
...@@ -82,12 +100,9 @@ void r128_driver_irq_preinstall(struct drm_device * dev) ...@@ -82,12 +100,9 @@ void r128_driver_irq_preinstall(struct drm_device * dev)
R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK); R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK);
} }
void r128_driver_irq_postinstall(struct drm_device * dev) int r128_driver_irq_postinstall(struct drm_device * dev)
{ {
drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private; return drm_vblank_init(dev, 1);
/* Turn on VBL interrupt */
R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN);
} }
void r128_driver_irq_uninstall(struct drm_device * dev) void r128_driver_irq_uninstall(struct drm_device * dev)
......
...@@ -59,8 +59,7 @@ static struct pci_device_id pciidlist[] = { ...@@ -59,8 +59,7 @@ static struct pci_device_id pciidlist[] = {
static struct drm_driver driver = { static struct drm_driver driver = {
.driver_features = .driver_features =
DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG |
DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED,
DRIVER_IRQ_VBL | DRIVER_IRQ_VBL2,
.dev_priv_size = sizeof(drm_radeon_buf_priv_t), .dev_priv_size = sizeof(drm_radeon_buf_priv_t),
.load = radeon_driver_load, .load = radeon_driver_load,
.firstopen = radeon_driver_firstopen, .firstopen = radeon_driver_firstopen,
...@@ -69,8 +68,9 @@ static struct drm_driver driver = { ...@@ -69,8 +68,9 @@ static struct drm_driver driver = {
.postclose = radeon_driver_postclose, .postclose = radeon_driver_postclose,
.lastclose = radeon_driver_lastclose, .lastclose = radeon_driver_lastclose,
.unload = radeon_driver_unload, .unload = radeon_driver_unload,
.vblank_wait = radeon_driver_vblank_wait, .get_vblank_counter = radeon_get_vblank_counter,
.vblank_wait2 = radeon_driver_vblank_wait2, .enable_vblank = radeon_enable_vblank,
.disable_vblank = radeon_disable_vblank,
.dri_library_name = dri_library_name, .dri_library_name = dri_library_name,
.irq_preinstall = radeon_driver_irq_preinstall, .irq_preinstall = radeon_driver_irq_preinstall,
.irq_postinstall = radeon_driver_irq_postinstall, .irq_postinstall = radeon_driver_irq_postinstall,
......
...@@ -304,6 +304,9 @@ typedef struct drm_radeon_private { ...@@ -304,6 +304,9 @@ typedef struct drm_radeon_private {
u32 scratch_ages[5]; u32 scratch_ages[5];
unsigned int crtc_last_cnt;
unsigned int crtc2_last_cnt;
/* starting from here on, data is preserved accross an open */ /* starting from here on, data is preserved accross an open */
uint32_t flags; /* see radeon_chip_flags */ uint32_t flags; /* see radeon_chip_flags */
unsigned long fb_aper_offset; unsigned long fb_aper_offset;
...@@ -374,13 +377,13 @@ extern int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file * ...@@ -374,13 +377,13 @@ extern int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *
extern int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern void radeon_do_release(struct drm_device * dev); extern void radeon_do_release(struct drm_device * dev);
extern int radeon_driver_vblank_wait(struct drm_device * dev, extern u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc);
unsigned int *sequence); extern int radeon_enable_vblank(struct drm_device *dev, int crtc);
extern int radeon_driver_vblank_wait2(struct drm_device * dev, extern void radeon_disable_vblank(struct drm_device *dev, int crtc);
unsigned int *sequence); extern void radeon_do_release(struct drm_device * dev);
extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS); extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS);
extern void radeon_driver_irq_preinstall(struct drm_device * dev); extern void radeon_driver_irq_preinstall(struct drm_device * dev);
extern void radeon_driver_irq_postinstall(struct drm_device * dev); extern int radeon_driver_irq_postinstall(struct drm_device * dev);
extern void radeon_driver_irq_uninstall(struct drm_device * dev); extern void radeon_driver_irq_uninstall(struct drm_device * dev);
extern int radeon_vblank_crtc_get(struct drm_device *dev); extern int radeon_vblank_crtc_get(struct drm_device *dev);
extern int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value); extern int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value);
...@@ -558,6 +561,12 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev, ...@@ -558,6 +561,12 @@ extern int r300_do_cp_cmdbuf(struct drm_device * dev,
? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \ ? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \
: RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) ) : RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) )
#define RADEON_CRTC_CRNT_FRAME 0x0214
#define RADEON_CRTC2_CRNT_FRAME 0x0314
#define RADEON_CRTC_STATUS 0x005c
#define RADEON_CRTC2_STATUS 0x03fc
#define RADEON_GEN_INT_CNTL 0x0040 #define RADEON_GEN_INT_CNTL 0x0040
# define RADEON_CRTC_VBLANK_MASK (1 << 0) # define RADEON_CRTC_VBLANK_MASK (1 << 0)
# define RADEON_CRTC2_VBLANK_MASK (1 << 9) # define RADEON_CRTC2_VBLANK_MASK (1 << 9)
......
...@@ -35,12 +35,61 @@ ...@@ -35,12 +35,61 @@
#include "radeon_drm.h" #include "radeon_drm.h"
#include "radeon_drv.h" #include "radeon_drv.h"
static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv, static void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state)
u32 mask)
{ {
u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) & mask; drm_radeon_private_t *dev_priv = dev->dev_private;
if (state)
dev_priv->irq_enable_reg |= mask;
else
dev_priv->irq_enable_reg &= ~mask;
RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
}
int radeon_enable_vblank(struct drm_device *dev, int crtc)
{
switch (crtc) {
case 0:
radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 1);
break;
case 1:
radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 1);
break;
default:
DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
crtc);
return EINVAL;
}
return 0;
}
void radeon_disable_vblank(struct drm_device *dev, int crtc)
{
switch (crtc) {
case 0:
radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 0);
break;
case 1:
radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 0);
break;
default:
DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",
crtc);
break;
}
}
static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv)
{
u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) &
(RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT |
RADEON_CRTC2_VBLANK_STAT);
if (irqs) if (irqs)
RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs); RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs);
return irqs; return irqs;
} }
...@@ -72,39 +121,21 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -72,39 +121,21 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS)
/* Only consider the bits we're interested in - others could be used /* Only consider the bits we're interested in - others could be used
* outside the DRM * outside the DRM
*/ */
stat = radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | stat = radeon_acknowledge_irqs(dev_priv);
RADEON_CRTC_VBLANK_STAT |
RADEON_CRTC2_VBLANK_STAT));
if (!stat) if (!stat)
return IRQ_NONE; return IRQ_NONE;
stat &= dev_priv->irq_enable_reg; stat &= dev_priv->irq_enable_reg;
/* SW interrupt */ /* SW interrupt */
if (stat & RADEON_SW_INT_TEST) { if (stat & RADEON_SW_INT_TEST)
DRM_WAKEUP(&dev_priv->swi_queue); DRM_WAKEUP(&dev_priv->swi_queue);
}
/* VBLANK interrupt */ /* VBLANK interrupt */
if (stat & (RADEON_CRTC_VBLANK_STAT|RADEON_CRTC2_VBLANK_STAT)) { if (stat & RADEON_CRTC_VBLANK_STAT)
int vblank_crtc = dev_priv->vblank_crtc; drm_handle_vblank(dev, 0);
if (stat & RADEON_CRTC2_VBLANK_STAT)
if ((vblank_crtc & drm_handle_vblank(dev, 1);
(DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) ==
(DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) {
if (stat & RADEON_CRTC_VBLANK_STAT)
atomic_inc(&dev->vbl_received);
if (stat & RADEON_CRTC2_VBLANK_STAT)
atomic_inc(&dev->vbl_received2);
} else if (((stat & RADEON_CRTC_VBLANK_STAT) &&
(vblank_crtc & DRM_RADEON_VBLANK_CRTC1)) ||
((stat & RADEON_CRTC2_VBLANK_STAT) &&
(vblank_crtc & DRM_RADEON_VBLANK_CRTC2)))
atomic_inc(&dev->vbl_received);
DRM_WAKEUP(&dev->vbl_queue);
drm_vbl_send_signals(dev);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -144,54 +175,27 @@ static int radeon_wait_irq(struct drm_device * dev, int swi_nr) ...@@ -144,54 +175,27 @@ static int radeon_wait_irq(struct drm_device * dev, int swi_nr)
return ret; return ret;
} }
static int radeon_driver_vblank_do_wait(struct drm_device * dev, u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc)
unsigned int *sequence, int crtc)
{ {
drm_radeon_private_t *dev_priv = drm_radeon_private_t *dev_priv = dev->dev_private;
(drm_radeon_private_t *) dev->dev_private; u32 crtc_cnt_reg, crtc_status_reg;
unsigned int cur_vblank;
int ret = 0;
int ack = 0;
atomic_t *counter;
if (!dev_priv) { if (!dev_priv) {
DRM_ERROR("called with no initialization\n"); DRM_ERROR("called with no initialization\n");
return -EINVAL; return -EINVAL;
} }
if (crtc == DRM_RADEON_VBLANK_CRTC1) { if (crtc == 0) {
counter = &dev->vbl_received; crtc_cnt_reg = RADEON_CRTC_CRNT_FRAME;
ack |= RADEON_CRTC_VBLANK_STAT; crtc_status_reg = RADEON_CRTC_STATUS;
} else if (crtc == DRM_RADEON_VBLANK_CRTC2) { } else if (crtc == 1) {
counter = &dev->vbl_received2; crtc_cnt_reg = RADEON_CRTC2_CRNT_FRAME;
ack |= RADEON_CRTC2_VBLANK_STAT; crtc_status_reg = RADEON_CRTC2_STATUS;
} else } else {
return -EINVAL; return -EINVAL;
}
radeon_acknowledge_irqs(dev_priv, ack); return RADEON_READ(crtc_cnt_reg) + (RADEON_READ(crtc_status_reg) & 1);
dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
/* Assume that the user has missed the current sequence number
* by about a day rather than she wants to wait for years
* using vertical blanks...
*/
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
(((cur_vblank = atomic_read(counter))
- *sequence) <= (1 << 23)));
*sequence = cur_vblank;
return ret;
}
int radeon_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence)
{
return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1);
}
int radeon_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence)
{
return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2);
} }
/* Needs the lock as it touches the ring. /* Needs the lock as it touches the ring.
...@@ -234,21 +238,6 @@ int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_pr ...@@ -234,21 +238,6 @@ int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_pr
return radeon_wait_irq(dev, irqwait->irq_seq); return radeon_wait_irq(dev, irqwait->irq_seq);
} }
static void radeon_enable_interrupt(struct drm_device *dev)
{
drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private;
dev_priv->irq_enable_reg = RADEON_SW_INT_ENABLE;
if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC1)
dev_priv->irq_enable_reg |= RADEON_CRTC_VBLANK_MASK;
if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC2)
dev_priv->irq_enable_reg |= RADEON_CRTC2_VBLANK_MASK;
RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg);
dev_priv->irq_enabled = 1;
}
/* drm_dma.h hooks /* drm_dma.h hooks
*/ */
void radeon_driver_irq_preinstall(struct drm_device * dev) void radeon_driver_irq_preinstall(struct drm_device * dev)
...@@ -260,20 +249,27 @@ void radeon_driver_irq_preinstall(struct drm_device * dev) ...@@ -260,20 +249,27 @@ void radeon_driver_irq_preinstall(struct drm_device * dev)
RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); RADEON_WRITE(RADEON_GEN_INT_CNTL, 0);
/* Clear bits if they're already high */ /* Clear bits if they're already high */
radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | radeon_acknowledge_irqs(dev_priv);
RADEON_CRTC_VBLANK_STAT |
RADEON_CRTC2_VBLANK_STAT));
} }
void radeon_driver_irq_postinstall(struct drm_device * dev) int radeon_driver_irq_postinstall(struct drm_device * dev)
{ {
drm_radeon_private_t *dev_priv = drm_radeon_private_t *dev_priv =
(drm_radeon_private_t *) dev->dev_private; (drm_radeon_private_t *) dev->dev_private;
int ret;
atomic_set(&dev_priv->swi_emitted, 0); atomic_set(&dev_priv->swi_emitted, 0);
DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); DRM_INIT_WAITQUEUE(&dev_priv->swi_queue);
radeon_enable_interrupt(dev); ret = drm_vblank_init(dev, 2);
if (ret)
return ret;
dev->max_vblank_count = 0x001fffff;
radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1);
return 0;
} }
void radeon_driver_irq_uninstall(struct drm_device * dev) void radeon_driver_irq_uninstall(struct drm_device * dev)
...@@ -315,6 +311,5 @@ int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value) ...@@ -315,6 +311,5 @@ int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value)
return -EINVAL; return -EINVAL;
} }
dev_priv->vblank_crtc = (unsigned int)value; dev_priv->vblank_crtc = (unsigned int)value;
radeon_enable_interrupt(dev);
return 0; return 0;
} }
...@@ -40,11 +40,13 @@ static struct pci_device_id pciidlist[] = { ...@@ -40,11 +40,13 @@ static struct pci_device_id pciidlist[] = {
static struct drm_driver driver = { static struct drm_driver driver = {
.driver_features = .driver_features =
DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ |
DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, DRIVER_IRQ_SHARED,
.load = via_driver_load, .load = via_driver_load,
.unload = via_driver_unload, .unload = via_driver_unload,
.context_dtor = via_final_context, .context_dtor = via_final_context,
.vblank_wait = via_driver_vblank_wait, .get_vblank_counter = via_get_vblank_counter,
.enable_vblank = via_enable_vblank,
.disable_vblank = via_disable_vblank,
.irq_preinstall = via_driver_irq_preinstall, .irq_preinstall = via_driver_irq_preinstall,
.irq_postinstall = via_driver_irq_postinstall, .irq_postinstall = via_driver_irq_postinstall,
.irq_uninstall = via_driver_irq_uninstall, .irq_uninstall = via_driver_irq_uninstall,
......
...@@ -75,6 +75,7 @@ typedef struct drm_via_private { ...@@ -75,6 +75,7 @@ typedef struct drm_via_private {
struct timeval last_vblank; struct timeval last_vblank;
int last_vblank_valid; int last_vblank_valid;
unsigned usec_per_vblank; unsigned usec_per_vblank;
atomic_t vbl_received;
drm_via_state_t hc_state; drm_via_state_t hc_state;
char pci_buf[VIA_PCI_BUF_SIZE]; char pci_buf[VIA_PCI_BUF_SIZE];
const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE];
...@@ -130,11 +131,13 @@ extern int via_init_context(struct drm_device * dev, int context); ...@@ -130,11 +131,13 @@ extern int via_init_context(struct drm_device * dev, int context);
extern int via_final_context(struct drm_device * dev, int context); extern int via_final_context(struct drm_device * dev, int context);
extern int via_do_cleanup_map(struct drm_device * dev); extern int via_do_cleanup_map(struct drm_device * dev);
extern int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); extern u32 via_get_vblank_counter(struct drm_device *dev, int crtc);
extern int via_enable_vblank(struct drm_device *dev, int crtc);
extern void via_disable_vblank(struct drm_device *dev, int crtc);
extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS); extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS);
extern void via_driver_irq_preinstall(struct drm_device * dev); extern void via_driver_irq_preinstall(struct drm_device * dev);
extern void via_driver_irq_postinstall(struct drm_device * dev); extern int via_driver_irq_postinstall(struct drm_device * dev);
extern void via_driver_irq_uninstall(struct drm_device * dev); extern void via_driver_irq_uninstall(struct drm_device * dev);
extern int via_dma_cleanup(struct drm_device * dev); extern int via_dma_cleanup(struct drm_device * dev);
......
...@@ -92,8 +92,17 @@ static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; ...@@ -92,8 +92,17 @@ static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1};
static unsigned time_diff(struct timeval *now, struct timeval *then) static unsigned time_diff(struct timeval *now, struct timeval *then)
{ {
return (now->tv_usec >= then->tv_usec) ? return (now->tv_usec >= then->tv_usec) ?
now->tv_usec - then->tv_usec : now->tv_usec - then->tv_usec :
1000000 - (then->tv_usec - now->tv_usec); 1000000 - (then->tv_usec - now->tv_usec);
}
u32 via_get_vblank_counter(struct drm_device *dev, int crtc)
{
drm_via_private_t *dev_priv = dev->dev_private;
if (crtc != 0)
return 0;
return atomic_read(&dev_priv->vbl_received);
} }
irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
...@@ -108,8 +117,8 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -108,8 +117,8 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
status = VIA_READ(VIA_REG_INTERRUPT); status = VIA_READ(VIA_REG_INTERRUPT);
if (status & VIA_IRQ_VBLANK_PENDING) { if (status & VIA_IRQ_VBLANK_PENDING) {
atomic_inc(&dev->vbl_received); atomic_inc(&dev_priv->vbl_received);
if (!(atomic_read(&dev->vbl_received) & 0x0F)) { if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) {
do_gettimeofday(&cur_vblank); do_gettimeofday(&cur_vblank);
if (dev_priv->last_vblank_valid) { if (dev_priv->last_vblank_valid) {
dev_priv->usec_per_vblank = dev_priv->usec_per_vblank =
...@@ -119,12 +128,11 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -119,12 +128,11 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
dev_priv->last_vblank = cur_vblank; dev_priv->last_vblank = cur_vblank;
dev_priv->last_vblank_valid = 1; dev_priv->last_vblank_valid = 1;
} }
if (!(atomic_read(&dev->vbl_received) & 0xFF)) { if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) {
DRM_DEBUG("US per vblank is: %u\n", DRM_DEBUG("US per vblank is: %u\n",
dev_priv->usec_per_vblank); dev_priv->usec_per_vblank);
} }
DRM_WAKEUP(&dev->vbl_queue); drm_handle_vblank(dev, 0);
drm_vbl_send_signals(dev);
handled = 1; handled = 1;
} }
...@@ -163,31 +171,34 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) ...@@ -163,31 +171,34 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv)
} }
} }
int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) int via_enable_vblank(struct drm_device *dev, int crtc)
{ {
drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; drm_via_private_t *dev_priv = dev->dev_private;
unsigned int cur_vblank; u32 status;
int ret = 0;
DRM_DEBUG("\n"); if (crtc != 0) {
if (!dev_priv) { DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc);
DRM_ERROR("called with no initialization\n");
return -EINVAL; return -EINVAL;
} }
viadrv_acknowledge_irqs(dev_priv); status = VIA_READ(VIA_REG_INTERRUPT);
VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE);
/* Assume that the user has missed the current sequence number VIA_WRITE8(0x83d4, 0x11);
* by about a day rather than she wants to wait for years VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30);
* using vertical blanks...
*/
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, return 0;
(((cur_vblank = atomic_read(&dev->vbl_received)) - }
*sequence) <= (1 << 23)));
*sequence = cur_vblank; void via_disable_vblank(struct drm_device *dev, int crtc)
return ret; {
drm_via_private_t *dev_priv = dev->dev_private;
VIA_WRITE8(0x83d4, 0x11);
VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30);
if (crtc != 0)
DRM_ERROR("%s: bad crtc %d\n", __FUNCTION__, crtc);
} }
static int static int
...@@ -292,23 +303,25 @@ void via_driver_irq_preinstall(struct drm_device * dev) ...@@ -292,23 +303,25 @@ void via_driver_irq_preinstall(struct drm_device * dev)
} }
} }
void via_driver_irq_postinstall(struct drm_device * dev) int via_driver_irq_postinstall(struct drm_device * dev)
{ {
drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
u32 status; u32 status;
DRM_DEBUG("\n"); DRM_DEBUG("via_driver_irq_postinstall\n");
if (dev_priv) { if (!dev_priv)
status = VIA_READ(VIA_REG_INTERRUPT); return -EINVAL;
VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL
| dev_priv->irq_enable_mask);
/* Some magic, oh for some data sheets ! */ drm_vblank_init(dev, 1);
status = VIA_READ(VIA_REG_INTERRUPT);
VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL
| dev_priv->irq_enable_mask);
VIA_WRITE8(0x83d4, 0x11); /* Some magic, oh for some data sheets ! */
VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); VIA_WRITE8(0x83d4, 0x11);
VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30);
} return 0;
} }
void via_driver_irq_uninstall(struct drm_device * dev) void via_driver_irq_uninstall(struct drm_device * dev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment